I'm trying to update a manufacturer in Prestashop through the REST interface. So far I've been able to GET the information, but when I try to PUT, I Always get an HTTP 500 error.
try
Memo1.Text := '<prestashop><manufacturer><id><![CDATA[804]]></id><name><![CDATA[DisneyLand]]></name></manufacturer></prestashop>';
RESTRequest1.Params.Clear;
//authentication does not work with PUT, use token as suffix....
RESTClient1.Authenticator := nil; //SimpleAuthenticator1;//
//SimpleAuthenticator1.UserNameKey := 'ws_key';
//SimpleAuthenticator1.UserName := 'HEREIGOESTHETOKEN';
RESTRequest1.Resource := 'manufacturers';
RESTRequest1.ResourceSuffix := '?ws_key=HEREIGOESTHETOKEN';
RESTRequest1.Method := rmPut; //update
RESTClient1.BaseURL := 'http://localhost/prestashop/api';
RESTRequest1.Params.AddItem('id', '804' ,pkGETorPOST , [], ctTEXT_PLAIN);
RESTRequest1.Params.AddItem('putXml', Memo1.Text,
pkGETorPOST , [], ctAPPLICATION_X_WWW_FORM_URLENCODED);
RESTRequest1.Execute;
Memo1.Lines.Clear;
Memo1.Lines.Add(RESTResponse1.Content);
except
on E : Exception do
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('Exception class name = '+E.ClassName);
Memo1.Lines.Add('Exception message = '+E.Message);
end;
end;
I've tried the other TRESTRequestParameterKind posibilities, but no avail.
Anyone tried this before?
UPDATE : After monitoring my network with Wireshark, i noticed that if I use the TSimpleauthenticater component anywhere before, the xml is always appended with the ws_key value, resulting in a 500 server error. This happens even if I clear all the SimpleAuthenticator settings and set the Clients authenticator to nil.
I also had to set the content type to ctTEXT_XML iso ctAPPLICATION_X_WWW_FORM_URLENCODED when specifying th xml in the body.
Following code works :
procedure TForm1.BtnNewMfgClick(Sender: TObject); //new
var
aNode, aCNode, aCCNode : IXMLNode;
i,j : integer;
aXml : string;
begin
RESTRequest1.Params.Clear;
RESTClient1.Authenticator := nil;
SimpleAuthenticator1.UserNameKey := '';
SimpleAuthenticator1.UserName := '';
RESTClient1.BaseURL := 'http://localhost/prestashop/api';
RESTRequest1.Resource := 'manufacturers';
RESTRequest1.ResourceSuffix := '?schema=blank&ws_key=HEREGOESMYKEY';
RESTRequest1.Method := rmGet;
RESTRequest1.Execute;
aXml := RESTResponse1.Content;
XMLDocument1.LoadFromXML(aXml);
aNode := XMLDocument1.ChildNodes.FindNode('prestashop');
if assigned(aNode)
then begin
for i := 0 to aNode.ChildNodes.Count-1 do
begin
aCNode := aNode.ChildNodes.Get(i);
for j := 0 to aCNode.ChildNodes.Count-1 do
begin
aCCNode := aCNode.ChildNodes.Get(j);
if aCCNode.NodeName = 'id' then aCCNode.NodeValue := ''; //cannot pass id at create
if aCCNode.NodeName = 'active' then aCCNode.NodeValue := '1' ;
if aCCNode.NodeName = 'name' then aCCNode.NodeValue := 'New Brand';
end;
end;
end;
XmlDocument1.SaveToXML(aXml);
RESTRequest1.ClearBody;
RESTRequest1.AddBody(aXml, ctTEXT_XML);
RESTRequest1.ResourceSuffix := '?ws_key=HEREGOESMYKEY';
RESTRequest1.Method := rmPost;
RESTRequest1.Execute;
//new id is returned in the contents XML id tag
Memo1.Lines.Clear;
Memo1.Lines.Add(RESTResponse1.Content);
end;
Although this works in test, I'm investigating it further, because in production it has to work over https:// so the key is not exposed...
In short : Simpleauthenticator only works for GET and DELETE. Once used, PUT and POST will never work.
Related
I have a TreeList , with many Items , each item has it's own unique ID .
I allow the user to open multiple IDs at once . But I would like to prevent the user from opening the same ID twice .
So I thought about creating a simple Dynamic Array where I store which TreeList ID is connected to which Form HWND . If I find a ID on my list with a Matching HWND, then I simply bring the Form which is already Created to Foreground.
Application.CreateForm(TChapter, Chapter);
Chapter.PopupParent:=Main;
Chapter.FID:=qryTreeID.Value;
Chapter.Caption:=qryTreeName.Value+Cardinal(Chapter.Handle).ToString;
Chapter.Show;
This is how I create a Form . This is just a "basic" example . I just wanted to make sure that the Handle is Unique , I opened Multiple Forms the Numbers were always different. But I want to make sure.
Thank you!
If you want to maintain your own lookup, a TDictionary would make more sense than a dynamic array. But either way, you should map the ID to the actual TForm object rather than to its HWND. The HWND is guaranteed to be unique, but not persistent, as it can change during the Form's lifetime. It would also save you from the extra step of having to get the TForm object from the HWND.
For example:
var
Chapters: TDictionary<Integer, TChapter> = nil;
procedure ChapterDestroyed(Self: Pointer; Sender: TObject);
begin
if Chapters <> nil then
Chapters.Remove(TChapter(Sender).FID);
end;
function FindChapterByID(ID: Integer): TChapter;
// var I: Integer;
begin
{
for I := 0 to Screen.FormCount-1 do
begin
if Screen.Forms[I] is TChapter then
begin
Result := TChapter(Screen.Forms[I]);
if Result.FID = ID then Exit;
end;
end;
Result := nil;
}
if not Chapters.TryGetValue(ID, Result) then
Result := nil;
end;
function CreateChapter(ID: Integer): TChapter;
var
Event: TNotifyEvent;
begin
TMethod(Event).Data := nil;
TMethod(Event).Code = #ChapterDestroyed;
Result := TChapter.Create(Main);
try
Result.FID := ID;
Result.PopupParent := Main;
Result.Caption := qryTreeName.Value + ID.ToString;
Result.OnDestroy := Event;
Chapters.Add(ID, Result);
except
Result.Free;
raise;
end;
end;
function ShowChapterByID(ID: Integer): TChapter;
begin
Result := FindChapterByID(ID);
if Result = nil then Result := CreateChapter(ID);
Result.Show;
end;
initialization
Chapters := TDictionary<Integer, TChapter>.Create;
finalization
FreeAndNil(Chapters);
Chapter := ShowChapterByID(qryTreeID.Value);
Thank you for both of you. I took SilverWariors advice , because of the simplicity :)
for i := 0 to Screen.FormCount-1 do
begin
if Screen.Forms[i] is TChapter then
if (Screen.Forms[i] as TChapter).FID = qryTreeID.Value then
begin
(Screen.Forms[i] as TChapter).BringToFront;
Exit;
end;
end;
My form contains a TIdSMTP, TIdMessage, TOpenDialog, SSL Handlers, and other visual components. I also have buttons for sending and attaching the different files.
procedure TForm1.Button1Click(Sender: TObject);
begin
email_connecter_TIdSMTP.Host := entered_host_TEdit.Text;
email_connecter_TIdSMTP.Username := entered_username_TEdit.Text;
email_connecter_TIdSMTP.Password := entered_password_TEdit.Text;
message_parts_TIdMessage.Clear();
message_parts_TIdMessage.Recipients.EMailAddresses := to_sender_email_TEdit.Text;
message_parts_TIdMessage.Subject := email_subject_TEdit.Text;
message_parts_TIdMessage.Body.Text := email_body_message_TMemo.Text;
email_connecter_TIdSMTP.Connect();
email_connecter_TIdSMTP.Send(message_parts_TIdMessage);
email_connecter_TIdSMTP.Disconnect();
end;
procedure TForm1.Button3Click(Sender: TObject);
var t:textfile;
s:string;
selected_file:string;
attatchment_message: TIdMessageBuilderHtml;
begin
selected_file := '';
try
attatchment_finder_TOpenDialog.InitialDir := 'C:\Documents';
attatchment_finder_TOpenDialog.Filter := 'All files (*.*)|*.*';
if attatchment_finder_TOpenDialog.Execute(Handle) then
selected_file := attatchment_finder_TOpenDialog.FileName;
if selected_file <>'' then
attatchment_message := TIdMessageBuilderHtml.Create;
attatchment_message.HtmlContentTransfer := 'quoted-printable';
memo_attachment_box_TMemo.Lines.Add(selected_file);
attatchment_message.Attachments.Add(selected_file);
attatchment_message.FillMessage(message_parts_TIdMessage);
finally
attatchment_finder_TOpenDialog.Free;
end;
end;
end.
What am I doing wrong when adding my selected file? How can I make it so that I can send any file type?
When I click send on my program it adds the file directory text to my memo box but it doesn't actually attach the file onto my TIdMessage component.
thank you!
The code you have shown clears the entire TIdMessage just before sending it, wiping out any attachments that Button3Click() may have added beforehand.
In fact, Button3Click() shouldn't be doing anything with the TIdMessage directly at all. That responsibility belongs solely in Button1Click() when it is populating the TIdMessage after clearing it.
Also, you are not using TIdMessageBuilderHtml correctly. You should be using its PlainText or HTML property (depending on what kind of text you are sending) instead of setting the TIdMessage.Body directly. Without that, FillMessage() doesn't set the TIdMessage.ContentType correctly. If you are sending plain text instead of HTML, you should be using TIdMessageBuilderPlain instead.
Try something more like this:
procedure TForm1.Button1Click(Sender: TObject);
var
email_builder: TIdMessageBuilderPlain;
I: integer;
begin
email_connecter_TIdSMTP.Host := entered_host_TEdit.Text;
email_connecter_TIdSMTP.Username := entered_username_TEdit.Text;
email_connecter_TIdSMTP.Password := entered_password_TEdit.Text;
message_parts_TIdMessage.Clear;
message_parts_TIdMessage.Recipients.EMailAddresses := to_sender_email_TEdit.Text;
message_parts_TIdMessage.Subject := email_subject_TEdit.Text;
email_builder := TIdMessageBuilderPlain.Create;
try
email_builder.PlainText.Assign(email_body_message_TMemo.Lines);
email_builder.PlainTextContentTransfer := 'quoted-printable';
for I := 0 to memo_attachment_box_TMemo.Lines.Count-1 do
email_builder.Attachments.Add(memo_attachment_box_TMemo.Lines[I]);
email_builder.FillMessage(message_parts_TIdMessage);
finally
email_builder.Free;
end;
email_connecter_TIdSMTP.Connect;
try
email_connecter_TIdSMTP.Send(message_parts_TIdMessage);
finally
email_connecter_TIdSMTP.Disconnect;
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
attatchment_finder_TOpenDialog.InitialDir := 'C:\Documents';
attatchment_finder_TOpenDialog.Filter := 'All files (*.*)|*.*';
if attatchment_finder_TOpenDialog.Execute(Handle) then
begin
memo_attachment_box_TMemo.Lines.Add(attatchment_finder_TOpenDialog.FileName);
// or, if ofAllowMultiSelect is enabled:
// memo_attachment_box_TMemo.Lines.AddStrings(attatchment_finder_TOpenDialog.Files);
end;
end;
Iam using Delphi 5 and Indy 10 recently downloaded from fulgan.
The access violation error occurres when the supplied credentials are incorrect. Authenticate function seems to produce the error.
It is also impossible to get past the access violation error, resulting in program shutting down without any warning.
Here is an example code:
procedure TForm1.Button2Click(Sender: TObject);
var IDSMTP1: TIdSMTP;
Idmessage1: TIDMessage;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
begin
IDSMTP1 := TIdSMTP.Create;
IdSSLIOHandlerSocketOpenSSL1 := TIdSSLIOHandlerSocketOpenSSL.Create;
try
with IDSMTP1 do begin
Host := 'smtp.gmail.com';
Port := 465;
Username := 'email#gmail.com';
Password := 'password';
AuthType := satDefault;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.SSLVersions := [sslvTLSv1];
IOHandler := IdSSLIOHandlerSocketOpenSSL1;
UseTLS := utUseRequireTLS;
end;
Idmessage1 := TIDMessage.Create;
try
with Idmessage1 do begin
Subject := 'test';
From.Address := 'email#gmail.com';
From.Name := 'testname';
Recipients.EMailAddresses := 'email#gmail.com';
end;
with IDSMTP1 do begin
if not Connected then
Connect;
try
Authenticate;
Send(IdMessage1);
except
end;
Disconnect;
end;
finally
Idmessage1.Free;
end;
finally
IDSMTP1.Free;
IdSSLIOHandlerSocketOpenSSL1.Free;
end;
end;
Thank you in advance.
I have tried the below code for sending the mail, but it shows connection closed gracefully error. I have tried 587 and 465 port also. Authentication also raise the error for "5.7.0 Must issue a STARTTLS command first. q27sm64805279pfi.83 - gsmtp".
uses
IdSMTP, IdMessage, IdEMailAddress;
procedure SendSimpleMail;
var
IdSMTP: TIdSMTP;
IdMessage: TIdMessage;
IdEmailAddressItem: TIdEmailAddressItem;
begin
IdSMTP := TIdSMTP.Create(nil);
try
IdSMTP.Host := 'smtp.gmail.com';
IdSMTP.Port := 25; // or 587 //or 465
IdSMTP.AuthType := satDefault;
IdSMTP.Username := 'username#gmail.com';
IdSMTP.Password := 'password';
IdSMTP.Connect;
if IdSMTP.Authenticate then
begin
IdMessage := TIdMessage.Create(nil);
try
IdMessage.From.Name := 'User Name';
IdMessage.From.Address := 'username#gmail.com';
IdMessage.Subject := 'E-mail subject';
IdMessage.Body.Add('E-mail body.');
IdEmailAddressItem := IdMessage.Recipients.Add;
IdEmailAddressItem.Address := 'recipient#email.com';
IdSMTP.Send(IdMessage);
finally
IdMessage.Free;
end;
end;
IdSMTP.Disconnect;
finally
IdSMTP.Free;
end;
end;
To use STARTTLS, you need to
assign an SSLIOHandler component, such as TIdSSLIOHandlerSocketOpenSSL, to the TIdSMTP.IOHandler property. You will have to deploy the OpenSSL DLLs with your app, if not already installed on the target machine that will be running your app.
set the TIdSMTP.UseTLS property to utUseExplicitTLS to enable STARTTLS handling. Make sure TIdSMTP.UseEHLO is true (it is by default) so TIdSMTP can discover whether the server supports STARTTLS.
connect to port 587.
Try this:
uses
IdSMTP, IdMessage, IdEMailAddress, IdSSLOpenSSL;
procedure SendSimpleMail;
var
IdSMTP: TIdSMTP;
IdMessage: TIdMessage;
IdEmailAddressItem: TIdEmailAddressItem;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
IdSMTP := TIdSMTP.Create(nil);
try
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP);
IdSMTP.IOHandler := IdSSL;
IdSMTP.UseTLS := utUseExplicitTLS;
IdSMTP.Host := 'smtp.gmail.com';
IdSMTP.Port := 587;
IdSMTP.AuthType := satDefault;
IdSMTP.Username := 'username#gmail.com';
IdSMTP.Password := 'password';
IdSMTP.Connect;
if IdSMTP.Authenticate then
begin
IdMessage := TIdMessage.Create(nil);
try
IdMessage.From.Name := 'User Name';
IdMessage.From.Address := 'username#gmail.com';
IdMessage.Subject := 'E-mail subject';
IdMessage.Body.Add('E-mail body.');
IdEmailAddressItem := IdMessage.Recipients.Add;
IdEmailAddressItem.Address := 'recipient#email.com';
IdSMTP.Send(IdMessage);
finally
IdMessage.Free;
end;
end;
IdSMTP.Disconnect;
finally
IdSMTP.Free;
end;
end;
Update: the code above is meant for Indy 10. If you are using Indy 9 instead, there are some differences.
The OpenSSL component was named TIdSSLIOHandlerSocket.
TIdSMTP did not support STARTTLS yet. You would have to manage the SSLIOHandler'sPassThrough` property manually.
If you connect to port 587 for explicit TLS, you have to set PassThrough to true initially so you connect to the server unencrypted, then send the STARTTLS command manually and set PassThrough to false to activate encryption before sending any further SMTP commands.
Procedure SendMail();
begin
IdSMTP1.Host := 'smtp.gmail.com';
IdSMTP1.AuthenticationType := atLogin;
IdSMTP1.Port := 587;
IdSMTP1.Username := 'user#gmail.com';
IdSMTP1.Password := 'password';
IdSSLIOHandlerSocket1.PassThrough := True;
IdSMTP1.Connect;
try
IdSMTP1.SendCmd('STARTTLS', [220]);
IdSSLIOHandlerSocket1.PassThrough := False;
IdSMTP1.SendCmd('EHLO ' + IdSMTP1.LocalName);
IdSMTP1.Authenticate;
IdMessage1.Body.Add('Test Message');
IdMessage1.From.Address := 'frommail#gmail.com';
IdMessage1.Recipients.EMailAddresses := 'tomail#gmail.com';
IdMessage1.Subject := 'Hi';
try
IdSMTP1.Send(IdMessage1);
except
ShowMessage('Error : email not send');
end;
finally
IdSMTP1.Disconnect;
end;
end;
If you connect to port 465 for implicit SSL, all you have to do is set PassThrough to false before connecting to the server. The connection will be initially encrypted before sending any SMTP commands at all, no STARTTLS needed.
Procedure SendMail();
begin
IdSMTP1.Host := 'smtp.gmail.com';
IdSMTP1.AuthenticationType := atLogin;
IdSMTP1.Port := 465;
IdSMTP1.Username := 'user#gmail.com';
IdSMTP1.Password := 'password';
IdSSLIOHandlerSocket1.PassThrough := False;
IdSMTP1.Connect;
try
IdMessage1.Body.Add('Test Message');
IdMessage1.From.Address := 'frommail#gmail.com';
IdMessage1.Recipients.EMailAddresses := 'tomail#gmail.com';
IdMessage1.Subject := 'Hi';
try
IdSMTP1.Send(IdMessage1);
except
ShowMessage('Error : email not send');
end;
finally
IdSMTP1.Disconnect;
end;
end;
Thanks, Here I am using delphi 7.0 it will support TIdSSLIOHandlerSocketOpenSSL dll, so i can change the coding as like this. Since i got the same error message in this code.
Procedure SendMail();
var Resp : integer;
begin
IdSMTP1.Host :='smtp.gmail.com';
IdSMTP1.AuthenticationType:= atLogin;
IdSMTP1.Port := 587;
IdSMTP1.Username := 'user#gmail.com';
IdSMTP1.Password := 'password';
IdSMTP1.Connect;
if IdSMTP1.Connected then
Begin
Resp:= -1;
Resp:= IdSMTP1.SendCmd('STARTTLS',Resp);
IdMessage1.Body.Add('Tets Message');
IdMessage1.From.Address := 'frommail#gmail.com';
IdMessage1.Recipients.EMailAddresses := 'tomail#gmail.com';
IdMessage1.Subject := 'Hi';
try
IdSMTP1.Send(IdMessage1);
except
ShowMessage('Error : email not send');
end;
IdSMTP1.Disconnect;
end;
end;
I was helped:
When sending mail, a server error occurs:
'501 Syntactically invalid EHLO argument (s)' (error code 0x800CCC63).
This error occurs if the name of the computer (running Windows) contains invalid characters:
(`` {_} '', Russian letters or spaces).
Have a look on naming files
It should be changed via:
Control Panel> Network> Authentication.
It is also necessary to check the name specified through:
Control Panel> Network> TCP
I would like to send mail with RTF format (including images, with attached files or not).
I have try the following code (with many other ContentType) but I always receive the plain text.
Someone know how to do that with Delphi XE3 ?
procedure TForm1.Button1Click(Sender: TObject);
var
zMessage: TIdMessage;
zSMTP: TIdSmtp;
begin
zSMTP := TIdSMTP.Create(Application);
zMessage := TIdMessage.Create;
zMessage.Recipients.EMailAddresses := 'dce#v.com';
zMessage.Subject := 'Test RTF';
zMessage.ContentType := 'multipart/mixed';
// zMessage.ContentType := 'multipart/alternative';
zMessage.From.Address := 'dce#v.com';
zMessage.From.Name := 'DCE';
// zMessage.Body.LoadFromFile('c:\TEMP\test.rtf');
with TIdText.Create(zMessage.MessageParts) do
begin
ContentType := 'text/richtext';
Body.LoadFromFile('c:\TEMP\test.rtf');
end;
zSMTP.Host := 'm.v.com';
zSMTP.UserName := 'dce#v.com';
zSMTP.Password := 'dce';
zSMTP.Connect;
zSMTP.Send(zMessage);
end;
I have also tried this code :
procedure TForm1.Button2Click(Sender: TObject);
var
zMBuilder: TIdMessageBuilderRtf;
zMyMemoryStream: TMemoryStream;
zMessage: TIdMessage;
zSMTP: TIdSmtp;
begin
try
zSMTP := TIdSMTP.Create(Application);
zMessage := TIdMessage.Create;
zMessage.Recipients.EMailAddresses := 'dce#v.com';
zMessage.Subject := 'Test RTF 2';
zMessage.ContentType := 'multipart/mixed';
zMessage.From.Address := 'dce#v.com';
zMessage.From.Name := 'DCE';
zMBuilder := TIdMessageBuilderRtf.Create;
zMyMemoryStream := TMemoryStream.Create;
zMBuilder.RtfType := idMsgBldrRtfRichtext;
zMyMemoryStream.LoadFromFile('c:\TEMP\test.rtf');
zMBuilder.Rtf.LoadFromStream(zMyMemoryStream);
zMBuilder.FillMessage(zMessage);
zSMTP.Host := 'm.v.com';
zSMTP.UserName := 'dce#v.com';
zSMTP.Password := 'ddd';
zSMTP.Connect;
zSMTP.Send(zMessage);
zSMTP.DisConnect;
finally
zMyMemoryStream.Free;
zMBuilder.Free;
end;
end;
Thanks in advance.