NetTalk WebServer - Frequently Asked Questions
    
      Need Help with Upgrading to NetTalk 5? See 
Upgrading
        To NetTalk 5
      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.
    
      
        - 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 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?
- W12 Sending a file to the browser as a Page
- W13 Client IP Address
- 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.
- W28 Error: "Subject Alternative Name Missing" or
          NET::ERR_CERT_COMMON_NAME_INVALID or "Your connection is not private"
 
    W2: Frame Contents on Startup
    
      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.
     
    W3: Forms
    
      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 to
      
 p_web.GetSessionLoggedIn()=0
     
    W4: How do I call a form directly using a link? Not via
      a Browse.
    
       Example 24 contains two
        examples of this. One via a menu item (direct) and the other as the Save
        URL of a form.
       The URL needs to contain 2
        pieces of information. The Action to be taken, and (for some actions) an
        ID of the record being edited.
       The action is passed as
        one of the following parameters;
      
        ?insert_btn=insert
        ?copy_btn=copy
        ?change_btn=change
        ?view_btn=view
      
        ?delete_btn=delete
      The Copy, Change, View and Delete actions require a unique row ID. This
      can be created using the 
p_web.AddBrowseValue
      method. This method is prototyped as follows;
      
      
 p_web.AddBrowseValue (string proc, string filename,
        key filekey, <field1Value>, <field2Value>,
        <field3Value>, <field4Value>, <field5Value>) 
      
      The row Id is passed as the 
_bidv_ parameter
      to the form.
      
      Examples; 
      
      Calling a form to Insert a new record
      
MailboxesFormControl?Insert_btn=Insert 
      No ID is required because the Insert action does not take an ID.
      
      Calling a memory form 
        MailboxesFormControl 
      Neither an action, nor an ID are required, as Memory forms always use the
      Change action, and there is no ID because there is no Table.
      
      Calling a form, from a URL, to change a specific record;
      
 'SomeForm?change_btn=change&_bidv_='
          &
          p_web.AddBrowseValue('pageheadertag','mailboxes',MAI:PrimaryKey,2)
      
      In the above example 'pageheadertag' is the name of the procedure,
      'mailboxes' is the name of the file, mai:primaryKey is the key, and 2 is
      the value to be used in the key (in other words, mai:MailboxNumber=2).
      
      Note that the value can be set to a couple of possible "special" values.
      If the value is set to
 '_first_' then the
      first record in the table will be edited. If it is set to 
'_last_'
      then the last row in the table will be edited. Note that this requires
      that filekey is a single component key.
      
  
     
    W4 (Old): The following
      answer applied to NetTalk 5.23 and earlier. It is included for comparison
      during the transition to the new system. The approach documented below
      will be decremented in a future build.
    
       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?MAI__MailBoxNumber=12&Change_btn=Change&
       or
      MailboxesFormControl?MAI__MailBoxNumber=12&Delete_btn=Delete&
        
      Here’s an example for
        Insert:
       MailboxesFormControl?Insert_btn=Insert&
      If the form you are calling works on Memory, and not on a file, then 
      the link looks like this;
      
 MailboxesFormControl?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 _ChainToPage_
          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
 &_ChainToPage_=IndexPage
 
 For example
 
 MailboxesFormControl&MAI__MailBoxNumber=12&Change_btn=Change&_ChainToPage_=IndexPage
 
    W5: Errors
     Question:
        
       I get an error when compiling my 
NetWebHandler
      procedure. The error says:
      
 Syntax error : Unknown procedure label
      In 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.
      
        -  Make sure the Declare Globally option is switched
          on.
-  Make sure the Prototype for the procedure is set to (NetWebServerWorker
            p_web)
      Do a “regenerate all” on the app (Project menu, Generate All option).
    
 
    W6: I want a menu item that links to another site.
     Simply put in the full URL of the site you want to
      link to. For example if you were linking to 
capesoft.com
      then you should put in 
https://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'
        W7: How many simultaneous connections can an EXE made
      with NetTalk WebServer?
     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.
    
    W8: How many page requests per second can be handled?
    
      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.
     
    W9: I've set my Browse Filter to
      'wmu:cust_no=loc:cust_no' but it doesn't work.
    
      If you do the filter like this :
        '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.
      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.
    
 
    W12: Sending a File to the browser as a Page
    
      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.site.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.
 
 
    W13: Client IP Address
    
      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
     
    W16: Using the Web Server in a Multi-DLL Application
    
      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.
 
 
    W17: How can my user Logout?
    
      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>
 
 
    W18: How do I refresh the page in another Frame
    
      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
     
    W19: How do I create my own styles?
    
      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. 
 
    W20: What is the __ (double underscore) for?
    
      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.
     
    W21: How do I get additional information back from a
      lookup?
    
      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)
     
    W22: How do I do Reports in a Web App?
    
      There is a separate document discussing reports here.
      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
     
    W23: Where can I host NetTalk Server apps?
    
      There is a  separate document discussing 
          deployment included with the NetTalk documentation.
     
    W24: How can I create my own Errors page?
    
      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. 
     
    W25: In my FormPage, I want to call my own method to
      update a file, how do I prevent NetTalk from updating the tables?
    
      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.
     
    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.
    
      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')
          End
      Then in the browse make use of the SessionValue called Current. For
        example
        
        'cus:current = ' &
          p_web.GetSessionValue('current')
     
     W27: I upgraded to Version 4.31 (or later) and now
      I'm getting compile errors in my WebHandler procedure.
     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
    
 W28: Error: "Subject Alternative Name Missing" or
      NET::ERR_CERT_COMMON_NAME_INVALID or "Your connection is not private"
     For Chrome 58 and later, only the
      subjectAlternativeName extension, not commonName, is used to match the
      domain name and site certificate. The certificate subject alternative name
      can be a domain name or IP address. If the certificate doesn’t have the
      correct subjectAlternativeName extension, users get a
      NET::ERR_CERT_COMMON_NAME_INVALID error letting them know that the
      connection isn’t private. If the certificate is missing a
      subjectAlternativeName extension, users see a warning in the Security
      panel in Chrome DevTools that lets them know the subject alternative name
      is missing. 
      
      If needed, until Chrome 65, you can set the
      EnableCommonNameFallbackForLocalAnchors policy. This lets Chrome use the
      commonName of a certificate to match a hostname if the certificate is
      missing a subjectAlternativeName extension.
      
      For more information see 
        https://support.google.com/chrome/a/answer/7391219?hl=en 
    [End of this document]