I have tried everything I can think of and can't get line breaks to go along with the addticketreply command to the API of WHMCS. It's coming from a window's application not PHP or anything. Simple query string, all characters are included before the first line break. After that line break nothing else of the message is included.
I've tried a urlencode method, no go, the API just inserts all text including the encoded part ha ha and doesn't unencode. I've tried replacing line breaks \n with <br> but still WHMCS just shows the <br> ha ha
Any clues how to get full messages through the API including new lines? Documentation is really lacking on this one.
Also the variable adminusername doesn't work at all! I've tried a full name, nope, I've tried username and userid and nope, still inserts my logged in username for ticket replies. The user I am using is a super admin with full access.
The query string for the API looks like this:
accesskey=key&adminusername=Name of Person&ticketid=488&message=asdf asdf
Nothing past that first line goes :(
sad sad sad&action=addticketreply&responsetype=json&username=admin&password=mypassword
I should mention I am using Indy for the Post. Everything else works, any other command passed works, heck even the ticket is updated, the message is updated. It just won't go past that first line break.
EDIT CODE: As I mentioned above I DO let IdHTTP encode my text using the post params, so it should work, AND as I also mentioned above I have tried to pre-encode it, with the results being that WHMCS won't un-encode then and just shows the %20's and other's. Since it's been asked I should mention, I am using Indy 10! So I am up to date with the most recent version.
j := TStringList.Create;
ret := TStringStream.Create('');
j.Text := k+params+'action='+method+'&responsetype=json&username='+username1.Text+'&password='+password;
try
htp1.Post('http'+s+'://'+url1.Text+'/includes/api.php', j, ret);
except
on E: EIdHTTPProtocolException do
result := '{"result":"error", "message":"'+htp1.ResponseText+'"}';
end;
Line breaks, spaces, and other reserved characters need to be URL-encoded, eg:
accesskey=key&adminusername=Name%20of%20Person&ticketid=488&message=asdf%20asdf%0D%0ANothing%20past%20that%20first%20line%20goes%20%3A%28%0D%0Asad%20sad%20sad&action=addticketreply&responsetype=json&username=admin&password=mypassword
If you are sending the values via a POST request, then make sure you are usiug an up-to-date version of Indy. TIdHTTP.Post(TStrings) handles the encoding for you, eg:
var
Params: TStringList;
begin
Params := TStringList.Create;
try
Params.Add('accesskey=key');
Params.Add('adminusername=Name of Person');
Params.Add('ticketid=488');
Params.Add('message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad');
Params.Add('action=addticketreply');
Params.Add('responsetype=json');
Params.Add('username=admin');
Params.Add('password=mypassword');
IdHTTP1.Post(URL, Params);
finally
Params.Free;
end;
end;
However, if you are sending the values via a URL query string, then you have to manually encode the URL, TdHTTP will not handle that for you, eg:
var
URL: String;
begin
URL := 'http://host/path?accesskey=key&adminusername=Name of Person&ticketid=488&message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad&action=addticketreply&responsetype=json&username=admin&password=mypassword';
IdHTTP1.Get(TIdURI.URLEncode(URL));
end;
Or:
var
URL: String;
Params: TStringList;
I: Integer;
begin
Params := TStringList.Create;
try
Params.Add('accesskey=key');
Params.Add('adminusername=Name of Person');
Params.Add('ticketid=488');
Params.Add('message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad');
Params.Add('action=addticketreply');
Params.Add('responsetype=json');
Params.Add('username=admin');
Params.Add('password=mypassword');
for I := 0 to Params.Count-1 do
Params[i] := TIdURI.ParamsEncode(Params[i]);
Params.Delimiter := '&';
Params.StrictDelimiter := True;
Params.Quotechar := #0;
URL := 'http://host/' + TIdURI.PathEncode('path') + '?' + Params.DelimitedText;
IdHTTP1.Get(URL);
finally
Params.Free;
end;
end;
Related
I can get a searched document as following golang code:
ret, err := client.Query(f.Get(f.MatchTerm(f.Index("label_search_by_externalID"), externalID)))
Then, I tried to delete a searched document as similar manner as follow:
ret, err := client.Query(f.Delete(f.MatchTerm(f.Index("label_search_by_externalID"), externalID)))
But, this code occurs an error:
Response error 400. Errors: [delete](invalid argument): Ref expected, Set provided.
I'm confused, by the API document, both Get and Delete request Ref for a document as param, and MatchTerm returns a Set, not Ref.
Then I have 2 questions.
How can I get the document Ref which the result of the search?
Is there any way to get the document ref from the search result with Index like RefCollection for collection, for example like RefIndex as follows?
ret, err := client.Query(f.Delete(f.RefIndex(f.Index("label_search_by_externalID"), externalID)))
Why my code for getting search result document works well? Is there more good coding for getting search result document?
Thank you for your suggestion!
Get will only return 1 result. If you have multiple values returned from this index search you will have problems. I would suggest the function Paginate(). This will return a set of results, which you can then map over and execute other functionality, such as a get() or delete(). I would strong suggest you look at the following tutorial (https://docs.fauna.com/fauna/current/tutorials/indexes/pagination).
This is pseudo code to delete a page of documents found. You can adjust the page size either up or down:
Map(
Paginate(Match(Index("label_search_by_externalID"), true)),
Lambda("X", Delete(Var("X")))
)
I want to create a query parameter without escaping the query string.
For example, I need to create a query parameter q with the content
"before:{2019-12-24 19:57:34}"
so that the URL is
https://android-review.googlesource.com/changes/?q=before:{2019-12-24 19:57:34}
If I use this code (Golang Playground)
url, _ := url.Parse("https://android-review.googlesource.com/changes/")
q := url.Query()
q.Set("q", "before:{2019-12-24 19:57:34}")
url.RawQuery = q.Encode()
fmt.Println(url)
url is escaping the special characters, spaces, and brackets:
https://android-review.googlesource.com/changes/?q=before%3A%7B2019-12-24+19%3A57%3A34%7D
How can I solve this issue except manually creating the URL (without query parameters then)?
If you don't want your URL query to be encoded, then don't use the Encode() method. Instead, set the RawQuery value directly, yourself:
url, _ := url.Parse("https://android-review.googlesource.com/changes/")
url.RawQuery = "q=before:{2019-12-24 19:57:34}"
fmt.Println(url)
Output:
https://android-review.googlesource.com/changes/?q=before:{2019-12-24 19:57:34}
playground
Keep in mind, however, that this is a recipe for potential disaster, depending on how that url is eventually used. In particular, the space in that URL should be escaped, according to RFC. See more here.
Perhaps you'll want to implement your own minimal escaping, if that's compatible with your use-case.
I need to open some tables from MSI database, read this and place some rows in this with using Delphi (in my example is Delphi 7 but allowed other versions if it needed).
For example it would look like ORCA. Msi must be open, written to the table where it's can be edited and written to the msi file.
By default, Delphi can't open MSI tables as I thing but I found a JEDI Windows API where exists libraris like JwaMsi and JwaMsiQuery. But I can't find documentations or examples of using functions like
function MsiOpenProduct(szProduct: LPCTSTR; var hProduct: MSIHANDLE): UINT; stdcall;
{$EXTERNALSYM MsiOpenProduct}
By the way, while I search information about this I found this code:
const msilib = 'msi.dll';
type
MSIHANDLE = DWORD;
TMsiHandle = MSIHANDLE;
function MsiCloseHandle(hAny: MSIHANDLE):UINT;stdcall;external msilib name 'MsiCloseHandle';
function MsiOpenProduct(szProduct:LPCSTR;var hProduct:MSIHANDLE):UINT;stdcall;external msilib name 'MsiOpenProductA';
function MsiGetProductProperty(hProduct:MSIHANDLE;szProperty:LPCSTR;lpValueBuf:LPSTR;pcchValueBuf:LPDWORD):UINT;stdcall; external msilib name 'MsiGetProductPropertyA';
function MsiSetInternalUI(dwUILevel:INSTALLUILEVEL;phWnd:LPHWND):INSTALLUILEVEL;stdcall; external msilib name 'MsiSetInternalUI';
function GetMSIProperty(aProductCode:string):string;
var
msi:TMSIHandle;
t:string;
function _getmsiproperty(_name:string):string;
var
txt:PChar;
sz:DWORD;
begin
sz:=MAX_PATH;
txt:=AllocMem(sz+1);
if MsiGetProductProperty(msi,PChar(_name),txt,#sz)=ERROR_MORE_DATA then
begin
ReAllocMem(txt,sz+1);
MsiGetProductProperty(msi,PChar(_name),txt,#sz);
end;
SetString(Result,txt,sz);
FreeMem(txt,sz+1);
end;
begin
MsiSetInternalUI(2,nil); // скрываем GUI/hide GUI
if MsiOpenProduct(PChar(aProductCode),msi)=ERROR_SUCCESS then
begin
t:=_getmsiproperty('ARPPRODUCTICON'); // главная иконка приложения/main program icon
if t='' then t:=_getmsiproperty('ProductIcon');
if t='' then t:=_getmsiproperty('CompleteSetupIcon');
if t='' then t:=_getmsiproperty('CustomSetupIcon');
if t='' then t:=_getmsiproperty('InfoIcon');
if t='' then t:=_getmsiproperty('InstallerIcon');
if t='' then t:=_getmsiproperty('RemoveIcon');
if t='' then t:=_getmsiproperty('RepairIcon');
Result:=t;
MsiCloseHandle(msi);
end;
end;
What is better to use and where I can see documentation and/or examples?
P.S. Sorry for my English
I would use the COM-based API to MSI.
See this thread and the MSI API documentation
So, solution exist and I do it! It's not so gracefully but it's working...
At first, read articles from MSDN (in my case is articles about work with MSI database).
At second, you must understand what you need to do. In my case it is:
Open msi database
Read table names
Read needed table and show it
If it needed - make changes and save it
Here I show how I do the three first steps.
Preparing
Download JEDI Windows AP (for easer work with msi) and add to project JwaMsi.pas, JwaMsiDefs.pas and JwaMsiQuery.pas (do not forget about USES list). Dependencies will be added automatically.
Next, place on form all needed components.
Let's code!
1 Open msi database and 2 Read table names
Define variables for handlers and for buffers
var msi_handler_DB, msi_handler_view, msi_handler_record:TMSIHandle;
txt:PChar;
sz:DWORD;
Now we need take a list of tables (more info here) and place it in ListBox, for example
begin
sz:=MAX_PATH; //initialise
txt:=AllocMem(sz+1); //variables
OpenDialog1.Execute; //select file
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //check if DB is open
then begin //start reading
Listbox1.Clear; //prepare listbox for filling
MsiDatabaseOpenView(msi_handler_DB, 'SELECT * FROM _Tables',msi_handler_view); //prepare query to _Table
MsiViewExecute(msi_handler_view, msi_handler_record); //execute...
While not MsiViewFetch(msi_handler_view, msi_handler_record)=ERROR_NO_MORE_ITEMS //and fetch it in cycle until end of table
do begin
MsiRecordGetString(msi_handler_record,1,txt,sz); //read string
ListBox1.Items.Add(txt); //and write to listbox
end; //end of fetch cycle
MsiCloseAllHandles; //close handles (we don't need they more)
end; //stop reading
end;
3 Read needed table and show it
It's easy!
begin
edit1.text:=Listbox1.Items.ValueFromIndex[ListBox1.ItemIndex];
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //open database again
then begin
MsiDatabaseExport(msi_handler_DB, pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]), 'C:\Windows\Temp', Pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt')); //export table to .idt
MsiCloseAllHandles; //and close handler again
//...
//here must be placed code for
//parsing .idt as tabulation separated file
//with wordwrap and show it.
//for example - in StringGrid
//...
DeleteFile('C:\Windows\Temp\'+ ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt'); //do not forget delete temporary .idt file. save our planet! :)
end;
end;
4 If it needed - make changes and save it
Just repeat third step from the end to beginning: export StringGrid to file (about .idt and it's structure you can read here: one, two, three), import in to MSI with property MsiDatabaseImport (do not forget open database before and append it after, also remember about close handlers) and delete temporary .idt file.
Good luck!
P.S. Sorry for my English, part two :)
I am trying to load database data by using dbplannercalender1.
procedure TForm1.DBPlannerCalendar1DaySelect(Sender: TObject;
SelDate: TDateTime);
begin
with absQuery2 do
begin
absQuery2.Close;
absQuery2.sql.Clear;
ABSQuery2.SQL.Text:='select * from log where date = :a1';
ABSQUERY2.PARAMS.ParamByName('a1').value:= DBPlannerCalendar1.Date;
ABSQuery2.ExecSQL;
end;
end;
I get the error "date string expected"YYYY-MM-DD",but "="found at line 1..."
What am I doing wrong ?
Use trunced value
adoQuery.Parameters.paramByName('DataDal').value := trunc(edDate.Date);
Use AsDateTime instead of Value, to let the driver convert it to the proper format:
AbsQuery2.Params.ParamByName('a1').AsDateTime := DBPlannerCalendar1.Date;
If Absolute doens't support AsDateTime for some reason, format the date yourself:
AbsQuery2.Params.ParamByName('a1').Value :=
FormatDateTime('yyyy-mm-dd`, DBPlannerCalendar1.Date);
The first method is the best choice, because it works more portably between database engines (it works for all of them, because the driverr does the formatting into the proper arrangement for dates). Using the second means that if the DB expects something different than YYYY-MM-DD, you have to change the code everywhere that uses dates.
this segment in my program first ads a customer to a textfile (declared in public variables) and saves it to a texfile. onbutton1click is the procudere to search the string thats in the editbox and return the relevant customer details to memo. the add customer works fine and adds to textfile, however when i search it returns nothing onto the memo, just the memo caption, memo1. any way i can resolve this? sorry im a newb to this.
procedure TForm2.btnsaveClick(Sender: TObject);
begin
cusfname:= edit1.text ;
cuslname:= edit2.text;
adress:= edit3.text;
phone:= edit4.text;
password:= edit5.Text;
AssignFile(F, 'Data.txt');
append(F);
WriteLn(F, cusfname);
WriteLn(F, cuslname);
WriteLn(F, adress);
WriteLn(F, phone);
WriteLn(F, password);
CloseFile(F);
end;
procedure TForm2.Button1Click(Sender: TObject);
var
SearchFile : Textfile;
found: boolean;
search: string;
begin
search := edit1.text;
Assignfile(SearchFile, 'data.txt');
Reset(SearchFile);
found:= false;
repeat
found:= search = phone
until eof(searchfile) or found;
if found then
memo1.append(phone);
memo1.append(cusfname);
memo1.append(adress);
if not found then
showmessage('member not found');
end;
wonder where are the read statements? In the write function you have Write() statements, but in the reading code no read() statements?
In your code you do not read() from file. In other similar question (probably your own): runerror(102) file not assigned? there is read(). But I think you should use readln(), or even better use TStringList class from Classes unit with its LoadFromFile() method and Lines property.