Enhancing CGIDEV2
November 4, 2009 Paul Tuohy
Maybe “enhancing” is too strong a word, but “Add-Ons for CGIDEV2” doesn’t have the same ring to it. I have always been (and still am) a big fan of CGIDEV2. A tool, such as CGIDDEV2, must be flexible enough to cater for all tastes. But when you have nailed down your own standards and working methods it is sometimes easier to have add-on procedures to implement those standards and working methods. With that in mind, I want to share a few of the subprocedures I have written that make CGIDEV2 a little easier to use in my development environment. All of these subprocedures are coded in a single module: UTILCGI. Although this article deals specifically with CGIDEV2, it also contains examples of how you can wrap functionality around existing subprocedures. Loading HTML Documents When it comes to the HTML documents that I use in a Web application, there are a number of points worth noting:
CGIDEV2 provides two main subprocedures for loading HTML documents: getHTMLIFS() and getHTMLIFSMult(). The getHTMLIFSMult() subprocedure allows an easy means of loading multiple HTML documents to be processed as one. This is the equivalent of having copy members in DDS. Example Code 1 below shows the content of the template document StdMaintHead.html. This document contains a standard header that is common to all Web pages created interactively by an RPG program in my application. The main points to note are:
Code 1: The StdMaintHead.html template document <!-- $top$ --> Content-type: text/html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>My Application</title> <script language="javascript" src="https://itjungle.wpenginepowered.com/js/entryRoutines.js" type="text/javascript" /> <link rel="stylesheet" href="https://itjungle.wpenginepowered.com/css/main.css" type="text/css" > <!-- $top2$ --> </head> <body"> <div id="container"> <div class="logo">My Logo </div> <div class="pageHeading"> My Common Headings </div> Code 2 shows the content of the template document StdMaintFoot.html. This document simply contains a section that completes the Web page. Code 2: The StdMaintFoot.html template document <!-- $end$ --> </div> </body> </html> Code 3 shows the content of the template document StdButtons.html. This document contains sections for each permutation and combination of submit buttons used throughout an application. Code 3: The StdButtons.html template document <!-- $changebutton$ --> <div class="buttons"> <input type="submit" name="cgioption" id="CheckButton" value="Change"> <input type="submit" name="cgioption" value="Delete" onClick= "return confirmDelete()"> <input type="submit" name="cgioption" value="Cancel"> </div> <!-- $addbutton$ --> <div class="buttons"> <input type="submit" name="cgioption" id="CheckButton" value="Add"> <input type="submit" name="cgioption" value="Cancel"> </div> </pre> <!-- $changeCancelButton$ --> <div class="buttons"> <input type="submit" name="cgioption" id="CheckButton" value="Change"> <input type="submit" name="cgioption" value="Cancel"> </div> Finally, Code 4 shows the content of the template document StdCGI.html. This is a very simple template containing a standard section name and variable name that is used in the generation of standard drop-down boxes, radio buttons, and check boxes. But more about that later in the article. Code 4: The StdCGI.html template document <!-- $SIDCGISection$ --> <!-- %SIDCGIVar% --> Assuming that all of these templates are stored in a directory named MyTemplates in the IFS, I will have a 512-byte data area named CGIROOT, in the library containing my CGIDEV2 programs, with a value of ‘/MyTemplates/’. I have wrapped the getHTMLIFSMult() subprocedure in another subprocedure, loadHTMLDocs(), which allows for less coding required in the template document being processed. Code 5 shows the relevant portion of the global D specs in UTILCGI along with the subprocedure loadHTMLDocs(). The main points to note are:
Code 5: The subprocedure loadHTMLDocs() D CGIRoot s 512a DtaAra(CGIROOT) D CGIRootDone s n D thisRoot s 512a Varying P loadHTMLDocs B Export D PI D doc01 50a Varying Const D doc02 50a Varying Const D Options(*NoPass) D doc03 50a Varying Const D Options(*NoPass) D doc04 50a Varying Const D Options(*NoPass) D doc05 50a Varying Const D Options(*NoPass) D HTMLDocs s 32767a Varying Static D HTMLDocsIn s 1000a Varying /free if not CGIRootDone; in(E) CGIRoot; thisRoot = %Trim(CGIRoot); HTMLDocs = thisRoot + 'StdMaintHead.html ' + thisRoot + 'StdButtons.html ' + thisRoot + 'stdCGI.html ' + thisRoot + 'StdMaintFoot.html '; CGIRootDone = *On; endIf; HTMLDOcsIn = thisRoot + doc01; if %Parms()> 1; HTMLDOcsIn = HTMLDOcsIn + thisRoot + doc02 + ' '; endIf; if %Parms()> 2; HTMLDOcsIn = HTMLDOcsIn + thisRoot + doc03 + ' '; endIf; if %Parms()> 3; HTMLDOcsIn = HTMLDOcsIn + thisRoot + doc04 + ' '; endIf; if %Parms()> 4; HTMLDOcsIn = HTMLDOcsIn + thisRoot + doc05 + ' '; endIf; getHTMLIFSMult(HTMLDocs + HTMLDocsIn : '<!-- $':'$ -->':'<!-- %':'% -->'); return; /end-Free P E The existing ability in CGIDEV2 to load multiple documents eases the requirement to repeat standard definitions in multiple HTML documents. Encapsulating the loading of these standard documents, along with the processing of an externally defined template directory, further eases the processing requirements in a CGI program. Selection Lists, Radio Buttons, and Check Boxes Some of the other items that can be tricky to handle with CGIDEV2 are selection lists, radio buttons, and check boxes. Of course CGIDEV2 has the crtTagOpt() subprocedure, which makes the generation of selection lists much easier and which I make use of in the following subprocedures: setCGIMultiple(), writeCGIMultiple(), and endCGIMultiple(). Let’s have a look at these subprocedures at work. These subprocedures make use of the standard document shown back in Code 4. Code 6 shows the relevant portion of a programs template document and Code 7 shows the corresponding program code that uses the template. Code 6: Template code <span class="entryQuestion">In List? </span> <!-- $afterInList$ --> </p> <p> <span class="entryQuestion">Check These</span> <!-- $afterCheckThese$ --> </p> <p> <span class="entryQuestion">Size</span> <!-- $afterSize$ --> </p> Code 7: Corresponding program code for Template code in Code 6 setCGIMultiple('R': 'InList' : inList : *Omit : *Omit : 2); writeCGIMultiple('Y' : 'Yes'); writeCGIMultiple('N' : 'No'); wrtsection('afterInList'); setCGIMultiple('C': 'CheckThis'); writeCGIMultiple('W' : 'Watch' : checkW); writeCGIMultiple('L' : 'Wallet' : checkL); writeCGIMultiple('G' : 'Glasses' : checkG); wrtsection('afterCheckThese'); setCGIMultiple('S': 'Size' : size); writeCGIMultiple('S' : 'Small'); writeCGIMultiple('M' : 'Medium'); writeCGIMultiple('L' : 'Large'); endCGIMultiple(); wrtsection('afterAllowRpt'); Assuming that program fields have the following values, inList=’Y’, checkW=’W’, checkL=’ ‘, checkG=’G’, and size=’M’, the above bit of jiggery-pokery will result in the html shown in Code 8. Code 8: HTML generated from code in Figure 6 and Figure 7 < span class="entryQuestion">In List?</span> <input type="radio" name="InList" class="entry" value="Y" checked> Yes <input type="radio" name="InList" class="entry" value="N" > No </p> <p> <span class="entryQuestion">Check These</span> <input type="checkbox" name="CheckThis" class="entry" value="W" checked> Watch <br /> <input type="checkbox" name="CheckThis" class="entry" value="L" > Wallet <br /> <input type="checkbox" name="CheckThis" class="entry" value="G" checked> Glasses </p> <p> <span class="entryQuestion">Size</span> <select name="size" class="entry" > <option value="S">Small</option> <option value="M" SELECTED>Medium</option> <option value="L">Large</option> </selected> </p> I know that one of the major benefits of CGIDEV2 is that HTML is externally defined in a template and that these subprocedures are diverting from that by generating the HTML internally. But remember that the generation of the HTML is to a standard and it is still external to the program that is generating the page. The subprocedures act as follows:
Code 9 shows the base data structure and work fields (defined in the global D specs in UTILCGI) used by the subprocedures. Code 9: Work fields used by the subprocedures D base Ds Qualified Inz D type 1a D currValue 50a Varying D tabIndex 10i 0 D accross 10i 0 Inz(1) D i 10i 0 D data s 1000a Varying D dataOut s 1000a Varying D VARNAME C 'SIDCGIVar' D SECTIONNAME C 'SIDCGISection' Let’s have a look at the subprocedures, starting with setCGIMultiple(), shown in Code 10. The main points to note are:
Code 10: The setCGIMultiple() subprocedure p setCGIMultiple B Export D PI D type 1a Const D variable 50a Varying Const D currValue 50a Varying Const D Options(*NoPass : *Omit) D tabIndex 10i 0 Const Options(*NoPass : *Omit) D class 50a Varying Const D Options(*NoPass : *Omit) D accross 10i 0 Const Options(*NoPass : *Omit) D other 500a Varying Const Options(*NoPass) D classDft s 50a Varying D Inz('class="entry" ') /free reset Base; base.type = type; select; when base.type = 'S'; data = '<select '; when base.type = 'R'; data = '<input type="radio" '; when base.type = 'C'; data = '<input type="checkbox" '; other; return; endSl; data = data + 'name="' + %trim(Variable) + '" '; if %Parms() > 2; if (%Addr(currValue) <> *null); base.currValue = %Trim(currValue); endIf; endIf; if %Parms() > 3; if (%Addr(tabIndex) <> *null); base.tabIndex = tabIndex; data = data+'tabIndex="' + %char(tabIndex) + '" '; endIf; endIf; if (%Parms() > 4); if (%Addr(class) <> *null); classDft = %Trim(class); endIf; endIf; data = data + classDft; if %Parms() > 5; if (%Addr(Accross) <> *null); base.accross = accross; endIf; endIf; if (%Parms() > 6); data = data + %Trim(other); endIf; if base.type = 'S'; data = data + '>'; updHTMLVar(VARNAME : data); wrtSection(SECTIONNAME); else; data = data + 'value="%vl%" %ch%>'; endIf; return; /end-Free P E Code 11 shows the writeCGIMultiple() subprocedure. The main points to note are:
Code 11: The writeCGIMultiple() subprocedure P writeCGIMultiple... P B Export D PI D value 50a Varying Const D desc 100a Varying Const D currValue 50a Varying Const Options(*Nopass) D workValue s Like(Value) D workDesc s Like(Desc) D workCurrValue s Like(currValue) /free workValue = %trim(value); workDesc = %trim(desc); if (base.type = 'S'); updHTMLvar(VARNAME:CrtTagopt(workValue : workDesc : base.currValue)); wrtsection(SECTIONNAME); else; base.i += 1; if (base.i > 1); if %Rem( base.i - 1 : base.accross) = 0; updHTMLVar(VARNAME : '<br/>'); else; updHTMLVar(VARNAME : ' '); endIf; wrtSection(SECTIONNAME); endIf; dataOut = %Replace(workValue:data:%Scan('%vl%':data):4); workCurrValue = base.currValue; if (base.type = 'C' and %Parms() > 2); workCurrValue = currValue; endIf; if (workValue = workCurrValue); dataOut = %Replace('checked':dataOut:%Scan('%ch%':dataOut):4); else; dataOut = %Replace(' ':dataOut:%Scan('%ch%':dataOut):4); EndIf; dataOut = dataOut + ' ' + workDesc; updHTMLVar(VARNAME : dataOut); wrtSection(SECTIONNAME); endIf; /end-Free P E Finally, Code 12 shows the endCGIMultiple() subprocedure. This subprocedure only needs to be called to complete a selection list (but no harm is done if called for any of the other elements). The subprocedure simply writes the required ending </select> element. Code 12: The endCGIMultiple() subprocedure P endCGIMultiple B Export D PI /free if (base.Type = 'S'); updHTMLVar(VARNAME : '</select>'); wrtSection(SECTIONNAME); endIf; /end-Free P E There You Have It Hopefully this has gotten your creative juices floating and, even if the code is of no direct or immediate use, it might give you some idea of how you can go about adding functionality to existing subprocedures without having to re-write or change them. If you like this approach to CGIDEV2, you might want to go the full hog and have a look at Rennaisance, which is an open source (i.e., free) framework built around CGIDEV2. Paul Tuohy is CEO of ComCon, an iSeries consulting company, and is one of the co-founders of System i Developer, which hosts the RPG & DB2 Summit conferences. He is an award-winning speaker who also speaks regularly at COMMON conferences, and is the author of “Re-engineering RPG Legacy Applications,” “The Programmers Guide to iSeries Navigator,” and the self-study course called “iSeries Navigator for Programmers.” Send your questions or comments for Paul to Ted Holt via the IT Jungle Contact page.
|