NetTalk WebServer - FAQ | |||
Version
www.capesoft.com Updated 11 November 2009 |
|||
Learn
NetTalk
|
Examples
|
Common
Features |
NetAuto
Objects |
NetSimple
Objects |
Dial-Up
|
Support
|
Version
History
|
NetTalk Web Server Documentation Index |
|
Basic Techniques | A good introduction to many new concepts for programming on the web. Recommended reading for beginners. |
Template Reference | Describes how to add, and use the WebServer templates in your application. |
Using Reports | How to re-use your existing report procedures in a NetTalk Webserver application. |
Building Secure (SSL) Web Sites | How to make your web sites use the Secure Sockets Layer. |
Deploying a Web server | How to deploy your web server on the Lan, WAN or Internet. |
FAQ | This document |
Version History | Upgrading from a previous version? Read the Version history here. (recommended) |
Also | |
Selecting a tool for building web sites | There are no silver bullets, and no perfect tool. Read this to determine which tool is best for your situation. |
Pre-Sales FAQ | Pre-Sales questions are covered here |
Shameless Plug: John
Hickey
of Positive Software has launched
NetTalk
Central web forum where NetTalk users can hang out, swap
tips and help each other. Although not affiliated to CapeSoft we support
this venture and encourage users to check out the forum for themselves.
Contents - NetTalk WebServer - FAQ | |||
W1) | Can I lose Multiple Logout Links? | ||
W2) | Frame contents on start-up | ||
W3) | Forms default to View-Only mode if user not logged in | ||
W4) | Calling Forms directly - not via a Browse | ||
W5) | I get an error when compiling my NetWebHandler procedure... | ||
W6) | I want to put in a menu item that links to another site. | ||
W7) | How many simultaneous connections can an EXE made with NetTalk WebServer | ||
W8) | How many page requests can be handled | ||
W9) | Why doesn't my Browse Filter work? | ||
W10) | Why do I get a 'Page not Available' or 'Page cannot be found' error? | ||
W11) | Passing Parameters between web procedures | ||
W12) | Sending a file to the browser as a Page | ||
W13) | Client IP Address | ||
W14) | Preserving the Session ID in static pages | ||
W15) | Assigning multiple fields after a lookup | ||
W16) | Adding the Web Server to a Multi-DLL app | ||
W17) | How can my User LogOut | ||
W18) | How do I refresh the page in another Frame when this frame loads? | ||
W19) | How do I create my own Styles? | ||
W20) | What is the __ (double underscore) for? | ||
W21) | How do I get additional information from a lookup? | ||
W22) | How do I do Reports in a Web app? | ||
W23 | Where can I host NetTalk server apps? | ||
W24) | How can I create my own Error page? | ||
W25 | In my FormPage, I want to call my own method to update a file, how do I prevent NetTalk from updating the tables? | ||
W26 | How to set a value from the menu that gets passed to my product browse so I can filter that by the a product category. | ||
W27 | I upgraded to version 4.31 (or later) and now I'm getting compile errors in my WebHandler procedure. | ||
Question:
Currently there’s a “Logout” button and “Logout” text link next to each other under the Outlook menu. Can we kill one of those (preferably the button)? It seems redundant to have them both there.
Answer:
The whole idea in the example is to show both approaches so you can pick the one you prefer.
The Link code looks like this;<a href="javascript:top.location.reload(1)">LogOut</a>The Button code looks like this;<form action="FramePage" method="post" name="Form_frm" id="Form_frm"><!-- Net:s:SessionID-->
<input type="submit" value="Logout" name="logout_btn" class="mainbutton" onClick="javascript:top.location.reload(1)">
</Form>
Question:
I’d like one of the browses to display immediately after a successful login (as opposed to the user having to click a menu option.) What’s the best way/place of doing that?
Answer:Each frame inside the frameset has a default URL setting. Go to your FrameSet procedure, identify which frame you want to default, and enter the URL in there. Remember you can use a procedure name as a URL.
Question:
I can see the option which requires a person to be logged in in order to see the Form. However if that fails then it goes to the Login window. I’d like it to revert to view-only mode if the person is not logged in.
Answer:
Firstly, don’t use the Must be Logged in option. Make sure that checkbox is blank.
Secondly, set the option View-Only mode IF top_web.GetSessionLoggedIn()=0
Question:
How do I call a form directly using a link. Not via a Browse.
Answer:[aside: As from version 4.16 there are some changes here. The UpdateFile, UpdateKey and IDField parameters are no longer necessary, so the URL is shorter.
First, take the case where you’re using a Menu extension. In this case all we need to know is the correct URL, the rest is done for us.
It’s simple, but exact. Because the form is expecting to be called from the browse, it’s expecting the browse to tell it what to do, and on which record to do it. Here’s an example;
MailboxesFormControl?<!-- Net:s:SID-->&MAI__MailBoxNumber=12&Change_btn=Change&orMailboxesFormControl?<!-- Net:s:SID-->&MAI__MailBoxNumber=12&Delete_btn=Delete&Here’s an example for Insert:
MailboxesFormControl?<!-- Net:s:SID-->&Insert_btn=Insert&If the form you are calling works on Memory, and not on a file, then the link looks like this;MailboxesFormControl?<!-- Net:s:SID-->&Change_btn=Change&Second let’s take the case where you’re placing a link on the window, not using a Menu.
Then you need to add your own <a> tag, and so it looks something like this;<a href="put the url explained above here">Login</a>Update: Putting the session Id in the URL is considered a "small" security risk. It is considered better to pass the Session ID via a cookie (which NetTalk already does). So it is not necessary, and possibly not desired, to pass the session ID in the URL.
Advanced
What happens if the form is opened in a separate frame? When it comes time to Save or Cancel the form then the page it "returns to" will open in the separate frame. This is usually not what you want if you are calling the form directly. there are 2 possible solutions to this:
- On the Form procedure options you can specify the "URL on Save" and "URL on Cancel". This is fine, as long as the Form will only be called directly (ie not from a Browse).
- An alternative method is where you want the form to behave normally when called from a Browse, but differently when called from a direct link. To do this add a ChainTo parameter to the URL's described above. For example, if you want the frame (that the form appeared in) to return to the IndexPage, when Save (or Cancel) is clicked, then the URL would contain
&ChainTo=IndexPage
For example
MailboxesFormControl?<!-- Net:s:SID--> &MAI__MailBoxNumber=12&Change_btn=Change&ChainTo=IndexPage&
Question:
I get an error when compiling my NetWebHandler procedure. The error says:Syntax error : Unknown procedure labelIn the source the line of the error looks something like this;ProcedureName(Self)Answer:
Go to the procedure properties for the ProcedureName procedure.
One of two possibilities exist.Do a “regenerate all” on the app (Project menu, Generate All option).
- Make sure the Declare Globally option is switched on.
- Make sure the Prototype for the procedure is set to (NetWebServerWorker p_web)
Question:
I want to put in a menu item that links to another site.
Answer:
Simply put in the full URL of the site you want to link to. For example if you were linking to www.capesoft.com then you should put in http://www.capesoft.com (the http:// is not always 100% necessary, but if you're going to an external site then it's a good practice.
Tip: If you want the site to open in a new browser window, then set the target of the URL to '_blank'
Question:
How many simultaneous connections can an EXE made with NetTalk WebServer?
Answer:
This is the wrong question. Because the server handles Requests, not Connections, there's no such thing as "simultaneous connections".
For example: Take a case where 10 users ask for a page over a 10 second period, and all of them look at the page for 20 seconds, then ask again, ask for another page. In this case there are no "simultaneous connections. The users feel like they're connected to the server for those 20 seconds, but in reality they were connected to the server for a fraction of a second. Then later on they connected again (briefly) to get a second page.
The correct question therefore is "How many page requests can be handled". I'm glad you asked. Read on.
Question:
How many page requests can be handled
Answer:
This is like going into a tire shop and asking the guy "If I put those tires on my car how fast will it go?" To answer that question the dealer needs to know something about your car. Putting 8 inch retreads on a Ferrari will matter. But putting 30 inch low-profiles on your Datsun isn't going to make it go any quicker.
In the same way, when considering performance, there are a number of factors that have to be taken into consideration.
- bandwidth
- complexity of the pages being served
- size of the pages being served (relates to bandwidth as well)
- speed of the database (and all that that entails)
- speed of the computer (cpu) which is doing the serving
and so on.
But what's the ballpark? Ok well on my machine (AMD 2600+) I ran various examples. On another machine on the LAN I ran a performance test utility. During the test my CPU went to 100%, which is good, it means we're testing the Server program. I tried with browse pages (16 records), Menus, Forms and so on. In all the tests I ran performance was in the 110-120 pages per second bracket.
So let's say you had 100 users, all getting a page every single second. Well it would cope with that comfortably.
This is with the class still in the beta stage, and running unoptimized code. We expect performance in the future to get even quicker than this.
Question:
I've set my Browse Filter to 'wmu:cust_no=loc:cust_no' but it doesn't work.Answer:
If you do the filter like this :
Because the whole browse procedure runs through once, then disappears, the value of loc:cust_no won't change. Thus it does not need to be a variable in the browse expression, but can rather be a constant.
'wmu:cust_no=loc:cust_no'
then loc:cust_no would need to be BINDed (_after_ the PUSHBIND).
however the filter could better be written as
'wmu:cust_no=' & loc:cust_no (if loc:cust_no is a LONG)
or
'wmu:cust_no=''' & clip(loc:cust_no) & '''' (if loc:cust_no is a STRING)
or
'UPPER(wmu:cust_no)=UPPER(''' & clip(loc:cust_no) & ''')' (if loc:cust_no is a case insensitive STRING)
and none of these would require binding.
Question:
After adding a new procedure to my app, I get a 'Page Not Available' or 'Page cannot be found' Error .
Answer:
You need to do a Generate All (Project Menu) after adding a new procedure.
Question:
How do I filter a browse control that I've whacked onto a form, to the form's tables' primary key.. You know the age old Invoice Ref 1234, and related Line items thing.
Answer:
Let's start with how you don't do it - you don't do it using the traditional "prototype" approach. That's cause ... well never mind, you just don't.
what you use is the "value queue".
So in one procedure (the form, assuming it comes first) you have
p_web.SetValue('something','whatever')
And then in the browse, you can test for it, and use it.
if p_web.IfExistsValue('something')
thisview{prop:filter} = p_web.GetValue('something')
End
[Aside - in this case you don't really need to do the IfExistsValue part, since non-existent values would return a blank, but I thought I'd put it in so you can see that as well. ]
the name of the variable ('something') is like any variable name. You can call it anything you like, but it should be unique <g>.
TIP: Make sure the browse is _file loaded_. If it's page loaded then things would get seriously interesting <g>...(and I suspect it wouldn't work so hot when the person did a "next")
Question:
I have a procedure, that generates a file. I want the file to be sent to the browser. I want this file to be the "whole page" sent to the browser.
Answer:
Remember all a web server does is send files to a browser. So this is a really simple thing to accomplish. But there are some gotchas you need to consider:
- The file needs to be in a format that the Browser can display. So sending an HTM or PDF file to the browser is no problem, but sending say a TPS file to a browser isn't going to give you much joy.
- The user has to ask for the file. This is usually done via either a Link, or a Button. The URL you use would be the name of the procedure that generates the file.
- You may, or may not, want to display the file in the current browser window. If you want a new window to open remember to set the target of the URL to '_blank' .
- Remember, for security reasons the Browser cannot serve files unless they are in the Web folder (or a subfolder of Web). So your procedure needs to generate the file into the correct place.
The code you need to add is simple.
- Firstly, you want to pass in the Web Handler to the procedure. So add
NetWebServerWorker p_web
as a parameter to the procedure. If you want to call this procedure from other places in your code then you can make the parameter optional if you like. ie
<NetWebServerWorker p_web>- Let the procedure generate the file as normal. Avoid displaying a window if possible (use window{prop:hide} = 1 to hide the window) and make 100% certain that no user attention is required when running the procedure. Remember this procedure will run on the Server, so the user will not see it.
- After the file has been created, send it to the browser using the following code:
If Not p_Web &= NULL
p_web._Sendfile(clip(p_web.RequestData.WebServer.WebFolderPath) & '\' & filename)
End- You may also want to REMOVE the file at the end of the procedure if it does not need to be stored.
Question:
I'm planning an app using the NetTalk web server, and I need to record the visitor's IP address. Can you tell me where I might be able to locate the client's IP?
Answer:
The incoming IP number is stored in the header of the incoming GET or POST.
It is easily accessible from anywhere in your app (that's processing the page request)
It's stored in
p_web.RequestData.FromIP
Question:
I need to be able to pass the current Session ID from a link in a static page. How do I know what the current session ID is set to?
Answer:
First add the following to the top (ie the first line) of the static page:
<!-- NetWebServer -->
This tells the engine to parse the page looking for tags.
Then in the place where you have a URL you add a SID tag. Like this:
<a href="wherever.htm<!-- Net:s:SID-->">Switch to Browse Roster</a>
That takes care of regular links. For <form> sections (which will ultimately result in a POST when the user presses a button) you do the following just inside the <form>:<!-- Net:s:SessionID -->
For example:
<form>
<!-- Net:s:SessionID>
<input type="button" value="Button" name="B3">
</form>
Question:
What is the trick to filling other fields after a lookup on a form. The prompts only allow for just returning information on 1 field from the lookup.
Answer:
When returning from a lookup, the Form is regenerated. So the best place to embed the additional code is in the form, at the top of the GenerateForm routine. You wrap your additional code in a detection of the Select button having been pressed and also the file that you were doing the lookup on. As well as setting the file field you must also set the Values. For example:
If p_web.IfExistsValue('Select_btn')
case upper(p_web.getvalue('lookupfile'))
of 'MAILBOXES'
ALI:Picture = MAI:MailBoxPicture
p_web.setvalue('ALI:Picture',MAI:MailBoxPicture)
p_web.setsessionvalue('ALI:Picture',MAI:MailBoxPicture)
End
End
Question:
Does the web server functionality work with a multi-dll app? I've given it a try, and the program always fails to initialize?
Answer:
Yes it does work, see example number 20, MultiDLL.
To use the Web Server in a Multi-DLL situation, follow these steps:
- Open the Data DLL app. Add the NetTalk Global Extension. (Remove the Suppress NetTalk extension if it is already there.) Compile.
- Open the App which will contain the server. Add the NetTalk Global extension, and the Activate Net Web Server Global extension.
- Use the template utility to import the WebServer and WebHandler procedures into the App.
- Add NetWeb procedures as normal. Compile.
- If Step 2 was the Exe app then you're done. If not, open the Exe App. Add a call to the WebServer procedure somewhere, so you can start the web server. Compile. And Run.
Question:
How can my user Logout?
Answer:
In order to logout one of two things needs to happen.
a) If they are inactive for a certain amount of time (NetWebServer.SessionExpiryAfterHS property) then their session is deleted, and hence they are logged out. Or
b) In your code you make a call to p_web.SetSessionLoggedIn(0)Cunningly, if you have the following parameter in your URL then a logout will be done for you
logout_btn=anything
For examples of Logout techniques see
- Uses an HTML window to confirm if the user wants to Logout. (LogoutForm procedure, note logout code embedded in this procedure).
- Uses a pop-up message to confirm if the user wants to Logout. (PageHeaderTag procedure, Logout(js) menu item, the URL is
'IndexPage?Logout_btn=yes'
but notice the onClick code on the URL Options button
'return chooseAction(''Do you want to Logout?'')'
This chooseAction line is the bit that makes the popup window appear on the screen. The URL is only invoked if the user selects Yes. Example 4
Uses a logout link to make the logout happen
Uses a logout URL to make the logout happen
Logout options are included in the MenuOnLeft procedure. Note that the two logout options here are NOT in the menu itself. You'll find them in the HTML tab under the NetWebPage Settings button.
- The clickable link URL looks like this
<a href="FramePage<!-- Net:s:SID -->&Logout_btn=yes" target="_top">LogOut</a>
- And there is also a button, wrapped in a small <form> that looks like this
<form action="FramePage" method="post" name="Form_frm" id="Form_frm" target="_top">
<!-- Net:s:SessionID-->
<input type="submit" value="Logout" name="logout_btn" class="mainbutton">
</Form>
Question:
How do I refresh the page in another Frame when this frame loads?
Answer:
When you are sending a page to a browser you can embed JavaScript in the page.
It doesn't matter too much where the Script is, but I usually put it at the bottom of the page, after the </form> statement.
The code to for a frame to refresh looks like this<script> top.frames.top_fram.location.reload(true) </script>
Where top_fram is the name of the frame you want to refresh.
Here's an example that conditionally loads the top frame, or the left frame, depending on the existence of a local value.
if p_web.IfExistsValue('loc:no')
packet = clip(packet) & '<script> top.frames.top_fram.location.reload(true) </script>'
packet = clip(packet) & '<script> top.frames.left_fram.location.reload(true) </script>'
end
Question:
How do I create my own styles?
Answer:
The first thing you'll want to do is to create your own style file. This will allow you to make your own styles, without your styles being overwritten by the shipping NetTalk files.
- Make a new text file in the \web\styles folder. You can call it anything you like, something like custom.css is a good name.
- The syntax for a style is
.rightjustify{
text-align: right;
}
where the attributes for the style are inside the { } and the name is before it (prepended with a . )
Have a look through the netweb.css file to find styles to use as starting points.
Remember each style needs a unique name.- In your web server app, add the Style file to the list of styles used by the site. This is done on the webServer procedure, NetTalk Extension, Settings Tab, Styles Tab.
- Change the item you want to use the new style.
Question:
In many of the URL's there is a double underscore. For example see question W4 above. What is this?
Answer:
Basic HTML does not allow for a colon (:) to be used in a field name. As a simple workaround NetTalk uses a double underscore in place of a colon. This is translated automatically for you where appropriate. However if you are coding a URL yourself (like the W4 example above) then you need to use a double underscore in place of a colon. For example fil:key becomes fil__key.
If you have an underscore in the name already then you'll need to add that in as well, potentially getting 3 underscores. eg fil:_key becomes fil___key.
If you have a double underscore in the name already then let me know. You're going to have a problem.
Question:
I have an Invoice line item, and I do a lookup on the product code. I'd like the Price from the products file to be entered in the Price field at the same time. (The user can then override the suggested price if necessary.)
Answer:
The easiest place to add extra assignments is by adding them under the More Assignments button. A good example of this is in Example 21. (BrowseInForm). The UpdateLineItems procedure does a lookup on the BrowseProducts procedure.
If you want to do more complicated things, then there is also an Embed point you can use, called AfterLookup.
Tip: Remember when embedding at this point in the code, you are working on the SessionQueue, not the actual file field. So If you want to set LIN:Price = PRO:RRP then the code needs to look like this:
p_web.SetSessionValue('Lin:Price',PRO:RRP)
Question:
How do I do Reports in a Web app?
Answer:
Read through the instructions in the NetTalk Docs here. These explain how to convert existing Clarion Reports into Web Reports.
Remember that all NetTalk has to do is convert your existing report output to PDF. So all the normal report questions (how to filter etc) are answered in exactly the same way you would for a normal Clarion report.
If you need to access settings, or parameters, set in the web interface remember that you do have access to the session queue when the report is running in web mode. You can access the queue using the normal syntax. TIP: Since the report can run in normal mode, and web mode, it's a good idea to check if the report is running in web mode, before accessing the session queue. This is done by testing the value of p_Web. For example
If Not p_Web &= Null
! we're in web mode
End
Question:
Once you've written a NetTalk server, how, and where, can you host it?
Answer:
There is a separate document discussing deployment included with the NetTalk documentation.
Question:
The default error page is great, but I want to make my own.
Answer:
There is a style file called Error.Css which contains the style used by the errors page. Editing this CSS file is the first option for changing the layout, and style, of the errors page.
If you wish to change the text of the errors page, then override the MakeErrorPage method in the WebServer procedure. The text returned by this procedure contains the HTML sent back to the browser. See example 32.
Tip: Resist the urge to place the name of the missing file in the error message. This can lead to a security problem known as cross-site-scripting.
There're 2 things you need to do:
1: To override the NetTalk4 file update, find the ValidateRecord routine (this will override insert, change and deletes) and put the following code in:
ans = 0
2: You'll need to place the calls to your own filechange method in the ValidateInsert, ValidateUpdate and ValidateDelete methods.
You can add the parameters to the menu URL using the ?a=b&c=d syntax. For example
'BrowseCustomers?Current=1&Paidup=1'The first parameter (after the page name) starts with a ? and after that you can have as many parameters as you like separated by a &.
Then in the Browse you need to store this parameter in a session variable so that the browse remembers it. Remember the browse procedure itself can get called many times in the life of the browse, so you can't rely on the parameter always being there. To store the parameter for later add the following to the top of the GenerateBrowse routine
If p_web.IfExistsValue('Current')
p_web.StoreValue('Current')
EndThen in the browse make use of the SessionValue called Current. For example
'cus:current = ' & p_web.GetSessionValue('current')
It's good for web pages to have a Title. But it's a pain to have to fill in
the Title field for each browse and form. So starting with version 4.31, if
the Title is blank, then the page "Header" setting will be copied into the
title field for you. Alas, this can cause a problem if your Header is not a
constant string, but is a variable. The Title field is generated into the
WebHandler procedure, and if you have used an expression, such as a session
value, or local variable, then you'll get a compile error when compiling the
Web Handler procedure.
Solution 1: For the offending procedures, explicitly set the title different
to the header. Set the title to a constant value, not using a local
variable. At the very worst set it to '' (quote quote).
Solution 2: If your NetTalk Web Handler object (i.e. the NetTalk extension to
your WebHandler procedure, has the This Object Name set to p_web rather than
ThisNetWorker, then you can still use Session Variables in the title.
This is the NetTalk extension in the WebHandler Procedure