S.H. Parker (c) 1999
Before addressing TopSpeed's claims for CWIC (pronounced "quick" I am told by an extremely authoritative source), you will notice that I will not talk about "converting" my applications (actually, I used that term once and was roundly thrashed by the aforementioned authoritative source). The only converting is in the upgrade to 2003, C4b or C5EE beta 2, if you are not already running it and if you call that a "conversion." As was pointed out to me, quite correctly, you add an extension template, ensure your target is a 32 bit executable and compile. Nothing about the app is changed. Indeed running a CWIC extended app directly on a PC gives absolutely no clue whatsoever about its alternate persona. So, the correct terminology is to "web-enable" a CW app. This process creates what I call a "dual-mode" app, which TopSpeed refers to as a "hybrid" application.
Any .App?
The first of TopSpeed's claims is that you can take existing applications, add the CWIC extension template and create Web-enabled apps.
Four of the six applications (originally) accessible at our site (http://www.par2.com) were taken from existing CW apps. Three are written for JOBTRAK Corporation (the college job listing service) and one is the "library" program I use to keep track of articles on Clarion programming. The fifth? I wanted to see if C3 Development's Browse Form Template (full record browsing) would support CWIC. Though I already knew Boxsoft's tagging worked (I use it in one of the apps; .LIBs, not .DLLs), third party template support has not been and, for obvious reasons, cannot be guaranteed by TopSpeed (by the way, the template does work, as did several functions in my personal library). The sixth, the guest book, is the only application written specifically for the Web site.
In light of this, it is safe to say that TopSpeed's claim that you can Web-enable existing applications is true. In fact, even the guest book was written and debugged in 2003 (and has been simply recompiled in each new Clarion release since) before having the extension template added to Web-enable it.
Hybrid apps?
The next of TopSpeed's claims is that web-enabled applications are hybrid, dual-mode apps. That is, they will run both on the Web and on local PCs/networks, saving developers the need to maintain multiple applications, frequently in multiple languages/environments to satisfy a variety of needs. (Even the "editor's choice" products do not do this.)
Do the same executables run on a PC/network and the Web? Yup, they sure do.
One Step?
This is probably the most important of TopSpeed's claims for the product.
The applications presented on my site were created in various releases of Clarion for Windows (one's genealogy even goes back to CPD2.0, circa 1991). After adding the extension template and recompiling in CW2003, I fired up my browser and the developer's copy of the Clarion Application Broker. The apps ran on the Web.
So, web-enabling a CW applications can be a one-step process.
That said, however, there is more to the story.
The applications did not behave the same on the Web as they did when run on my PC. In fact my initial impression, before analyzing what was happening, was that they ran quite bizarrely.
For example, data validation did not seem to be working. Lookups did not occur when they should have. Some occurred after I pressed the Ok button, others did not occur at all though they should have. Runtime field computations did not display.
Thus, considerable custom code did not appear to execute and entire functionalities simply seemed to disappear. With further experimentation, I discovered that this code actually was executing, it just did not execute when I expected it to.
To my mind, code not executing when I expect it to is worse than code that does not execute properly or at all. Code that fails, I can debug. But if the code is executing at a different time, all my debugging efforts are wasted.
Without re-creating the angst of discovering why this was so, the difference between running an app on a PC/network and on the Web turns on "the nature of the (Internet) beast."
In hindsight I realize that I had forgotten what the Internet was about. It is imperative to remember that the purpose of the Internet is to provide platform independent document publishing. When describing database applications, we often use the metaphor of file cabinets. This metaphor is utterly inappropriate to the Web. A much more accurate metaphor is a booklet.
The host (server) delivers pages (sections of the booklet). These pages are static; this is essential to what makes them viewable on any kind of client. But, as soon as you add embedded source code, you create a need for dynamic printing of the page. Well, printing doesn't work that way; never has, never will. Indeed, one of the main themes in leading edge implementations of HTML, browsers and servers is to make pages appear more dynamic, particularly through the inclusion of things like Javascript and Java (which allows the client to "change" the page dynamically, but current implementations are inconsistent).
If, for example, you add data validation code to a control's Event:Accepted, as a Clarion developer you expect it to execute when the user completes the field because Accept() generates an event when a control is completed. Web pages do not behave in this manner; events are not posted on a field-by-field basis. The server has "printed" and delivered the page and that is that.
In web-enabled CW applications, while running on the Web, Accept() completes only when a button is pressed. At that time, your embedded code, all of it, executes. That is, web-enabled applications behave as if the Accept loop cycles only once, when Ok or Cancel is pressed.
In fact, this is exactly what happens. When the user presses Ok, the page contents are returned to the server and execution of your program continues, on the server (in CWIC, this is what allows maintenance of state; using standard web servers, the server does whatever its script requires and waits for the next request). This is a fundamental difference from the way we are used to thinking. On a network, all program execution is on the client. Even in client/server, most execution is local (the notable exception being retrieval of the required records and RI checks). On the Internet, the client is little more than the I/O device and most code executes on the server.
The single cycling of Accept explains why some of my lookups did not execute. They were all hand coded and, because I did not want them executing during Non-Stop mode running on a network, I had wrapped them in
If ~{Prop:AcceptAll}
!lookup code
End
When the Ok button is pressed Prop:AcceptAll is True. Even though the fields had bad or no values, the condition failed and the code did exactly what it was supposed to do: nothing.
Web pages, in general, only post events on two pre-defined methods, Get or Post, and two types of action (Submit or Reset -- the actual "action" is, usually, a script), which are associated with buttons on HTML forms. This is part of the HTML spec. That is the nature of the beast.
Enabling a CW app and expecting your event-dependent code to execute is like trying to do filing but only having a text editor to do it with.
Because my apps make extensive use of embedded source and Source Template procedures, I faced significant programming issues. It was not a matter of getting my apps working. They worked. Neither was it a matter of ensuring that everything that needed to happen, happened. It did. It was a matter of things happening in a way that makes sense to the user (including me); that is, of having things happen in the manner of my choosing.
One of the great myths of Windows programs is that "the user is in control." If you buy into this, you are in deep trouble. The user has only as much control as I allow her or him to have, no more.
For example, a lookup occurring after pressing OK makes no sense to me. So, while I left the data validation code in place, I also added buttons next to the fields. In the buttons I embedded a call to the lookup procedure as well as code to assign the retrieved value to the target control.
It is my normal practice to provide both forms of validation: once on the control's Actions tab and once on a button next to the field. This gives users two ways of getting a good value into the field. Fortuitously (for my learning curve, though not my nerves), this was one of the rare cases I had not done so.
While this makes my app look more like standard Web apps (and I do not think this a particularly good thing), it does have the virtue of being web-standard. A Web user can be expected to understand it on first sight. And that means that s/he knows what to do with the button. In addition, it makes any subsequent lookup comprehensible, should it appear. If the user did not avail her-/himself of the button and entered a bad (or no) value, the look up will appear (remember, I did not remove the original data validation code). But this time, the user should understand why the look up appears when they thought they were done.
Since this button is appropriate only when running in web-mode, I can prevent it from appearing when the app is not running on the Web. I use the Active method of the Broker object in the After Opening Window embed:
If ~(WebServerActive) Hide(?LookupButton) End
Similarly, if there is code I want to execute only when running on the Web:
If WebServerActive Enable(?Find2) End
ensures that the control referred to will be on the page.
Rethinking is Required
So, data validation was working, but not when I expected it to. That is why I missed it (that, and not reading the documentation closely enough). Thankfully an easy solution presented itself.
Other cases were not so easy.
I mentioned an application to inventory Clarion articles. When I enter the volume and issue, the app calculates the month and moves the cursor within the control for entry of the year. This will not happen when running on the Web until a button is pressed. This becomes a case where I will not want the code to execute and I wrap it in If ~WebServerActive ... End. Yes, I lose the functionality, but what can you do? (Why must I lose this functionality?-Because the calculation of the month and placement of the cursor will happen only after I press a button, i.e., after I have already entered the data!)
So, when coding for the Web it is important to code around the fact that the Accept loop does not cycle with each control. This means that we have to review each control and decide (and even with the most careful consideration you will occasionally discover -- the hard way) which code is appropriate for the Web and which is not, which executes as expected and which does not.
In short, you need to move, disable or provide an alternative for embedded code on these criteria:
- Can the code safely be moved into the OK Accepted event for both operating modes?-Is execution on page Accept (sorry) acceptable?
- Should the code not be allowed to execute when on the Web?
- Should the code be allowed to execute only when on the Web?
- Must the code execute in either mode (when the Ok button is not Ok)?
For myself, I ended up minimizing my reliance on embedded code on the Web (even though you can set a property for a control to refresh on completion) and this meant a significant rethinking of how the program should behave when run on the Web. I think this is the key to successful Web applications: the Web behaves differently, so my apps need to behave differently.
Code that must execute
This is really the only difficult case: because it must execute, but if it depends on an event, as it usually does, it will not. So, ways must be found to force execution.
Lookup, locator and required field checking code must, in my opinion, execute regardless of operating mode. Lookups and locators cannot reasonably be moved to the end of the Accept loop while required field checking is inherently at the end of Accept.
Lookups, as mentioned, were fairly neatly handled with a button. Locators, it turns out, can be handled in a similar manner. I copied the standard lookup code from the generated source to the button's Event:Accepted and it functions just like any other Web Find or Search button.
Required fields were a bit more interesting. The broker will return the page if you do required field checking and one is blank. But, because the browser has control of the cursor, the empty field will not be selected. This, I think, is unnecessarily confusing (if not to end users, it certainly was to me).
A number of solutions are possible (remember, no change is necessary for PC mode). You could, for example, present a message to the user with the names of the fields, calling this code only when WebServerActive. Most web applications I have seen do just this.
Or, you could have a series of additional forms (one for each field), conditionally called when the broker is active and the field is empty. In other words, there is no easy resolution.
What finally struck me was a default behavior of how CWIC-enabled applications format pages: two of the extension's options can be combined to dynamically hide disabled controls. Given that the page is going to be regenerated, why not just disable controls that already have contents? That would cause them to be hidden and leave only the empty fields for display to the user. It also means a smaller page which, in turn, means faster page generation which, in turn, means faster return of the page and less traffic down the wire (ain't it great when a plan comes together?).
What I did was to simply disable all non-required fields and any required field that had a value. So, when the page was re-presented, only the (required) fields without values were on the page. While I was at it, I re-set the title so the user would know why the page was being presented:
If Incomplete()
If WebServerActive
Disable(?pre:not_required_field:Prompt,?pre:not_required_field)
QuickWindow{Prop:Text} = 'Please complete required fields'
If pre:field <> ''
Disable(?pre:field:Prompt,?pre:field)
End
End
Select(Incomplete())
If WebServerActive
ForceRefresh = True
Do RefreshWindow
End
Cycle
End
This code goes in the Ok button's Accepted, before generated code embed. In fact, it is just a modification of my standard required field checking code, as you can see from examining it.
"Simply" -- this involves an awful lot of code. But it is worth it.
Miscellaneous Thoughts
It is important, I believe, to learn at least basic HTML to maximize your effectiveness with CWIC. There are any of a number of good books you can use. I hear there are even sites on the World Wide Web devoted to the subject. You can also capture the source for pages you like and study that (this is my authoritative source's favored method, but I suspect that he already knows a bit more than basic HTML).
Using standard HTML for example, you can provide a standard home page as a gateway into your applications. This is what you will see on my site. This is useful, I think, when you have a number of applications that you want to run on one site but do not wish to call them from a single Frame (i.e., create a new app which contains the procedures of all the other applications).
Because events do not post when running on the Web in the way we are used to them being posted, much of your code will not behave as you expect when in Web mode. Therefore you cannot fully debug an application unless you run it in a browser.
Of course, this also means that wizard-created procedures and procedures without embedded source operate on the Web beautifully and require no special considerations. Similarly, embeds behind buttons also tend to work without modification because buttons, generated as an HTML Submit, post events properly ("properly" meaning in the same way a PC application would).
Lastly, I do believe that it is very important to rethink applications as you web-enable them. In fact, even though there are options to support control events (Partial and Full Refresh in the control's Internet Properties), I am not sure that we want to take full advantage of this.
If the same code could run in both modes, why would we not want to take advantage of this efficiency? Simply, handling events at the control level in the browser may very well entail a performance hit because posting events in the standard ways requires a round trip to the server and a regen of the page's HTML. Are there cases where we will be willing to take this performance hit? Absolutely. Will we want to take this hit for multiple controls in a single procedure? That remains to be seen.