|
@@ -14,10 +14,11 @@ 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.
|
|
|
+traditional Delphi way of handling templates. The rest of this file is about
|
|
|
+these additions.
|
|
|
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.
|
|
@@ -34,11 +35,13 @@ EndDelimiter:='+}'
|
|
|
(the default '{' and '}' is not good when processing HTML templates with
|
|
|
JavaSript in them):
|
|
|
|
|
|
-{+ATagHere+}
|
|
|
+{+ATagHere+} //Tag name: ATagHere
|
|
|
|
|
|
-{+AnotherTagHere [-paramname1=paramvalue1-]+}
|
|
|
+{+AnotherTagHere [-paramname1=paramvalue1-]+}
|
|
|
+<!--Tag name: AnotherTagHere ; Tag param name=paramname1;
|
|
|
+Tag param value=paramvalue1-->
|
|
|
|
|
|
-{+HereIsATagToo //with param
|
|
|
+{+HereIsATagToo //Tag name=HereIsATagToo; with 3 paramname-paramvalue pairs
|
|
|
[-param1=param1value-] //some text here to ignore
|
|
|
//this text is ignored too
|
|
|
[-param2=param2value which
|
|
@@ -66,7 +69,8 @@ This allows the use of Dephi-like tags like these:
|
|
|
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.
|
|
|
+some might contain characters indicating tag-param-end or tag-end (see below
|
|
|
+to overcome this).
|
|
|
|
|
|
When the tags are processed, for each tag a
|
|
|
|
|
@@ -81,7 +85,7 @@ 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
|
|
|
+begin //Template:TFPTemplate is a property of the web module
|
|
|
Template.FileName := 'pathtotemplate\mytemplate.html';
|
|
|
Template.AllowTagParams := true;
|
|
|
Template.StartDelimiter := '{+';
|
|
@@ -106,7 +110,7 @@ end;
|
|
|
procedure TFPWebModule1.func1callReplaceTag(Sender: TObject; const TagString:
|
|
|
String; TagParams: TStringList; Out ReplaceText: String);
|
|
|
begin
|
|
|
- if AnsiCompareText(TagString, 'TagName1') = 0 then
|
|
|
+ if AnsiCompareText(TagString, 'ATagHere') = 0 then
|
|
|
begin
|
|
|
ReplaceText := 'text to replace this tag, using the TagParams if needed';
|
|
|
end else begin
|
|
@@ -119,8 +123,9 @@ 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:
|
|
|
+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
|
|
@@ -140,7 +145,7 @@ list the web designer can use the following Tag in a template:
|
|
|
</tr>
|
|
|
-]
|
|
|
.
|
|
|
-.snip, and so on more parameters
|
|
|
+.snip, and so on more parameters if needed
|
|
|
.
|
|
|
[- NOTFOUND=
|
|
|
<tr class="Error"><td>There are no entries found.</td></tr>
|
|
@@ -162,3 +167,261 @@ sql query result set.
|
|
|
Or if there is nothing to list, just use the NOTFOUND parameter as a replace
|
|
|
text for the whole REPORTRESULT template tag.
|
|
|
|
|
|
+Sample code for the above template snippet (see below for simpler examples
|
|
|
+in the Step By Step list):
|
|
|
+
|
|
|
+procedure TFPWebModule1.func2callRequest(Sender: TObject; ARequest: TRequest;
|
|
|
+ AResponse: TResponse; var Handled: Boolean);
|
|
|
+var s:String;
|
|
|
+begin //Template:TFPTemplate is a property of the web module
|
|
|
+ Template.FileName := 'pathtotemplate\mytemplate.html';{template file with
|
|
|
+the above template tag -> REPORTRESULT}
|
|
|
+ Template.AllowTagParams := true;
|
|
|
+ Template.StartDelimiter := '{+';
|
|
|
+ Template.EndDelimiter := '+}';
|
|
|
+ Template.OnReplaceTag := @func2callReplaceTag;
|
|
|
+
|
|
|
+ AResponse.Content := Template.GetContent;
|
|
|
+
|
|
|
+ Handled := true;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TFPWebModule1.func2callReplaceTag(Sender: TObject; const TagString:
|
|
|
+ String; TagParams: TStringList; Out ReplaceText: String);
|
|
|
+var
|
|
|
+ header, footer, onerow, notfound:String;
|
|
|
+ NoRecordsToShow, EndOfRecords:Boolean;
|
|
|
+begin//HTML template tag handling for an html template file
|
|
|
+ //Replace the REPORTRESULT html tag using it's tag parameters
|
|
|
+ if AnsiCompareText(TagString, 'REPORTRESULT') = 0 then
|
|
|
+ begin
|
|
|
+ //NoRecordsToShow could be something like SQL1.IsEmpty , etc.
|
|
|
+ if NoRecordsToShow then
|
|
|
+ begin //if there's nothing to list, just replace the whole tag with the
|
|
|
+ //Not Found message that the template contains
|
|
|
+ ReplaceText := TagParams.Values['NOTFOUND'];
|
|
|
+ Exit;
|
|
|
+ end;
|
|
|
+
|
|
|
+ header := TagParams.Values['HEADER'];
|
|
|
+ //insert header parameters
|
|
|
+ //1st column title
|
|
|
+ header := StringReplace(header, '~Column1', '1st column', []);
|
|
|
+ //2nd column title
|
|
|
+ header := StringReplace(header, '~Column2', '2nd column', []);
|
|
|
+
|
|
|
+ ReplaceText := header;//done with the header (could have been looping
|
|
|
+ //through table field names also)
|
|
|
+ //insert the rows
|
|
|
+ onerow := TagParams.Values['ONEROW'];//template for 1 row
|
|
|
+ //loop through the rows, it could be someting like "while not SQL1.EOF do"
|
|
|
+ while not EndOfRecords do
|
|
|
+ begin
|
|
|
+ ReplaceText := ReplaceText + StringReplace(StringReplace(onerow
|
|
|
+ ,'~Column1Value', '$14.56', [])
|
|
|
+ ,'~Column2Value', '$12.00', []);
|
|
|
+
|
|
|
+ //get the next record, it could be:
|
|
|
+ //SQL1.Next
|
|
|
+ end;
|
|
|
+
|
|
|
+ //insert the footer
|
|
|
+ footer := TagParams.Values['FOOTER'];
|
|
|
+ //replace footer parameters if needed
|
|
|
+ //...
|
|
|
+
|
|
|
+ ReplaceText := ReplaceText + footer;
|
|
|
+ end else begin
|
|
|
+
|
|
|
+//Not found value for tag -> TagString
|
|
|
+ ReplaceText := 'Template tag {' + TagString + '} is not implemented yet.';
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+full example code at /packages/fcl-web/fptemplate-examples/listrecords/
|
|
|
+===============================================================================
|
|
|
+
|
|
|
+Step by Step:
|
|
|
+
|
|
|
+Creating CGI or Apache applications with WebModule in Lazarus, using HTML
|
|
|
+templates (FPTemplate)
|
|
|
+
|
|
|
+ I. Hello World first
|
|
|
+ II. Using templates
|
|
|
+III. More complicated HTML template design notes
|
|
|
+ IV. Passing tag parameters
|
|
|
+===============================================================================
|
|
|
+
|
|
|
+I. Hello World first:
|
|
|
+
|
|
|
+1. File -> New -> CGI application or Apache module
|
|
|
+2. Delete the httpd20 and httpd13 directories (we are making Apache 2.2 modules)
|
|
|
+from the fpc directory (ex: C:\pp\units\i386-win32\httpd20\ and
|
|
|
+C:\pp\units\i386-win32\httpd13\)
|
|
|
+Need to recompile FPC and then Lazarus if FPC was earlier compiled with these
|
|
|
+older httpd13 or httpd20 files.
|
|
|
+To avoid this recompilation you can also just copy all the files from the
|
|
|
+/packages/fcl-web/src/ directory into your project directory so they will
|
|
|
+be recompiled as needed.
|
|
|
+3. Click inside the webmodule if not already selected
|
|
|
+4. In the Object Inspector double click on the "Actions"
|
|
|
+5. Click on +Add to create a new action for your web module
|
|
|
+6. Change Default to True if you wish this one to be the default action
|
|
|
+7. Change the action name to "func1call" (this will be the calling identifier
|
|
|
+of this action from the web browser. Something like
|
|
|
+http://localhost/mod_apache1/func1call?param1=... )
|
|
|
+
|
|
|
+8. Inside the Events tab, double click on the "OnRequest" to create the
|
|
|
+procedure called "func1callRequest" that handles this action
|
|
|
+9. Enter the following into the procedure body:
|
|
|
+
|
|
|
+procedure TFPWebModule1.func1callRequest(Sender: TObject; ARequest: TRequest;
|
|
|
+ AResponse: TResponse; var Handled: Boolean);
|
|
|
+begin
|
|
|
+ AResponse.Content := '<html><body>Hello World!</body></html>';
|
|
|
+
|
|
|
+ Handled := true;
|
|
|
+end;
|
|
|
+
|
|
|
+10. Save all, compile, configure the apache server to load the module:
|
|
|
+in your apache httpd.conf you can put
|
|
|
+
|
|
|
+LoadModule mod_apache1 "/<path to the mod>/mod_apache1.dll" #or mod_apache1.so
|
|
|
+<Location /myapache>
|
|
|
+ SetHandler mod_apache1
|
|
|
+ Order allow,deny
|
|
|
+ Allow from all
|
|
|
+</Location>
|
|
|
+
|
|
|
+11. Call your module action from your web browser
|
|
|
+ex: http://localhost/myapache/func1call?param1=paramvalue1
|
|
|
+12. See "Hello World!" in your browser
|
|
|
+13. Repeat from step 4 for other web actions
|
|
|
+
|
|
|
+full example code at /packages/fcl-web/fptemplate-examples/helloworld/
|
|
|
+===============================================================================
|
|
|
+
|
|
|
+II. Using templates:
|
|
|
+
|
|
|
+1. Lets make a simple html template and save it as mytemplate1.html :
|
|
|
+
|
|
|
+<html>
|
|
|
+<body>
|
|
|
+This is a replaced template tag: {TagName1}
|
|
|
+</body>
|
|
|
+</html>
|
|
|
+
|
|
|
+2. Save it and put it somewhere your apache module can access it (ex: below the
|
|
|
+apache module .dll or .so in a directory called "templates/")
|
|
|
+3. Declare a procedure for your web module to handle the template tags
|
|
|
+
|
|
|
+ private
|
|
|
+ { private declarations }
|
|
|
+ procedure func1callReplaceTag(Sender: TObject; const TagString:String;
|
|
|
+ TagParams: TStringList; Out ReplaceText: String);
|
|
|
+
|
|
|
+4. Create the body of the procedure
|
|
|
+
|
|
|
+procedure TFPWebModule1.func1callReplaceTag(Sender: TObject; const TagString:
|
|
|
+ String; TagParams: TStringList; Out ReplaceText: String);
|
|
|
+begin
|
|
|
+ if AnsiCompareText(TagString, 'TagName1') = 0 then
|
|
|
+ begin
|
|
|
+ ReplaceText := 'Here I am from the web module!';
|
|
|
+ end else begin
|
|
|
+
|
|
|
+//Not found value for tag -> TagString
|
|
|
+ ReplaceText := 'Template tag {' + TagString + '} is not implemented yet.';
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+5. In step 9 above in the fist example change the procedure body to:
|
|
|
+
|
|
|
+procedure TFPWebModule1.func1callRequest(Sender: TObject; ARequest: TRequest;
|
|
|
+ AResponse: TResponse; var Handled: Boolean);
|
|
|
+begin //Template:TFPTemplate is a property of the web module
|
|
|
+ Template.FileName := 'pathtotemplate/mytemplate1.html';
|
|
|
+ Template.AllowTagParams := true;
|
|
|
+ Template.OnReplaceTag := @func1callReplaceTag;
|
|
|
+ AResponse.Content := Template.GetContent;
|
|
|
+
|
|
|
+ Handled := true;
|
|
|
+end;
|
|
|
+
|
|
|
+6. Compile, etc. and call it. Should show
|
|
|
+
|
|
|
+This is a replaced template tag: Here I am from the web module!
|
|
|
+
|
|
|
+full example code at /packages/fcl-web/fptemplate-examples/simpletemplate/
|
|
|
+===============================================================================
|
|
|
+
|
|
|
+III. More complicated HTML template design notes:
|
|
|
+
|
|
|
+1. Template tag delimiters.
|
|
|
+
|
|
|
+ Template.StartDelimiter := '{+';
|
|
|
+ Template.EndDelimiter := '+}';
|
|
|
+should be used if there are { or } characters in the HTML template
|
|
|
+(ex: Javascript exist in the template)
|
|
|
+
|
|
|
+2. For "same as Delphi" template tag handling, use
|
|
|
+ Template.StartDelimiter := '<#';
|
|
|
+ Template.EndDelimiter := '>';
|
|
|
+ Template.ParamStartDelimiter := ' ';
|
|
|
+ Template.ParamEndDelimiter := '"';
|
|
|
+ Template.ParamValueSeparator := '="';
|
|
|
+
|
|
|
+ex: <#TagName1 param1="value1" param2="value2">
|
|
|
+
|
|
|
+===============================================================================
|
|
|
+
|
|
|
+IV. Passing tag parameters:
|
|
|
+
|
|
|
+You can pass parameters to your CGI/Apache web module from the templates.
|
|
|
+
|
|
|
+Example HTML template tag:
|
|
|
+{+HereIsATag
|
|
|
+ [-param1=param1value-] //some text here to ignore
|
|
|
+-]
|
|
|
+ [-param3=param3value-]
|
|
|
++}
|
|
|
+
|
|
|
+ex: {+DATETIME [-FORMAT=MM/DD hh:mm:ss-]+}
|
|
|
+
|
|
|
+Code:
|
|
|
+procedure TFPWebModule1.func1callRequest(Sender: TObject; ARequest: TRequest;
|
|
|
+ AResponse: TResponse; var Handled: Boolean);
|
|
|
+var s:String;
|
|
|
+begin //Template:TFPTemplate is a property of the web module
|
|
|
+ Template.FileName := 'pathtotemplate\templatename.html';
|
|
|
+ Template.AllowTagParams := true;
|
|
|
+ Template.StartDelimiter := '{+';
|
|
|
+ Template.EndDelimiter := '+}';
|
|
|
+ Template.OnReplaceTag := @func1callReplaceTag;
|
|
|
+
|
|
|
+ AResponse.Content := Template.GetContent;
|
|
|
+
|
|
|
+ Handled := true;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TFPWebModule1.func1callReplaceTag(Sender: TObject; const TagString:
|
|
|
+ String; TagParams: TStringList; Out ReplaceText: String);
|
|
|
+begin
|
|
|
+ if AnsiCompareText(TagString, 'DATETIME') = 0 then
|
|
|
+ begin
|
|
|
+ ReplaceText := FormatDateTime(TagParams.Values['FORMAT'], Now);
|
|
|
+ end else begin
|
|
|
+
|
|
|
+//Not found value for tag -> TagString
|
|
|
+ ReplaceText := 'Template tag {' + TagString + '} is not implemented yet.';
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+For example, this way if the web designer changes the look of a page, - in this
|
|
|
+ case the format of the date/time on the page - no changes are needed in the
|
|
|
+apache module code, therefore no recompiling or apache restart is needed. The
|
|
|
+best way is to make the project such, that the web/html design is separated
|
|
|
+from the back end apache module as much as possible.
|
|
|
+
|
|
|
+full example code at /packages/fcl-web/fptemplate-examples/tagparam/
|
|
|
+===============================================================================
|