* Bugfixes from Attila Borka:

- Bug fix in the template parser. Bug0012095
  - Fixed: CGI applications did not create and populate contentfields (caused AV if someone 
    tried to access it) for the http request, both the query and content parameters were put 
    into the queryfields list
  - Bug0012094 fix: CGI applications AllowDefaultModule=true did not work for Delphi style calls (it is ok 
    for querystring parameter passed module names)
  - fptemplate.pp->TTemplateParser: Added support for template tag parameters. 

git-svn-id: trunk@11747 -
This commit is contained in:
michael 2008-09-11 18:47:41 +00:00
parent 20fd2d692f
commit af63b5466b
6 changed files with 438 additions and 84 deletions

1
.gitattributes vendored
View File

@ -1608,6 +1608,7 @@ packages/fcl-res/xml/winpeimagereader.xml svneol=native#text/plain
packages/fcl-web/Makefile svneol=native#text/plain packages/fcl-web/Makefile svneol=native#text/plain
packages/fcl-web/Makefile.fpc svneol=native#text/plain packages/fcl-web/Makefile.fpc svneol=native#text/plain
packages/fcl-web/fpmake.pp svneol=native#text/plain packages/fcl-web/fpmake.pp svneol=native#text/plain
packages/fcl-web/fptemplate.txt svneol=native#text/plain
packages/fcl-web/src/README svneol=native#text/plain packages/fcl-web/src/README svneol=native#text/plain
packages/fcl-web/src/cgiapp.pp svneol=native#text/plain packages/fcl-web/src/cgiapp.pp svneol=native#text/plain
packages/fcl-web/src/custcgi.pp svneol=native#text/plain packages/fcl-web/src/custcgi.pp svneol=native#text/plain

View File

@ -0,0 +1,164 @@
fptemplate.pp
implements template support
Default behaviour:
In the basic default version the TFPTemplate object can handle simple template
tags ex. {templatetagname} and requires the replacement strings in a Values
array before the parsing starts. An OnGetParam:TGetParamEvent event can be
triggered if it is set, when a value is not found in the Values list.
The template tag start and end delimiters can be set with the StartDelimiter
and EndDelimiter properties (defaults are '{' and '}' for now).
The parsing happens recursively so a replace text string can contain further
tags in it.
Recent improvements:
With the recent improvements the template tag handling got more close to the
traditional Delphi way of handling templates.
By setting the AllowTagParams property to True this new parsing method will be
activated and it is possible to pass parameters to the processing program from
the template tags.
Other than the two original StartDelimiter and EndDelimiter properties to
specify the boundaries of a template tag, there are 3 more delimiters to
define these parameters.
ParamStartDelimiter (default is '[-')
ParamEndDelimiter (default is '-]')
ParamValueSeparator (default is '=')
Some examples for tags with these above, StartDelimiter:='{+' and
EndDelimiter:='+}'
(the default '{' and '}' is not good when processing HTML templates with
JavaSript in them):
{+ATagHere+}
{+AnotherTagHere [-paramname1=paramvalue1-]+}
{+HereIsATagToo //with param
[-param1=param1value-] //some text here to ignore
//this text is ignored too
[-param2=param2value which
is multi line something
text ending here
-]
[-param3=param3value-]
+}
If we want something close to the Delphi tag delimiters, we can set the
StartDelimiter := '<#';
EndDelimiter := '>';
ParamStartDelimiter := ' ';
ParamEndDelimiter := '"';
ParamValueSeparator := '="';
This allows the use of Dephi-like tags like these:
<#input type="text" name="foo1" value="" caption="bar" checked="false">
<#input type="RadioButton" name="foo2"
value=""
caption="bar" checked="false" >
<#fieldvalue fieldname="FIRSTNAME">
Of course, the above setting requires at least one space before the parameter
names. Cannot just use tabs for example to separate them. Also, Delphi (and its
emulation here) cannot handle any HTML code within the tag parameters because
some might contain characters indicating tag-param-end or tag-end.
When the tags are processed, for each tag a
TReplaceTagEvent = Procedure(Sender : TObject; Const TagString : String;
TagParams:TStringList; Out ReplaceText : String) Of Object;
will be called with the parameters passed in TagParams:TStringList so it has
to be assigned to such a procedure.
Example:
procedure TFPWebModule1.func1callRequest(Sender: TObject; ARequest: TRequest;
AResponse: TResponse; var Handled: Boolean);
var s:String;
begin //Template:TFPTemplate is a property of the web Action
Template.FileName := 'pathtotemplate\mytemplate.html';
Template.AllowTagParams := true;
Template.StartDelimiter := '{+';
Template.EndDelimiter := '+}';
Template.OnReplaceTag := @func1callReplaceTag;
s := Template.GetContent;
//lets use some Delphi style tags too and re-run the parser
Template.StartDelimiter := '<#';
Template.EndDelimiter := '>';
Template.ParamStartDelimiter := ' ';
Template.ParamEndDelimiter := '"';
Template.ParamValueSeparator := '="';
Template.FileName := '';
Template.Template := s;
AResponse.Content := Template.GetContent;
Handled := true;
end;
procedure TFPWebModule1.func1callReplaceTag(Sender: TObject; const TagString:
String; TagParams: TStringList; Out ReplaceText: String);
begin
if AnsiCompareText(TagString, 'TagName1') = 0 then
begin
ReplaceText := 'text to replace this tag, using the TagParams if needed';
end else begin
.
.snip
.
//Not found value for tag -> TagString
end;
end;
With these improvements it is easily possible to separate the web page design
and the web server side programming. For example to generate a table record
list the web designer can use the following Tag in a template:
.
.snip
.
<table class="beautify1"><tr class="beautify2"><td class="beautify3">
{+REPORTRESULT
[-HEADER=
<table bordercolorlight="#6699CC" bordercolordark="#E1E1E1" class="Label">
<tr class="Label" align=center bgcolor="#6699CC">
<th><font color="white">~Column1</font></th>
<th><font color="white">~Column2</font></th>
</tr>
-]
[- ONEROW =
<tr bgcolor="#F2F2F2" class="Label3" align="center">
<td>~Column1Value</td><td>~Column2value</td>
</tr>
-]
.
.snip, and so on more parameters
.
[- NOTFOUND=
<tr class="Error"><td>There are no entries found.</td></tr>
-]
[-FOOTER=</table>-]
+}
</table>
.
.snip
.
I know, I know its ugly html progamming and who uses tables and font html tags
nowadays, etc. ... but you get the idea.
The OnReplaceTag event handler just need to replace the whole REPORTRESULT
template tag with the ~Column1, ~Column2 for the HEADER parameter, and the
~Column1Value, ~Column2Value in the ONEROW parameter while looping through a
sql query result set.
Or if there is nothing to list, just use the NOTFOUND parameter as a replace
text for the whole RESULT template tag.

View File

@ -436,9 +436,9 @@ begin
end;} end;}
CT:=ContentType; CT:=ContentType;
if Pos('MULTIPART/FORM-DATA',Uppercase(CT))<>0 then if Pos('MULTIPART/FORM-DATA',Uppercase(CT))<>0 then
ProcessMultiPart(M,CT) ProcessMultiPart(M,CT, ContentFields)
else if CompareText('APPLICATION/X-WWW-FORM-URLENCODED',CT)=0 then else if CompareText('APPLICATION/X-WWW-FORM-URLENCODED',CT)=0 then
ProcessUrlEncoded(M) ProcessUrlEncoded(M, ContentFields)
else else
begin begin
{$ifdef CGIDEBUG} {$ifdef CGIDEBUG}
@ -465,7 +465,7 @@ begin
{$endif} {$endif}
FQueryString:=GetEnvironmentVariable('QUERY_STRING'); FQueryString:=GetEnvironmentVariable('QUERY_STRING');
If (FQueryString<>'') then If (FQueryString<>'') then
ProcessQueryString(FQueryString); ProcessQueryString(FQueryString, QueryFields);
{$ifdef CGIDEBUG} {$ifdef CGIDEBUG}
SendMethodExit('InitGetVars'); SendMethodExit('InitGetVars');
{$endif} {$endif}

View File

@ -75,12 +75,14 @@ end;
function TCGIApplication.GetModuleName(Arequest: TRequest): string; function TCGIApplication.GetModuleName(Arequest: TRequest): string;
begin begin
If (FModuleVar<>'') then If (FModuleVar<>'') then
Result:=ARequest.QueryFields.Values[FModuleVar]; Result:=ARequest.QueryFields.Values[FModuleVar];//Module name from query parameter using the FModuleVar as parameter name (default is 'Module')
If (Result='') then If (Result='') then
begin
if (Pos('/', ARequest.PathInfo[2]) <= 0) and AllowDefaultModule then Exit;//There is only 1 '/' in ARequest.PathInfo -> only ActionName is there -> use default module
Result:=ARequest.GetNextPathInfo; Result:=ARequest.GetNextPathInfo;
end;
end; end;
function TCGIApplication.FindModule(ModuleClass : TCustomHTTPModuleClass): TCustomHTTPModule; function TCGIApplication.FindModule(ModuleClass : TCustomHTTPModuleClass): TCustomHTTPModule;

View File

@ -28,14 +28,20 @@ Const
MaxDelimLength = 5; MaxDelimLength = 5;
Type Type
TParseDelimiter = String[5]; TParseDelimiter = String[MaxDelimLength];
Var Var
DefaultStartDelimiter : TParseDelimiter = '{'; DefaultStartDelimiter : TParseDelimiter = '{'; //Template tag start |If you want Delphi-like, set it to '<#'
DefaultEndDelimiter : TParseDelimiter = '}'; DefaultEndDelimiter : TParseDelimiter = '}'; //Template tag end | '>'
DefaultParamStartDelimiter : TParseDelimiter = '[-'; //Tag parameter start | ' '
DefaultParamEndDelimiter : TParseDelimiter = '-]'; //Tag parameter end | '"'
DefaultParamValueSeparator : TParseDelimiter = '='; //Tag parameter name/value separator | '="'
// |for tags like <#TagName paramname1="paramvalue1" paramname2="paramvalue2">
Type Type
TGetParamEvent = Procedure(Sender : TObject; Const ParamName : String; Out AValue : String) Of Object; TGetParamEvent = Procedure(Sender : TObject; Const ParamName : String; Out AValue : String) Of Object; //for simple template tag support only (ex: {Name})
TReplaceTagEvent = Procedure(Sender : TObject; Const TagString : String; TagParams:TStringList; Out ReplaceText : String) Of Object;//for tags with parameters support
{ TTemplateParser } { TTemplateParser }
@ -45,9 +51,15 @@ Type
FMaxParseDepth : Integer; FMaxParseDepth : Integer;
FEndDelimiter: TParseDelimiter; FEndDelimiter: TParseDelimiter;
FStartDelimiter: TParseDelimiter; FStartDelimiter: TParseDelimiter;
FRecursive: Boolean; FParamStartDelimiter: TParseDelimiter;
FValues : TStringList; FParamEndDelimiter: TParseDelimiter;
FOnGetParam: TGetParamEvent; FParamValueSeparator: TParseDelimiter;
FAllowTagParams: Boolean; //default is false -> simple template tags allowed only [FValues, FOnGetParam (optional) used];
//if true -> template tags with parameters allowed, [FOnReplaceTag] is used for all tag replacements
FRecursive: Boolean; //when only simple tags are used in a template (AllowTagParams=false), the replacement can
FValues : TStringList; //contain further tags for recursive processing (only used when no tag params are allowed)
FOnGetParam: TGetParamEvent; //Event handler to use for templates containing simple tags only (ex: {Name})
FOnReplaceTag: TReplaceTagEvent; //Event handler to use for templates containing tags with parameters (ex: <#TagName paramname1="paramvalue1" paramname2="paramvalue2">)
function GetDelimiter(Index: integer): TParseDelimiter; function GetDelimiter(Index: integer): TParseDelimiter;
function GetValue(Key : String): String; function GetValue(Key : String): String;
procedure SetDelimiter(Index: integer; const AValue: TParseDelimiter); procedure SetDelimiter(Index: integer; const AValue: TParseDelimiter);
@ -56,15 +68,22 @@ Type
Constructor Create; Constructor Create;
Destructor Destroy; override; Destructor Destroy; override;
Procedure Clear; Procedure Clear;
Function GetParam(Const Key : String; Out AValue : String) : Boolean; Function ReplaceTag(const Key: String; TagParams:TStringList; out ReplaceWith: String): Boolean;//used only when AllowTagParams = true
Function GetParam(Const Key : String; Out AValue : String) : Boolean; //used only when AllowTagParams = false
Procedure GetTagParams(var TagName:String; var TagParams : TStringList) ;
Function ParseString(Src : String) : String; Function ParseString(Src : String) : String;
Function ParseStream(Src : TStream; Dest : TStream) : Integer; // Wrapper, Returns number of bytes written. Function ParseStream(Src : TStream; Dest : TStream) : Integer; // Wrapper, Returns number of bytes written.
Procedure ParseStrings(Src : TStrings; Dest : TStrings) ; // Wrapper Procedure ParseStrings(Src : TStrings; Dest : TStrings) ; // Wrapper
Property OnGetParam : TGetParamEvent Read FOnGetParam Write FOnGetParam; // Called if not found in values Property OnGetParam : TGetParamEvent Read FOnGetParam Write FOnGetParam; // Called if not found in values //used only when AllowTagParams = false
Property StartDelimiter : TParseDelimiter Index 1 Read GetDelimiter Write SetDelimiter; // Start char/string, default '}' Property OnReplaceTag : TReplaceTagEvent Read FOnReplaceTag Write FOnReplaceTag; // Called if a tag found //used only when AllowTagParams = true
Property EndDelimiter : TParseDelimiter Index 2 Read GetDelimiter Write SetDelimiter; // end char/string, default '}' Property StartDelimiter : TParseDelimiter Index 1 Read GetDelimiter Write SetDelimiter;// Start char/string, default '}'
Property Values[Key : String] : String Read GetValue Write SetValue; // Contains static values. Property EndDelimiter : TParseDelimiter Index 2 Read GetDelimiter Write SetDelimiter; // end char/string, default '{'
Property Recursive : Boolean Read FRecursive Write FRecursive; Property ParamStartDelimiter : TParseDelimiter Index 3 Read GetDelimiter Write SetDelimiter;
Property ParamEndDelimiter : TParseDelimiter Index 4 Read GetDelimiter Write SetDelimiter;
Property ParamValueSeparator : TParseDelimiter Index 5 Read GetDelimiter Write SetDelimiter;
Property Values[Key : String] : String Read GetValue Write SetValue; // Contains static values. //used only when AllowTagParams = false
Property Recursive : Boolean Read FRecursive Write FRecursive; //used only when AllowTagParams = false
Property AllowTagParams : Boolean Read FAllowTagParams Write FAllowTagParams;
end; end;
{ TFPCustomTemplate } { TFPCustomTemplate }
@ -73,11 +92,17 @@ Type
private private
FEndDelimiter: TParseDelimiter; FEndDelimiter: TParseDelimiter;
FStartDelimiter: TParseDelimiter; FStartDelimiter: TParseDelimiter;
FParamStartDelimiter: TParseDelimiter;
FParamEndDelimiter: TParseDelimiter;
FParamValueSeparator: TParseDelimiter;
FFileName: String; FFileName: String;
FTemplate: String; FTemplate: String;
FOnGetParam: TGetParamEvent; FOnGetParam: TGetParamEvent; //used only when AllowTagParams = false
FOnReplaceTag: TReplaceTagEvent; //used only when AllowTagParams = true
FAllowTagParams: Boolean;
Protected Protected
Procedure GetParam(Sender : TObject; Const ParamName : String; Out AValue : String);virtual; Procedure GetParam(Sender : TObject; Const ParamName : String; Out AValue : String);virtual; //used only when AllowTagParams = false
Procedure ReplaceTag(Sender : TObject; Const TagName: String; TagParams:TStringList; Out AValue: String);virtual; //used only when AllowTagParams = true
Function CreateParser : TTemplateParser; virtual; Function CreateParser : TTemplateParser; virtual;
Public Public
Function HasContent : Boolean; Function HasContent : Boolean;
@ -85,15 +110,22 @@ Type
Procedure Assign(Source : TPersistent); override; Procedure Assign(Source : TPersistent); override;
Property StartDelimiter : TParseDelimiter Read FStartDelimiter Write FStartDelimiter; Property StartDelimiter : TParseDelimiter Read FStartDelimiter Write FStartDelimiter;
Property EndDelimiter : TParseDelimiter Read FEndDelimiter Write FEndDelimiter; Property EndDelimiter : TParseDelimiter Read FEndDelimiter Write FEndDelimiter;
Property ParamStartDelimiter : TParseDelimiter Read FParamStartDelimiter Write FParamStartDelimiter;
Property ParamEndDelimiter : TParseDelimiter Read FParamEndDelimiter Write FParamEndDelimiter;
Property ParamValueSeparator : TParseDelimiter Read FParamValueSeparator Write FParamValueSeparator;
Property FileName : String Read FFileName Write FFileName; Property FileName : String Read FFileName Write FFileName;
Property Template : String Read FTemplate Write FTemplate; Property Template : String Read FTemplate Write FTemplate;
Property OnGetParam : TGetParamEvent Read FOnGetParam Write FOnGetParam; Property OnGetParam : TGetParamEvent Read FOnGetParam Write FOnGetParam;
Property OnReplaceTag : TReplaceTagEvent Read FOnReplaceTag Write FOnReplaceTag;
Property AllowTagParams : Boolean Read FAllowTagParams Write FAllowTagParams;
end; end;
TFPTemplate = Class(TFPCustomTemplate) TFPTemplate = Class(TFPCustomTemplate)
Published Published
Property FileName; Property FileName;
Property Template; Property Template;
// Property AllowTagParams;
// Property OnReplaceTag;
end; end;
ETemplateParser = Class(Exception); ETemplateParser = Class(Exception);
@ -128,6 +160,8 @@ begin
FValue:=AValue; FValue:=AValue;
end; end;
{ TTemplateParser }
function TTemplateParser.GetValue(Key : String): String; function TTemplateParser.GetValue(Key : String): String;
Var Var
@ -145,10 +179,14 @@ end;
function TTemplateParser.GetDelimiter(Index: integer): TParseDelimiter; function TTemplateParser.GetDelimiter(Index: integer): TParseDelimiter;
begin begin
If Index=1 then case Index of
Result:=FStartDelimiter 1: Result:=FStartDelimiter;
else 2: Result:=FEndDelimiter;
Result:=FEndDelimiter; 3: Result:=FParamStartDelimiter;
4: Result:=FParamEndDelimiter;
else
Result:=FParamValueSeparator;
end;
end; end;
procedure TTemplateParser.SetDelimiter(Index: integer; procedure TTemplateParser.SetDelimiter(Index: integer;
@ -156,10 +194,14 @@ procedure TTemplateParser.SetDelimiter(Index: integer;
begin begin
If Length(AValue)=0 then If Length(AValue)=0 then
Raise ETemplateParser.Create(SErrNoEmptyDelimiters); Raise ETemplateParser.Create(SErrNoEmptyDelimiters);
If Index=1 then case Index of
FStartDelimiter:=AValue 1: FStartDelimiter:=AValue;
else 2: FEndDelimiter:=AValue;
FEndDelimiter:=AValue; 3: FParamStartDelimiter:=AValue;
4: FParamEndDelimiter:=AValue;
else
FParamValueSeparator:=AValue;
end;
end; end;
@ -167,8 +209,7 @@ procedure TTemplateParser.SetValue(Key : String; const AValue: String);
Var Var
I : Integer; I : Integer;
SI : TStringItem;
begin begin
If (AValue='') then If (AValue='') then
begin begin
@ -200,9 +241,14 @@ end;
constructor TTemplateParser.Create; constructor TTemplateParser.Create;
begin begin
FParseLevel:=0;
FMaxParseDepth:=MaxParseDepth; FMaxParseDepth:=MaxParseDepth;
FStartDelimiter:=DefaultStartDelimiter; FStartDelimiter:=DefaultStartDelimiter;
FEndDelimiter:=DefaultEndDelimiter; FEndDelimiter:=DefaultEndDelimiter;
FParamStartDelimiter:=DefaultParamStartDelimiter;
FParamEndDelimiter:=DefaultParamEndDelimiter;
FParamValueSeparator:=DefaultParamValueSeparator;
FAllowTagParams := false;
end; end;
destructor TTemplateParser.Destroy; destructor TTemplateParser.Destroy;
@ -247,6 +293,13 @@ begin
AValue:=ParseString(AValue); AValue:=ParseString(AValue);
end; end;
function TTemplateParser.ReplaceTag(const Key: String; TagParams:TStringList; out ReplaceWith: String): Boolean;
begin
Result:=Assigned(FOnReplaceTag);
If Result then
FOnReplaceTag(Self,Key,TagParams,ReplaceWith);
end;
Function FindDelimiter(SP : PChar; D : TParseDelimiter; MaxLen : Integer) : PChar; Inline; Function FindDelimiter(SP : PChar; D : TParseDelimiter; MaxLen : Integer) : PChar; Inline;
Var Var
@ -298,62 +351,168 @@ begin
Move(P^,S[Slen+1],NChars); Move(P^,S[Slen+1],NChars);
end; end;
procedure TTemplateParser.GetTagParams(var TagName:String; var TagParams : TStringList) ;
var
I,SLen:Integer;
TS,TM,TE,SP,P : PChar;
PName, PValue, TP : String;
IsFirst:Boolean;
begin
SLen:=Length(TagName);
if SLen=0 then exit;
IsFirst := true;
SP:=PChar(TagName);
TP := TagName;
P:=SP;
while (P-SP<SLen) do
begin
TS:=FindDelimiter(P,FParamStartDelimiter,SLen-(P-SP));
if (TS<>Nil) then
begin//Found param start delimiter
if IsFirst then
begin//Get the real Tag name
IsFirst := false;
I := 1;
while not (P[I] in [#0..' ']) do Inc(I);
SetLength(TP, I);
Move(P^, TP[1], I);
end;
inc(TS, Length(FParamStartDelimiter));
I:=TS-P;//index of param name
TM:=FindDelimiter(TS,FParamValueSeparator,SLen-I+1);
if (TM<>Nil) then
begin//Found param value separator
I:=TM-TS;//lenght of param name
SetLength(PName, I);
Move(TS^, PName[1], I);//param name
inc(TS, Length(FParamValueSeparator) + I);
I := TS - P;//index of param value
TE:=FindDelimiter(TS,FParamEndDelimiter, SLen-I+1);
if (TE<>Nil) then
begin//Found param end
I:=TE-TS;//Param length
Setlength(PValue,I);
Move(TS^,PValue[1],I);//Param value
TagParams.Add(Trim(PName) + '=' + PValue);//Param names cannot contain '='
P:=TE+Length(FParamEndDelimiter);
TS:=P;
end else break;
end else break;
end else break;
end;
TagName := Trim(TP);
end;
function TTemplateParser.ParseString(Src: String): String; function TTemplateParser.ParseString(Src: String): String;
Var Var
PN,PV : String; PN,PV,ReplaceWith : String;
i,RLen,SLen,STlen : Integer; i,SLen : Integer;
TS,TE,SP,P : PChar; TS,TE,SP,P : PChar;
TagParams:TStringList;
begin begin
Inc(FParseLevel); if FAllowTagParams then
If FParseLevel>FMaxParseDepth then begin//template tags with parameters are allowed
Raise ETemplateParser.CreateFmt(SErrParseDepthExceeded,[FMaxParseDepth]); SLen:=Length(Src);
SLen:=Length(Src); // Minimum If SLen=0 then
If SLen=0 then exit;
exit; Result:='';
STLen:=Length(FStartDelimiter); SP:=PChar(Src);
Result:=''; P:=SP;
SP:=PChar(Src); While (P-SP<SLen) do
P:=SP;
While (P-SP<SLen) do
begin
TS:=FindDelimiter(P,FStartDelimiter,SLen-(P-SP));
If (TS=Nil) then
begin begin
TS:=P; TS:=FindDelimiter(P,FStartDelimiter,SLen-(P-SP));
P:=SP+SLen If (TS=Nil) then
end begin//Tag Start Delimiter not found
else
begin
I:=TS-P;
TE:=FindDelimiter(TS,FendDelimiter,SLen-I+1);
If (TE=Nil) then
begin
TS:=P; TS:=P;
P:=SP+SLen; P:=SP+SLen;
end end
else else
begin begin
// Add text prior to template to result I:=TS-P;
AddToString(Result,P,I); TE:=FindDelimiter(TS,FEndDelimiter,SLen-I+1);
// retrieve template name If (TE=Nil) then
inc(TS,Length(FendDelimiter)); begin//Tag End Delimiter not found
I:=TE-TS; TS:=P;
Setlength(PN,I); P:=SP+SLen;
Move(TS^,PN[1],I); end
If GetParam(PN,PV) then else//Found start and end delimiters for the Tag
begin begin
Result:=Result+PV; // Add text prior to template tag to result
AddToString(Result,P,I);
// Retrieve the full template tag (only tag name if no params specified)
inc(TS,Length(FStartDelimiter));//points to first char of Tag name now
I:=TE-TS;//full Tag length
Setlength(PN,I);
Move(TS^,PN[1],I);//full Tag string (only tag name if no params specified)
TagParams := TStringList.Create;
try
TagParams.Sorted := True;
GetTagParams(PN, Tagparams);
If ReplaceTag(PN,TagParams,ReplaceWith) then
Result:=Result+ReplaceWith;
finally
TagParams.Free;
end; end;
P:=TE+Length(FEndDelimiter); P:=TE+Length(FEndDelimiter);
TS:=P;
end;
end
end;
I:=P-TS;
If (I>0) then
AddToString(Result,TS,I);
end else begin//template tags with parameters are not allowed
Inc(FParseLevel);
If FParseLevel>FMaxParseDepth then
Raise ETemplateParser.CreateFmt(SErrParseDepthExceeded,[FMaxParseDepth]);
SLen:=Length(Src); // Minimum
If SLen=0 then
exit;
// STLen:=Length(FStartDelimiter);
Result:='';
SP:=PChar(Src);
P:=SP;
While (P-SP<SLen) do
begin
TS:=FindDelimiter(P,FStartDelimiter,SLen-(P-SP));
If (TS=Nil) then
begin
TS:=P; TS:=P;
end; P:=SP+SLen
end end
end; else
I:=P-TS; begin
If (I>0) then I:=TS-P;
AddToString(Result,TS,I); TE:=FindDelimiter(TS,FEndDelimiter,SLen-I+1);
If (TE=Nil) then
begin
TS:=P;
P:=SP+SLen;
end
else
begin
// Add text prior to template to result
AddToString(Result,P,I);
// retrieve template name
inc(TS,Length(FStartDelimiter));
I:=TE-TS;
Setlength(PN,I);
Move(TS^,PN[1],I);
If GetParam(PN,PV) then
begin
Result:=Result+PV;
end;
P:=TE+Length(FEndDelimiter);
TS:=P;
end;
end
end;
I:=P-TS;
If (I>0) then
AddToString(Result,TS,I);
end;
end; end;
function TTemplateParser.ParseStream(Src: TStream; Dest: TStream): Integer; function TTemplateParser.ParseStream(Src: TStream; Dest: TStream): Integer;
@ -370,6 +529,7 @@ begin
Finally Finally
SS.Free; SS.Free;
end; end;
FParseLevel := 0;
R:=ParseString(S); R:=ParseString(S);
Result:=Length(R); Result:=Length(R);
If (Result>0) then If (Result>0) then
@ -383,28 +543,48 @@ Var
begin begin
For I:=0 to Src.Count-1 do For I:=0 to Src.Count-1 do
begin
FParseLevel := 0;
Dest.Add(ParseString(Src[i])); Dest.Add(ParseString(Src[i]));
end;
end; end;
{ TFPCustomTemplate } { TFPCustomTemplate }
procedure TFPCustomTemplate.GetParam(Sender: TObject; const ParamName: String; procedure TFPCustomTemplate.GetParam(Sender: TObject; const ParamName: String; out AValue: String);
out AValue: String);
begin begin
If Assigned(FOnGetParam) then If Assigned(FOnGetParam) then
FOnGetParam(Self,ParamName,AValue); FOnGetParam(Self,ParamName,AValue);
end; end;
procedure TFPCustomTemplate.ReplaceTag(Sender: TObject; const TagName: String; TagParams:TStringList; Out AValue: String);
begin
If Assigned(FOnReplaceTag) then
begin
FOnReplaceTag(Self,TagName,TagParams,AValue);
end;
end;
function TFPCustomTemplate.CreateParser: TTemplateParser; function TFPCustomTemplate.CreateParser: TTemplateParser;
begin begin
Result:=TTemplateParser.Create; Result:=TTemplateParser.Create;
Result.FParseLevel := 0;
If (FStartDelimiter<>'') then If (FStartDelimiter<>'') then
Result.StartDelimiter:=FStartDelimiter; Result.StartDelimiter:=FStartDelimiter;
If (FEndDelimiter<>'') then If (FEndDelimiter<>'') then
Result.EndDelimiter:=FEndDelimiter; Result.EndDelimiter:=FEndDelimiter;
If (FParamStartDelimiter<>'') then
Result.ParamStartDelimiter:=FParamStartDelimiter;
If (FParamEndDelimiter<>'') then
Result.ParamEndDelimiter:=FParamEndDelimiter;
If (FParamValueSeparator<>'') then
Result.ParamValueSeparator:=FParamValueSeparator;
Result.OnGetParam:=@GetParam; Result.OnGetParam:=@GetParam;
Result.OnReplaceTag:=@ReplaceTag;
Result.AllowTagParams:=FAllowTagParams;
end; end;
function TFPCustomTemplate.HasContent: Boolean; function TFPCustomTemplate.HasContent: Boolean;
@ -461,9 +641,14 @@ begin
T:=Source as TFPCustomTemplate; T:=Source as TFPCustomTemplate;
FEndDelimiter:=T.EndDelimiter; FEndDelimiter:=T.EndDelimiter;
FStartDelimiter:=T.StartDelimiter; FStartDelimiter:=T.StartDelimiter;
FParamEndDelimiter:=T.ParamEndDelimiter;
FParamStartDelimiter:=T.ParamStartDelimiter;
FParamValueSeparator:=T.ParamValueSeparator;
FFileName:=T.FileName; FFileName:=T.FileName;
FTemplate:=T.Template; FTemplate:=T.Template;
FOnGetParam:=T.OnGetParam; FOnGetParam:=T.OnGetParam;
FOnReplaceTag:=T.OnReplaceTag;
FAllowTagParams := T.AllowTagParams;
end end
else else
inherited Assign(Source); inherited Assign(Source);

View File

@ -268,9 +268,9 @@ type
procedure ParseFirstHeaderLine(const line: String);override; procedure ParseFirstHeaderLine(const line: String);override;
function GetFirstHeaderLine: String; function GetFirstHeaderLine: String;
Protected Protected
Procedure ProcessMultiPart(Stream : TStream; Const Boundary : String); virtual; Procedure ProcessMultiPart(Stream : TStream; Const Boundary : String;SL:TStrings); virtual;
Procedure ProcessQueryString(Const FQueryString : String); virtual; Procedure ProcessQueryString(Const FQueryString : String; SL:TStrings); virtual;
procedure ProcessURLEncoded(Stream : TStream); virtual; procedure ProcessURLEncoded(Stream : TStream;SL:TStrings); virtual;
Function GetTempUploadFileName : String; virtual; Function GetTempUploadFileName : String; virtual;
Property ReturnedPathInfo : String Read FReturnedPathInfo Write FReturnedPathInfo; Property ReturnedPathInfo : String Read FReturnedPathInfo Write FReturnedPathInfo;
public public
@ -656,6 +656,7 @@ constructor THttpHeader.Create;
begin begin
FCookieFields:=TStringList.Create; FCookieFields:=TStringList.Create;
FQueryFields:=TStringList.Create; FQueryFields:=TStringList.Create;
FContentFields:=TStringList.Create;
FHttpVersion := '1.1'; FHttpVersion := '1.1';
end; end;
@ -664,6 +665,7 @@ destructor THttpHeader.Destroy;
begin begin
FreeAndNil(FCookieFields); FreeAndNil(FCookieFields);
FreeAndNil(FQueryFields); FreeAndNil(FQueryFields);
FreeAndNil(FContentFields);
inherited Destroy; inherited Destroy;
end; end;
@ -935,7 +937,7 @@ begin
Result := Result + ' HTTP/' + HttpVersion; Result := Result + ' HTTP/' + HttpVersion;
end; end;
Procedure TRequest.ProcessQueryString(Const FQueryString : String); Procedure TRequest.ProcessQueryString(Const FQueryString : String; SL:TStrings);
var var
@ -1038,7 +1040,7 @@ begin
if (QueryItem<>'') then if (QueryItem<>'') then
begin begin
QueryItem:=HTTPDecode(QueryItem); QueryItem:=HTTPDecode(QueryItem);
FQueryFields.Add(QueryItem); SL.Add(QueryItem);
end; end;
end; end;
{$ifdef CGIDEBUG}SendMethodExit('ProcessQueryString');{$endif CGIDEBUG} {$ifdef CGIDEBUG}SendMethodExit('ProcessQueryString');{$endif CGIDEBUG}
@ -1050,7 +1052,7 @@ begin
end; end;
Procedure TRequest.ProcessMultiPart(Stream : TStream; Const Boundary : String); Procedure TRequest.ProcessMultiPart(Stream : TStream; Const Boundary : String; SL:TStrings);
Var Var
L : TList; L : TList;
@ -1129,7 +1131,7 @@ begin
end; end;
FI.Free; FI.Free;
L[i]:=Nil; L[i]:=Nil;
QueryFields.Add(Key+'='+Value) SL.Add(Key+'='+Value)
end; end;
Finally Finally
For I:=0 to L.Count-1 do For I:=0 to L.Count-1 do
@ -1139,7 +1141,7 @@ begin
{$ifdef CGIDEBUG} SendMethodExit('ProcessMultiPart');{$endif CGIDEBUG} {$ifdef CGIDEBUG} SendMethodExit('ProcessMultiPart');{$endif CGIDEBUG}
end; end;
Procedure TRequest.ProcessURLEncoded(Stream: TStream); Procedure TRequest.ProcessURLEncoded(Stream: TStream; SL:TStrings);
var var
S : String; S : String;
@ -1149,7 +1151,7 @@ begin
SetLength(S,Stream.Size); // Skip added Null. SetLength(S,Stream.Size); // Skip added Null.
Stream.ReadBuffer(S[1],Stream.Size); Stream.ReadBuffer(S[1],Stream.Size);
{$ifdef CGIDEBUG}SendDebugFmt('Query string : %s',[s]);{$endif CGIDEBUG} {$ifdef CGIDEBUG}SendDebugFmt('Query string : %s',[s]);{$endif CGIDEBUG}
ProcessQueryString(S); ProcessQueryString(S,SL);
{$ifdef CGIDEBUG} SendMethodEnter('ProcessURLEncoded');{$endif CGIDEBUG} {$ifdef CGIDEBUG} SendMethodEnter('ProcessURLEncoded');{$endif CGIDEBUG}
end; end;