Changes between Initial Version and Version 1 of HowToCreateWidget


Ignore:
Timestamp:
05/21/08 11:54:42 (17 years ago)
Author:
aboudreault
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • HowToCreateWidget

    v1 v1  
     1[[PageOutline]]
     2= How To Create A Widget =
     3
     4== Introduction ==
     5
     6This !HowTo describe how create a widget using Fusion and MapServer. It will show you all the steps to do and files to create for having functionnal widget. It assume that you have followed the MapServer tutorial and that your Fusion installation is working. In this !HowTo, we'll create a widget that display a search form and make a simple search via the MapServer CGI.
     7
     8== !HowTo Prerequisites ==
     9
     10This !HowTo uses the same MapServer data than the MapServerTutorial. 
     11
     12 * '''Fusion''': [http://trac.osgeo.org/fusion/wiki/GetIt]
     13 * '''MapServer gmap-ms46 sample data''': [http://dl.maptools.org/dl/]
     14 * [http://trac.osgeo.org/fusion/attachment/wiki/MapServerTutorialFr/icons-demo.zip?format=raw Icons]
     15
     16You can follow these [http://trac.osgeo.org/fusion/wiki/MapServerTutorial#Installation installation instructions] for install the data if you haven't followed the MapServerTutorial but you should follow the entire tutorial and make it works first because this !HowTo is a sequel to it.
     17
     18== Basic steps ==
     19
     20This is the basic steps that you'll need to do for creating a Fusion widget.
     21
     22 1. Create the !JavaScript implementation for the widget.
     23    This will define all the widget functionnalities available.
     24
     25 2. Create the script that the widget will use. (In this !HowTo, it will be a PHP script)
     26    This step is optionnal. A widget may not needs to use script. (ie. Zoom widget: it doesn't uses a PHP script, it just use a !OpenLayers control)
     27
     28 3. Add the new widget in a Fusion application.
     29
     30 4. Create the WidgetInfo xml of the new widget.
     31    This step is also optionnal. It's for the widget documentation. It will describe the widget description and it parameters that you can pass to it through the ApplicationDefinition.xml. (I didn't make it for this tutorial)
     32
     33
     34== Creating the !JavaScript implementation ==
     35
     36Here, we'll make the !JavaScript implementation of the widget. This widget has two visual component: a text box for let the user write the search pattern and a button for execute the search. Fusion already provides a basic button implementation (Fusion.Tool.!ButtonBase). So we'll only need to generate the text box manually and then make our widget inherit from the !ButtonBase class to have our button. Once we inherit from the !ButtonBase, we can implement the button action in the execute method. Take a look to the widget implementation and the comments. This is the content of the file named '''!MySearchWidget.js''' and have to be in the ''fusion/widgets/'' directory.
     37
     38{{{
     39 /********************************************************************
     40 * Class: Fusion.Widget.MySearchWidget
     41 *
     42 * A widget example that displays a basic search form.
     43 *
     44 * **********************************************************************/
     45
     46Fusion.Widget.MySearchWidget = Class.create(); // This is the way for create a new class
     47Fusion.Widget.MySearchWidget.prototype = {
     48    oTextBox : '',
     49    sResultPanel : '',                        // These variables are the data member of our widget
     50    sLayerName : '',
     51    sAttribute: '',
     52
     53    /*  This method is off course required. It's the constructor of the widget.
     54        All class must have a constructor. When ApplicationDefinition will
     55        be parsed, the widgets will be created and initialized with this
     56        method. The parameter widgetTag is the json object that contains the widget
     57        parameters. (ie. Name, Type, ImageUrl, Label, etc.)
     58     */
     59    initialize : function(widgetTag){
     60        // A Fusion widget must implement the base widget class to work.
     61        Object.inheritFrom(this,Fusion.Widget.prototype, [widgetTag, true]);
     62        this.enable = Fusion.Widget.MySearchWidget.prototype.enable;
     63       
     64        /* This is to get the specific parameters of the current widget.
     65         * The widget allow to set the following parameters in the
     66         * ApplicationDefinition.xml:
     67         *  LayerName: The layer name to execute the mapserver queryByAttributes()
     68         *  Attribute: The attribute name (column name)
     69         *  ResultPanel: The place to display the search results.
     70         */
     71        var json = widgetTag.extension;
     72       
     73        this.sLayerName = json.LayerName ? json.LayerName : '';
     74        this.sAttribute = json.Attribute ? json.Attribute : '';
     75        this.sResultPanel = json.ResultPanel ? json.ResultPanel : '';
     76
     77        // Creating the text box html element and adding it to the page.
     78        this.oTextBox = document.createElement('input');
     79        this.oTextBox.type = 'text';
     80        this.oTextBox.size = 20;
     81        this.oTextBox.className = "searchInputBox";
     82        this.domObj.appendChild(this.oTextBox);
     83       
     84        // Creating the button with all necessary event binded.
     85        Object.inheritFrom(this, Fusion.Tool.ButtonBase.prototype, []);
     86    },
     87
     88  /*  This method is also important and required. It define the action to do
     89   *  when we execute the widget. The Fusion.Tool.ButtonBase class has already binded
     90   *  it to the button click event. In this example, the widget send a ajaxRequest to a
     91   *  php that use the MapServer PHP/MapScript for doing a queryByAttributes().
     92   */
     93   execute : function() {
     94      this.oMap._addWorker();
     95      var options = {};
     96      var pattern = this.oTextBox.value.trim();
     97      if (pattern == "")
     98          return;
     99     
     100      options.sessionid = this.getMap().getSessionID();
     101      options.attribute = this.sAttribute;
     102      options.pattern = "/"+pattern+"/i";
     103      options.layer = this.sLayerName;
     104     
     105      var sl = Fusion.getScriptLanguage();
     106      var loadmapScript = this.oMap.aMaps[0].arch + '/' + sl  + '/SimpleSearch.' + sl;
     107      var params = 'mapname='+this.oMap.aMaps[0]._sMapname+"&session="+options.sessionid+'&layer='+options.layer+'&attribute='+options.attribute+'&pattern='+options.pattern;
     108      // The ajaxRequest is binded to the processQueryResults method of our widget for process the result of the php script.
     109      var requestOptions = {onSuccess: this.processQueryResults.bind(this), parameters: params};
     110      Fusion.ajaxRequest(loadmapScript, requestOptions);
     111     
     112  },
     113
     114    /*  This method is specific to our widget and will be called after each ajaxRequest
     115     *  made for process the results. In this case, i only take the name of cities found and
     116     *  display them in the result panel.
     117     */
     118    processQueryResults : function(r) {
     119        this.oMap._removeWorker();
     120        var myDiv = document.getElementById(this.sResultPanel);
     121        var o;
     122        eval("o="+r.responseText);
     123        if (myDiv)
     124        {
     125            myDiv.innerHTML = '';
     126            if (!o.hasSelection)
     127            {
     128                myDiv.innerHTML = "no result";
     129            }
     130            else
     131            {
     132                var numResult = o.values.length;
     133                for (i = 0; i < numResult; i++)
     134                    myDiv.innerHTML += o.values[i][5] + "<br/>";
     135            }
     136        }
     137    },
     138};
     139
     140}}}
     141
     142The !JavaScript implementation is now done. We'll now need to create the script.
     143
     144== Creating the Script ==
     145
     146This script will be used by our widget using an ajax request. In the future, Fusion will probably support more than one script langage, but for the moment it's PHP that is implemented and used. This is my simple script that make a queryByAttributes() to MapServer and returns the results in JSON. This is the content of the file '''!SimpleSearch.php''' and have to be in the ''fusion/MapServer/php'' directory.
     147
     148{{{
     149<?php
     150/*****************************************************************************
     151 * Purpose: create a query by attributes
     152 *****************************************************************************/
     153
     154  /* set up the session */
     155include ("Common.php");
     156include ("Utilities.php");
     157include('../../common/php/Utilities.php');
     158
     159if ( ($_REQUEST['layer'] != '') && ($_REQUEST['attribute'] != '') && ($_REQUEST['pattern'] != ''))  {
     160     $szLayer = $_REQUEST['layer'];
     161     $szAttribute = $_REQUEST['attribute'];
     162     $szPattern = $_REQUEST['pattern'];
     163}
     164else
     165{
     166     die('layer, attribute or value not set');
     167}
     168
     169if (!isset($mapName)) {
     170     die('mapname not set');
     171}
     172if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
     173     $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
     174}
     175
     176$result = NULL;
     177$result->hasSelection = false;
     178$result->values = array();
     179
     180$oLayer = $oMap->GetLayerByName($szLayer);
     181
     182if (@$oLayer->queryByAttributes($szAttribute,$szPattern, MS_MULTIPLE) == MS_SUCCESS)
     183{
     184     $numResults = $oLayer->getNumResults();
     185     $result->hasSelection = true;
     186}
     187
     188header('Content-type: text/x-json');
     189header('X-JSON: true');
     190if ($result->hasSelection)
     191{
     192     $oLayer->open();
     193     
     194     //get first shape to get the attributes
     195     $oResultSet = $oLayer->getResult(0);
     196     $selFields = array();
     197     $oShape = $oLayer->getShape($oResultSet->tileindex,$oResultSet->shapeindex);
     198
     199     while ( list($key,$val) = each($oShape->values) )
     200     {
     201          array_push($selFields, $key);
     202     }
     203   
     204   
     205     for ($iRes=0; $iRes < $numResults; $iRes++)
     206     {
     207          $oResultSet = $oLayer->getResult($iRes);
     208          $oShape = $oLayer->getShape($oResultSet->tileindex,$oResultSet->shapeindex);
     209          $result->values[$iRes] = array();
     210         
     211          //field values
     212          $iNumField = count($selFields);
     213          for($iField=0; $iField < $iNumField; $iField++)
     214          {
     215               $value = $oShape->values[$selFields[$iField]];
     216               $value = str_replace("'", "\'", $value);
     217               array_push($result->values[$iRes],$value);
     218          }
     219     }
     220
     221     $oLayer->close();
     222
     223}
     224
     225echo var2json($result);
     226
     227?>
     228}}}
     229
     230
     231The widget is now complete and needs to be tested.
     232
     233== Adding the new widget in a Fusion application ==
     234
     235 * '''Html page'''
     236
     237For those who have already the ''index.html'' from the MapServer tutorial, you can simply add two <div> somewhere in the page (one for the search widget, and one for the result panel) or replace the old ''index.html'' by this one:
     238
     239{{{
     240<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
     241<html xmlns="http://www.w3.org/1999/xhtml">
     242  <head>
     243    <title>Fusion MapServer Widget</title>
     244    <script type="text/javascript" src="../fusion/lib/fusion.js"></script>
     245    <style type="text/css">
     246      @import url(../fusion/jx/css/jxskin-graphic.css);
     247      #bodyContent { position:relative; background-color:#dfe9f5; border: thin solid #000000; height:580px; width:760px; margin-top:25px;}
     248      #Map { position:absolute; top:35px; right:5px; width:439px; height:349px; border: thin solid #000000; float: right; visibility: visible; display:block; }
     249      #Legend { position:absolute; top:35px; left:5px; width:300px; height:349px; background-color:#ffffff; border: thin solid #000000; overflow-y: auto; overflow-x:hidden; }
     250      #toolbar { position:absolute; left: 310px; }
     251      #mySimpleSearch { position:absolute; left: 10px; bottom: 150px; }
     252      .searchInputBox {float:left;}
     253      #myResultPanel { position: absolute; right: 5px; bottom: 15px; height: 170px; width: 439px; color:red; background-color:white; overflow:auto; border: thin solid black; text-align: left;}
     254    </style>
     255    <script type="text/javascript">
     256      window.onload = function() {
     257         Fusion.initialize();
     258      }
     259    </script>
     260  </head>
     261  <body>
     262    <center>
     263      <div id="bodyContent">
     264        <div id="toolbar">
     265          <div id="Pan" ></div>
     266          <div id="ZoomIn"></div>
     267          <div id="ZoomOut"></div>
     268          <div id="Measure"></div>
     269        </div>
     270        <div id="Legend" align="left"></div>
     271        <div id="Map"></div>
     272        <div id="mySimpleSearch"></div>
     273        <div id="myResultPanel"></div>
     274      </div>
     275    </center>
     276  </body>
     277</html>
     278}}}
     279
     280 * '''ApplicationDefinition.xml'''
     281
     282Now, add the new widget definition in the ApplicationDefinition.xml. As you can see, the tag <Extension> is used to add the specifics parameters to our new widget. This widget configuration will uses the layer named '''popplace''' for search all the city that matches with the pattern provided in the textbox. Then, the results will be displayed in the '''myResultPanel''' div.
     283
     284{{{
     285  <Widget xsi:type="UiWidgetType">
     286  <Name>mySimpleSearch</Name>
     287  <Type>MySearchWidget</Type>
     288  <StatusItem>A simple search widget</StatusItem>
     289  <ImageUrl></ImageUrl>
     290  <ImageClass/>
     291  <Tooltip></Tooltip>
     292  <Label>Search</Label>
     293  <Disabled/>
     294  <Extension xsi:type="CustomContentType">
     295    <LayerName>popplace</LayerName>
     296    <Attribute>NAME</Attribute>
     297    <ResultPanel>myResultPanel</ResultPanel>
     298  </Extension>
     299</Widget>
     300
     301}}}
     302
     303You should now be able to test the new widget via the web address. In my case: http://localhost:8080/demo/index.html