Using fpWeb components
lazarus / 1-Linux
----------------------------------------------------------------------
sandeep_c24 · 2009-02-25 09:23
Is there any example of how to use components in fpWeb to generate
html pages? I want to write web application as apache modules and I am
looking at the possibility of using these components.
Sandeep
----------------------------------------------------------------------
chrnobel · 2009-02-25 10:35
You can use Powtils:
http://z505.com/powtils/idx.shtml
It is quite easy to use, albeit it seems like development has frozen.
/Christian
----------------------------------------------------------------------
bobo · 2009-02-25 18:51
The simplest is to use the TFPTemplate to generate pages. With it you
can use template tags in your HTML files that will be replaced in your
apache module.
Some info can be found in /packages/fcl-web/fptemplate.txt
----------------------------------------------------------------------
sandeep_c24 · 2009-02-26 10:37
I had a look at the fptemplate.txt but I could not figure out how to
replace!column1value.
Could anyone give a complete example of using templates?
Sandeep
----------------------------------------------------------------------
chrnobel · 2009-02-26 13:17
Again, take a closer look at Powtils.
It is very simple to make webapplications, and if you eg. combine it
with Zeoslib, you can make very powerful on-line databases.
/Christian
----------------------------------------------------------------------
Loesje · 2009-02-26 13:59
Quite a coincidence, I just explained my collegue how to use fpweb.
It totally lacks any documentation, though. And I don't have a simple
example. But I can tell you what I told him:
First install fpweb in Lazarus, offcourse. Then create a new cgi-
application. You can convert it to an apache-module later.
An empty program with a TFPWebModule will be created. At first, just
add a 'OnRequest' event and add the following code:
Code: [Select]AResponse.contents.add('Hello world');
Handled := True;
Compile the program and place it somewhere where your web-server can
find it and recognizes it as a cgi-application (you can use the
'execute after' setting of Lazarus in the compiler options for that
goal). Then open it in your web-browser and admire the result...
(Hello world)
But that's useless. So now remove the OnRequest event and add an
action to the TFPWebModule. Place something like 'Hello world 2' in
it's 'content' property and make the action the default. Compile
(+copy) and refresh your web-browser.
Now add a second action, give it a name like 'goodbye' and also add
'goodbye' to the content-property. Now you can add '?action=goodbye'
to your URL and it will show 'goodbye' instead of 'Hello World 2'.
Now you probably want somewhat more. Drop a
THTMLDatasetFormEditProducer on your module and connect it to a
datasource linked to a dataset, as you would do normally. You have to
set TableCols and TableRows of the producer and add some columns which
are bound to some data-fields.
Now set the 'ContentProducer' property of one of the actions to the
THTMLDatasetFormEditProducer you just made. Compile and use your
webbrowser to open the page with the right action. If all goes well it
will show you some data from your dataset...
Last thing for now is how to combine more producers and also add your
own tags. That can be done by creating a new action and assign a new
THtmlEntityProducer to it. Then create an 'OnWriteEntity' event (Or a
'OnWritePage' event for fpc-versions more then two days old) and add
the following code:
Code: [Select]procedure TwmMain.eHeaderWritePage(Sender:
THTMLContentProducer; aWriter: THTMLWriter);
begin
with aWriter do
begin
startHeader;
Meta('','Content-Type','text/html; charset=ISO-8859-1');
title('My webpage');
link('stylesheet','stylesheet.css','text/css','screen');
EndHeader;
Startbody;
paragraph('Some text');
Image.src := 'logo.png';
ADatasetFormProducer.WriteContent(aWriter);
Endbody;
end;
end;
Just to give you some ideas... Latest fpc-versions also have a
THTMLPageProducer component which simply links a header, body and
footer together, which I found usefull to use. It's not included by
the Lazarus fpweb-packate (yet), though.
----------------------------------------------------------------------
bee · 2009-02-26 14:22
fpWeb and Powtils has a different approach which each has it's own
advantages and disadvantages. Nobody can't assure you which is the
best approach for you but you yourself. It's just a matter of
programming habit. If you like the PHP-like (more procedural) way
programming habit, then maybe Powtils will suit you more than fpWeb.
It'd be another way around if you like VCL-like (very OOP-ish) way
programming habit. So, it's just a matter of choice.
Anyway, one thing I'd like to see is FastCGI support from fpWeb
package. It should be out of the box as the Apache module support. I
prefer to have my web app as an FCGI service rather than as an Apache
module. It doesn't force you to restart the whole server if you need
to restart your application which somehow I found it's easier to
maintain during implementation.
----------------------------------------------------------------------
bobo · 2009-02-26 21:56
This might be a little long, but I wrote some steps and code snippets
about how to use templates (FPTemplate):
Creating CGI or Apache applications with WebModule, using HTML
templates (FPTemplate)
Hello World first:
1. File -> New -> CGI application or Apache module
2. in the Project -> Compiler options put the path to the httpd22 (we
are using Apache 2.2 web server) directory into the Other Unit files
field
(ex: C:\pp\units\i386-win32\httpd22\)
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 := '
Hello World!';
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 "//mod_apache1.dll" #or
mod_apache1.so, etc.
SetHandler mod_apache1
Order allow,deny
Allow from all
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
==============
Using templates:
1. Lets make a simple html template and save it as mytemplate1.html :
This is a replaced template tag: {TagName1}
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 Action
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!
======================
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)
more in the FPC /packages/fcl-web/fptemplate.txt
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">
more in the FPC /packages/fcl-web/fptemplate.txt
======================
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 Action
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.
----------------------------------------------------------------------
bobo · 2009-02-26 22:42
To answer your question more specifically regarding the
fptemplate.txt, here is an example for replacing the html tag using
the tag parameters:
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 one html template file (ex:
mytemplate1.html)
if AnsiCompareText(TagString, 'DATETIME') = 0 then //Replace
the DATETIME html tag from the previous post example
begin
ReplaceText := FormatDateTime(TagParams.Values['FORMAT'], Now);
end else
if AnsiCompareText(TagString, 'REPORTRESULT') = 0 then //Replace
the REPORTRESULT html tag using it's tag parameters
begin
//NoRecordsToShow could be something like SQL1.IsEmpty , etc.
if NoRecordsToShow then
begin //if theres 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;
----------------------------------------------------------------------
sandeep_c24 · 2009-02-27 05:43
Thanks everyone for the tips.
Loesje could you please give an example of using THTMLPageProducer?
Sandeep
----------------------------------------------------------------------
bobo · 2009-02-27 18:39
Just in case, I've created some more explanations into fptemplate.txt
as well as multiple example projects and step by step instructions how
to create these apache/cgi applications.
If you unpack the attached zip file (template-examples.zip) in
/packages/fcl-web/ it should all work until
http://bugs.freepascal.org/view.php?id=13250 is applied (a patch to
include everything from the zip file into the SVN)
Note if you are making apache modules: "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."
----------------------------------------------------------------------
sandeep_c24 · 2009-02-27 20:55
Thanks bobo. I'll have a look at the examples.
Sandeep
----------------------------------------------------------------------
sandeep_c24 · 2009-02-27 22:13
I want to have a Master page(like in asp.net) and then for each action
I want to create the body part for that action. This way the look and
feel of the website can be handled easily and I would not have to
recompile the web-application.
How should I go about doing this? Can this be done easily using
THTMLPageProducer or should I stick to using Template for Actions?
Sandeep
----------------------------------------------------------------------
sandeep_c24 · 2009-02-27 22:41
How do I generate following using THTMLPageProducer
Sandeep
----------------------------------------------------------------------
sandeep_c24 · 2009-02-27 23:31
How do I redirect the request to login if the login is not valid?
Sandeep
----------------------------------------------------------------------
bobo · 2009-02-28 00:26
Maybe return with the html code for the login page again?
or if you don't need an error message displayed then
AResponse.Content :=
'Redirection' +
'' +
'Please wait...';
or something similar
PS: Start new threads for new questions so this thread will not be a
project development and hiccup history
----------------------------------------------------------------------
sandeep_c24 · 2009-03-08 10:44
Hi bobo
I have now finished my simple website using CGI. It works fine and as
expected, but when I converted it to Apache Module it did not work
properly.
What is happening is I am getting same page for different web actions.
When I go to login page I get Login page correctly but when I click
some link on the page, which points to another web action I do not see
that page but see the Login page. This works properly in CGI.
I am using Templates as you explained earlier in this thread. But to
me it looks like Template is a property of WebModule rather than of
Action and for some reason Templates cannot be changed once assigned.
Sandeep
----------------------------------------------------------------------
bobo · 2009-03-08 19:46
Hi,
Yes, there are a few patches I have submitted to correct a bunch of
problems, but not yet included in the SVN.
I have attached a zip file (fcl-web-fixes.zip) that you can unzip in
your /packages/fcl-web/ directory to have all the fixes.
Just overwrite the files there with the ones in the zip.
Also, in the next post I'll attach a bunch of examples on how to use
templates (max upload is only 128KB so had to split into two).
They needed the above bug fixes to work properly.
You can unzip them into the fcl-web directory too.
Included fixes:
http://bugs.freepascal.org/view.php?id=13228
http://bugs.freepascal.org/view.php?id=13250
http://bugs.freepascal.org/view.php?id=13254
http://bugs.freepascal.org/view.php?id=13280
http://bugs.freepascal.org/view.php?id=13287
http://bugs.freepascal.org/view.php?id=13288
----------------------------------------------------------------------
bobo · 2009-03-08 19:47
...here are the examples attached (they require the patches above to
work, that are included in the above zip file)
----------------------------------------------------------------------
sandeep_c24 · 2009-03-10 08:41
Hi bobo
Thanks for that. The web app is working fine. I'll test it a bit more
and get back if I find any problem.
Thanks again for the great work.
Sandeep
----------------------------------------------------------------------
vfclists · 2010-10-08 19:24
Loesje, do you have a good example of THTMLDatasetFormEditProducer
working correctly? I tried it and it was all over the place. The form
editor would not update the .lfm correctly, and the placement was
hardly logical, and the row and column positioning was simply non
existent. There is hardly any proper CSS labelling in the output.
Creating objects in code may work, but does laying out rows and
columns in the IDE properties really work?
I am using a recent svn update, 27548 to be exact.
Quote from: Loesje on February 26, 2009, 02:59:56 pmQuite a
coincidence, I just explained my collegue how to use fpweb.
It totally lacks any documentation, though. And I don't have a simple
example. But I can tell you what I told him:
----snip--------
Now you probably want somewhat more. Drop a
THTMLDatasetFormEditProducer on your module and connect it to a
datasource linked to a dataset, as you would do normally. You have to
set TableCols and TableRows of the producer and add some columns which
are bound to some data-fields.
Now set the 'ContentProducer' property of one of the actions to the
THTMLDatasetFormEditProducer you just made. Compile and use your
webbrowser to open the page with the right action. If all goes well it
will show you some data from your dataset...
--- snip