Overview of the Technique


Before diving into details, let's get a bird's-eye view of what's involved:

  • One or more templates with editable regions are constructed and stored in a Dreamweaver site.

  • The data that is intended to replace the content in the editable regions is stored in a data source, such as an Access or SQL Server database.

  • A Web application page that extracts the data from the data source and restyles it in a text format is created.

  • A request is sent from a Dreamweaver extension to an application server (either local or remote) for the Web application page.

  • Dreamweaver parses the data and inserts it into the proper places in an instance of a template.

  • The new documentderived from a template and complete with the integrated datais stored as a standard HTML page.

TIP

Here's one thought to keep in mind as you are involved with the data side of the process: The data that is inserted into the page goes in as HTML. Many fields contain straight text that, when replaced in the template, take on the formatting of the surrounding area; you can, however, add your own tagseither HTML or CSS styling. If you are inserting multiple paragraphs, be sure to enclose each with the <p> tags so that they will render properly.


To help you understand the concept more completely, this workflow is diagrammed in Figure 5.1.

Figure 5.1. With the aid of a custom extension, Dreamweaver can combine external data and Dreamweaver templates to generate static pages.

graphics/05fig01.gif

This chapter assumes you know how to create templates in Dreamweaver and have data available in a data source. Before we discuss the development of the Web application page, you need to understand how Dreamweaver makes the connection and what format specifications are involved.

Connecting with the HTTP API

Dreamweaver MX enables the user to easily create Web applications that connect to data sources and populate pages with all manner of data. These Web applications must be processed by a specified application server, whether the application is ASP, ColdFusion, or something else, before the data can be displayed. Even Live Data view, which shows the actual data in the page, is constructed by sending a copy of the page being designed to the application server and then extracting and integrating the data. Rather than taking the Live Data route, which involves a great deal of extremely complex translation, Dreamweaver provides another apparatus for processing a Web application page and extracting the results: the HTTP API.

The HTTP API works, as you might suspect, by sending and receiving data via hypertext transfer protocol, or HTTP. Numerous functions in the HTTP APIseven in allallow extension builders to post form data to a Web server and get responses; all the functionality is documented in Extending Dreamweaver (Help, Extending Dreamweaver) under the Utility APIs section. All of the HTTP API functions are methods of the MMHttp object.

To connect to a Web application server and extract data, only two related MMHttp functions are required: MMHttp.getFile() and MMHttp.getFileCallback() . Both methods are similar, each taking a URL as an argument and returning a server reply. The only difference between the two methods is that getFileCallback() calls a specified function after the data object has been received; the function you use depends on how your extension is constructed.

The getFile() function requires only one argument: the URL of the page to be processed:

 var theFile = MMHttp.getFile("http://localhost/beyond/ch05_ex.asp"); 

Other optional arguments allow the returned file object to be saved under user control. However, in an automated production extension, where many pages are being generated, it's more expedient to save the files programmatically. Therefore, the optional arguments are not explored here.

If you are using the getFileCallback() function, the syntax includes the name of the function to be called:

 var theFile = MMHttp.getFileCallback("convertFile()",  "http://localhost/beyond/ch05_ex.asp"); 

Let's look next at the application page.

Writing the Data Source Page

Exactly how the application page is coded varies greatly according to the server model, the language, and the results desired. The overall structure of the page translates well from one application to the next. You can even use Dreamweaver to handle much of the foundation coding for you.

NOTE

To view the completed code, see Listing 5-1 at the end of this section.


Basically, the application page needs to do four things:

  • Connect to a data source.

  • Extract a recordset.

  • Convert the data to an XML-based file.

  • Return the file to Dreamweaver.

The first two tasks are typical for application development, with or without Dreamweaver. If you're using Dreamweaver, simply create the recordset desired on an otherwise blank, dynamic page. Then, because the page should execute but never display, delete all the standard HTML code, including the <!DOCTYPE> declaration. At this point, if you are working in ASP/VBScript, the page will look like this one:

 <%@LANGUAGE="VBSCRIPT"%>  <!--#include file="../Connections/Extension.asp" -->  <%  Dim rsEx  Dim rsEx_numRows  Set rsEx = Server.CreateObject("ADODB.Recordset")  rsEx.ActiveConnection = MM_Extension_STRING  rsEx.Source = "SELECT * FROM extensions"  rsEx.CursorType = 0  rsEx.CursorLocation = 2  rsEx.LockType = 1  rsEx.Open()  rsEx_numRows = 0  %>  <%  rsEx.Close()  Set rsEx = Nothing  %> 

Because Dreamweaver maintains a powerful Document Object Model (DOM) extensibility feature set, it's easy to extract data from a page if the page is structured as or like XML. Dreamweaver doesn't require a page to be in XML-compliant format to work this way. All that's necessary is a structured document with a clear separation of parameters and their content. Here's a simple example:

 <main>     <item name="dbName">Joseph Lowery</item>     <item name="dbEmail">jlowery@idest.com</item>  </main> 

After a recordset has been established, you can replace the static values (Joseph Lowery and jlowery@idest.com ) with dynamic values, like these in ASP:

 <main>     <item name="dbName">(rsEx.Fields.Item("dbName").Value)</item>     <item name="dbEmail">(rsEx.Fields.Item("dbEmail").Value)</item>  </main> 

The MMHttp.getFile() function retrieves a text-based file, so the static and dynamic values must be concatenated into one long string and returned like this:

graphics/05icon01.gif

graphics/05icon02.gif

Although each application page that is designed to generate data for static pages is different, one trait is virtually certain to be shared: Each page needs to be able to easily move through the recordset, returning one record after another on demand. This is a fairly common task for an experienced Web application developer. Dreamweaver can insert the necessary code for you, although this time with a bit of "sleight of hand." Here's how it's done:

  1. Before the standard HTML code has been removed, drag a field or two from the Bindings panel onto the page.

    This field is really only inserted to indicate the change from one record to the next.

  2. Press Enter (Return) and add a text phrase such as Next Record to the page.

    This is a dummy phrase that will be deleted later.

  3. Select the text phrase and enter a hash mark in the Link field of the Property inspector to create a null link.

  4. With the newly applied link selected, from the Server Behavior panel, add a Move to Next Record server behavior.

    Dreamweaver adds all the necessary code to handle recordset paging.

  5. Select the entire text phrase, including the <a> tag surrounding it, and delete it.

    Dreamweaver alerts you that not all of the server behavior has been removed. In fact, only the smallest portion of the behavior, the event call, has been removed. All of the recordset paging code is still intact and ready for use.

NOTE

The process is slightly different for ColdFusion because you need to add a Move to Next Page server behavior and restrict the number of entries on a page to one.


You can test the effectiveness of Dreamweaver's paging server-side code fairly easily. While in Design view, enter into Live Data View mode to see the results returned for the first record. In the Live Data toolbar field, enter the following:

 offset=1 

After you press Enter or click back into Design view, the Live Data View updates to display the second record. An extension can use this added facility to page through the recordset for each new generated page.

Reading the Results

As noted earlier, the getFile() and getFileCallback() functions return a reply from the server. The reply is either an error codedesignating everything from an unknown file error to a disk full erroror a file URL to the retrieved file. Although these functions can be used to retrieve standard HTML files from the Web, they are much more beneficial for our purposes when they retrieve pages processed by an application server. After processing, they are, like all files viewed in the browser, essentially text files. Unlike a technology such as Flash Remoting, Dreamweaver cannot process data objects such as recordsets directly. However, Dreamweaver is quite capable of handling XML and XML-styled documents.

After you have retrieved the results by using the MMHttp.getFile() function, you need to perform some error checking. Status codes are returned with the function to indicate a successful operationwhere the status code is 200or a problemwhich returns any other number. Code like the following reports a general error if one is encountered , but otherwise allows the operation to proceed:

TIP

You'll find a complete listing of status codes and their meaning in the Extending Dreamweaver document, available under the Help menu.


 var theResult = MMHttp.getFile(theURL);     if (theResult.statusCode != 200) {        return alert('HTTP Error: ' + theResult.statusCode + ' at '        + theURL);     } 

Remember that besides the status code, the getFile() function returns the file URL of where the retrieved file is stored. Next, the file URL is extracted from the result using the .data property:

 var theFile = theResult.data; 

Unless specified, this file is stored in the Dreamweaver MX/Configuration/ Temp folder as a TMP file; such files are automatically deleted when Dreamweaver is shut down. With the file URL known, check first to make sure that the file is valid:

 if ( (theFile == null)  (theFile == '') ){     return alert('HTTP Error: Unknown File Error');  } 

Now, we're ready to begin the process of reading the file. First, we'll get the DOM and check to make sure it is readable:

 var theDOM = dreamweaver.getDocumentDOM(theFile);  if (theDOM == null) {     return alert('HTTP Error: File Not Properly Formed');  } 

If the application file output data in the <item> tag syntax (described in the previous section), then you can use the DOM function getElementsByTagName() :

 var theItems = theDOM.getElementsByTagName('item');  if ( (theItems == null)  (theItems.length == 0) ){     return alert('HTTP Error: No Items In File');  } 

This puts all of the <item> tags and their contents into an array where they can be easily compared to a template's editable regions, as described in the next section.

Listing 5-1 Sample Application Page.asp (05_sampleapp.asp)
 <%@LANGUAGE="VBSCRIPT"%>  <!--#include file="../Connections/Extension.asp" -->  <%  Dim rsEx   Dim rsEx_numRows  Set rsEx = Server.CreateObject("ADODB.Recordset")  rsEx.ActiveConnection = MM_Extension_STRING  rsEx.Source = "SELECT * FROM extensions"  rsEx.CursorType = 0  rsEx.CursorLocation = 2  rsEx.LockType = 1  rsEx.Open()  rsEx_numRows = 0  %>  <%  rsEx.Close()  Set rsEx = Nothing  %> 

Replacing Editable Regions with Data

Editable regions are the most basic building block of Dreamweaver templates. Although it's possible to generate static pages dynamically without using templates (as described in the final section of this chapter), doing so foregoes numerous advantages. First, templates are visual. With placeholder graphics and text, it is easy to achieve and maintain an overall look and feel. Second, if all the generated pages share common elements such as logos or navigation, these elements can be incorporated into a template without difficulty. Third, and perhaps most importantly, is the ability to update all the template-derived pages into a single operation.

The simplest approach to combining data with editable regions requires a minor bit of planning and coordination. When inserting an editable region into a template, give the region the same name as the field that is supplying the data. With matching names , you can compare an array of data fields to each of the editable regions. If you find a match, you can replace the contents of the editable region with that of the data field.

Two basic comparison techniques are availableone quite a bit more flexible than the other. For situations in which the scope of the project is somewhat limitedeither in the number of fields and editable regions or in the number of different application pages and template combinationseditable region names are compared to elements of the data array. Dreamweaver provides an API function to gather a list of all the editable regions in a template:

 var theERs = theDOM.getEditableRegionList(); 

If you know the order in which the editable regions appear in the array (it parallels their position in the code), you can address the desired region. This technique employs a series of if-else if statements, as follows :

 var theERs = theDOM.getEditableRegionList();  for (var j=0; j<theItems.length; j++) {     var theItem = theItems[j];     var theItemName = theItem.getAttribute('name');     if (theItemName.toLowerCase() == 'dbName')     {        theERs[2].innerHTML = theItem.innerHTML;     }     else if (theItemName.toLowerCase() == 'dbDescription')     {        theERs[3].innerHTML = theItem.innerHTML;     }     else if (theItemName.toLowerCase() == 'dbInfo')     {        theERs[4].innerHTML = theItem.innerHTML;     }     else if (theItemName.toLowerCase() == 'dbPrice')     {        theERs[5].innerHTML = theItem.innerHTML;     }  } 

When a particular data field is found, the contentthe innerHTML propertyis replaced with the innerHTML of the corresponding editable region.

The second method is a bit more complex from a code perspective, but it's also far more flexible. In this technique, the two arrays (the data field items and the editable regions) are compared to one another:

 var theERs = theDOM.getEditableRegionList();  for (i = 0; i < theItems.length; ++i) {     for (k = 0; k < theERs.length; ++k) {        if(theItems[i].getAttribute("name") ==        theERs[k].getAttribute("name")) {           theERs[k].innerHTML = theItems[i].innerHTML;           break;        }     }  } 

The Static from Dynamic Extension

I've intentionally tried to keep the concepts separate from an actual implementation knowing full well that each extension requires a high degree of customization. However, before proceeding into more advanced template topics such as optional regions and template parameters, I thought a look at an extension example that pulls it all together would be beneficial.

NOTE

To view the completed code, see Listing 5-2 at the end of this section.


The Static from Dynamic extension, shown in Figure 5.2, parameterizes a number of the values necessary for the command:

Figure 5.2. The Static from Dynamic extension works on a persite basis and automatically lists all the templates available within the current site.

graphics/05fig02.gif

  • A drop-down list of available templates for the current site.

  • A text field for entering the application page's URL. I pre-filled the box with what I thought was the most common prefix.

  • An option to process all records found or to enter a number.

  • A text field for the path to store the generated pagescomplete with a Browse button so that you can easily select the folder.

  • Another drop-down list showing all the fields found in the application page. After a field is selected, the data in that field provides the base name for the generated files. This list populates after a proper address is entered into the URL field.

One additional element completes the interface. Underneath the Name list is a bit of text that initially reads as follows: Enter URL to Load Fields for Name . After you have entered the URL, the text shows the number of fields found. When the command is generating the static pages, the text area acts as a progress indicator and states, for example, Processing 3 of 25 .

TIP

If you're looking for a similar way to generate Web graphics dynamically, check out my Data-Driven Graphics Wizard available in Fireworks MX, which combines an XML data file with variables embedded into a Fireworks template.


Let's look at the primary function of the Static From Dynamic command, runCommand() , which is called when the user selects OK. Several variables are declared at the outset by using the findObject() function to simplify the coding:

 var theBaseURL = findObject("url_text").value;  var theRB = findObject("records_rb");  var theLimit;  flag = "last" 

I also set a variable, flag , that another function, checkFile() , uses to determine how to proceed. Next, I set theLimit variable, which determines how many records to process. If the first option button, All, is selected, then all of the records are available. Otherwise, the upper limit is determined by whatever number is entered into the number text field.

 if (theRB[0].checked == true) { //all     theLimit = theItems.length;  } else {     theLimit = parseInt(findObject("number_text").value);  } 

Now we're ready for the main loop. After we have entered the looping condition, we construct the theURL variable, which is required for the MMHttp function. The variable is composed of the base URL, which is pulled from the URL text field, and the offset argument string. The offset string ( ?offset=3 ), you'll recall, is used to page through the recordset.

 for (z=0; z < theLimit; ++z) {     theURL = theBaseURL + "?offset=" + z; 

Next, we execute the application file and check the results. Why am I using getFileCallback() rather than just getFile() ? The getFile() function is fine for processing one file at a time as we did in the earlier examples, but when we're executing one file after another, it is necessary to make sure the results from one file are received and verified before proceeding to the next. The callback function ( checkFile() ) not only makes sure that the results received are as expected, but it also slows down the processing sufficiently to avoid data overwriting:

 theResult = MMHttp.getFileCallback("checkFile",theURL); 

With data in handor rather, in theResult we're ready to create a new document based on the template chosen from the pull-down menu. Note that although a dw.newFromTemplate() command is available, it actually applies the template to the current document. Rather than making the user close all of his open files, I create a new, blank document before I call newFromTemplate() :

graphics/05icon03.gif

The next function is an implementation of the technique for replacing content inside of editable regions with the data, described earlier in this chapter:

 var theERs = theTempDOM.getEditableRegionList();  for (i = 0; i < theItems.length; ++i) {     for (k = 0; k < theERs.length; ++k) {        if(theItems[i].getAttribute("name") ==        theERs[k].getAttribute("name")) {           theERs[k].innerHTML = theItems[i].innerHTML;           break;        }     }  } 

All the content has been inserted, and now it's time to save the document. User-defined values for both the filename and the path to the folder are employed:

graphics/05icon04.gif

Listing 5-2 StaticfromDynamic.htm (05_staticdynamic.mxp)
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  <html>  <head>  <title>Static From Dynamic</title>   <meta http-equiv="Content-Type" content="text/html;  charset=iso-8859-1">  <script language="JavaScript"  src="../Shared/Common/Scripts/dwscripts.js"  type="text/JavaScript"></script>  <script language="JavaScript" src="../Shared/MM/Scripts/CMN/UI.js"  type="text/JavaScript"></script>  <script language="JavaScript">  <!-- //*************** Global Variables  var theResult, theFile, theItems, theHelpText, z;  var theNameList = findObject("name_list");  var flag = "first";  function commandButtons(){      return new Array( 'OK', 'runCommand()', 'Cancel',      'window.close()', 'Help', 'getHelp(theHelpFile)');  }  function checkFile() {     var theBaseURL = findObject("url_text").value;     if (flag == "first") {        theResult = MMHttp.getFile(theBaseURL);     }     if (theResult.statusCode != 200) {        return alert('HTTP Error: ' + theResult.statusCode + ' at '        + theURL);     }     theFile = theResult.data;     if ( (theFile == null)  (theFile == '') ){        return alert('HTTP Error: Unknown File Error');     }     var theDataDOM = dreamweaver.getDocumentDOM(theFile);     if (theDataDOM == null) {        return alert('HTTP Error: File Not Properly Formed');     }     theItems = theDataDOM.getElementsByTagName('item');     if ( (theItems == null)  (theItems.length == 0) ){        return alert('HTTP Error: No Items In File');     }     var theFields = new Array();     for (i=0; i < theItems.length; ++i) {        theFields[i] = theItems[i].getAttribute("name");     }     if (flag == "first") {        theHelpText = document.getElementsByTagName("helptext");        loadSelectList(theNameList,theFields);        theHelpText[0].innerHTML = theItems.length + " fields        found.";     } else {        var theMsg = "Processing " + z + " of " + theItems.length;        theHelpText[0].innerHTML = theMsg;     }     return;  }  function runCommand(){     //set URL     var theBaseURL = findObject("url_text").value;     flag = "last"     //get data     var theLimit;     var theRB = findObject("records_rb");     if (theRB[0].checked == true)   { //all        theLimit = theItems.length;     } else {        theLimit = parseInt(findObject("number_text").value);     }      for (z=0; z < theLimit; ++z) {        theURL = theBaseURL + "?offset=" + z;        theResult = MMHttp.getFileCallback("checkFile",theURL);        //create document from template        var theList = findObject("template_list");        var theTemplate = dw.getSiteRoot() + "Templates/" +        theList.options[theList.selectedIndex].text;        var theTempDOM = dw.createDocument();        dw.newFromTemplate(theTemplate);        var theERs = theTempDOM.getEditableRegionList();        //replace editable regions with data        for (i = 0; i < theItems.length; ++i) {           for (k = 0; k < theERs.length; ++k) {              if(theItems[i].getAttribute("name") ==              theERs[k].getAttribute("name")) {                 theERs[k].innerHTML = theItems[i].innerHTML;                 alert("here");                 break;              }           }        }        //store document        docName = findObject("folder_text").value +        theNameList.options[theList.selectedIndex].text + ".htm";        res = dw.saveDocument(theTempDOM, docName);     }     window.close()  }  function findFolder() {     findObject("folder_text").value = dw.browseForFolderURL();  }  function getTemplateList() {     //returns a list of templates in site     var theTemplateDir = dw.getSiteRoot() + "Templates/";     var theTemplates = new Array();     theTemplates = DWfile.listFolder(theTemplateDir + "*.dwt",     "files");     if (theTemplates){        var theList = findObject("template_list");        loadSelectList(theList,theTemplates);     }  }  function initUI() {     getTemplateList();  }  //-->  </script>  </head>  <body onLoad="initUI()">  <form name="form1" method="post" action="">    <table border="0">      <tr>        <td><div align="right">Template</div></td>        <td><select name="template_list" id="template_list"        style="width:220">            <option selected>Loading templates.............</option>          </select></td>      </tr>      <tr>        <td><div align="right">URL</div></td>        <td><input name="url_text" type="text" id="url_text"        style="width:220" value="http://localhost/fp2dw/beyond05/        ch05.asp" onBlur="checkFile()"></td>      </tr>      <tr>        <td><div align="right">Records</div></td>         <td><input name="records_rb" type="radio" value="all"            checked>          All          <input name="records_rb" type="radio" value="some">          <input name="number_text" type="text" id="number_text"          size="5"></td>      </tr>      <tr>        <td><div align="right">Save In</div></td>        <td><input name="folder_text" type="text" id="folder_text"        style="width:150">          <input type="button" name="Button" value="Browse"          onClick="findFolder()"></td>      </tr>      <tr>        <td><div align="right">Name</div></td>        <td><select name="name_list" id="name_list"        style="width:220">            <option selected>Choose Base Name</option>          </select></td>      </tr>      <tr>        <td>&nbsp;</td>        <td><helptext>Enter URL to Load Fields for        Name</helptext></td>      </tr>    </table>    </form>  </body>  </html> 


Joseph Lowery's Beyond Dreamweaver
Joseph Lowerys Beyond Dreamweaver
ISBN: B000H2MWYS
EAN: N/A
Year: 2001
Pages: 87
Authors: Joseph Lowery

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net