< Calling Plugins

Dynamic mapfile modifications >

## 2. New Plugins

## 2.1. What are Plugins

### 2.1.1. Definition

CartoWeb plugins are modular packages of files (PHP classes, HTML templates, images and other resources) that are used to perform a dedicated action: main map formatting, layers browsing interface, map browsing (zooming, paning etc.), queries, user authentication, search interfaces and many more.

### 2.1.2. Plugins and Coreplugins

There are two kinds of plugins:

- `coreplugins`: fundamental plugins that perform "low-level" actions such as map size handling, browsing tools, layers selection. Plugins that are frequently used in many CartoWeb applications may be included in this category as well. They are always available and activated. As a result, other plugins may interact with them. Coreplugins files are grouped in the `coreplugins/` directory.
- `plugins`: "normal" plugins perform more specificactions and are not always activated. Normal plugins activation is done by setting the `loadPlugins` parameter in `client_conf/client.ini` for CartoClient plugins and in `server_conf/<mapId>/<mapId>.ini` for CartoServer ones. For instance:

      loadPlugins = auth, outline, exportHtml

  Since they are not always available, simple plugins usually do not rely on each other. On the other hand, it is not a problem for them to call some coreplugins functionalities if the latter are publicly accessible. Simple plugins files are grouped in the `plugins/` directory.

The general philosophy is to gather all files of a given plugin in the same dedicated directory, including files from both CartoClient and CartoServer sides of the plugin. Thus it is easy to "plug" a new module in CartoWeb architecture by simply pasting it in the `plugins/` or `coreplugins/` parent directories. Note however that plugins configuration files (named `<pluginName>.ini`) are placed in the `client_conf/` and/or `server_conf/<mapId>/` depending if those plugins have CartoClient/CartoServer components.

### 2.1.3. Plugins Structure

Plugins and coreplugins have the following general structure:

    <pluginName>/
    <pluginName>/client/
    <pluginName>/server/
    <pluginName>/common/
    <pluginName>/templates/
    <pluginName>/htdocs/
    <pluginName>/htdocs/gfx/
    <pluginName>/htdocs/js/
    <pluginName>/htdocs/css/

- `client/` contains all specific CartoClient-side PHP files.
- `server/` contains all specific CartoServer-side PHP files.
- `common/` contains PHP files shared by both CartoClient and CartoServer sides, or at least files that are not specific to one side or the other.
- `templates/` contains all the plugin-specific Smarty templates. Since HTML templates are only used in CartoClient, files from `templates/` are only called by `client/` code.
- `htdocs/` contains all files (PHP pages, images, JavaScript or CSS files, etc.) that may be web-accessed when running the plugin. Those files are dispatched in various directories depending on their nature. If necessary, you can create additional subdirectories. For instance `java/` if your plugin uses a Java applet. To preserve the plugin independence, it is strongly recommended not to add your CSS styles in the general CartoClient style sheet but to create a specific file here that will be called separately.

Note that it is not required to actually create the whole structure described above. Only directories that contain files are necessairy. For instance if a plugin only perform CartoServer actions, it is no use to create `client/`, `templates/` and `htdocs/` directories. `common/` may be usefull if not-CartoServer-specific classes have to be defined.

There are two ways to add a plugin/coreplugin to CartoWeb: writing a brand new one or overriding/extending an existing one.

## 2.2. Writing a Plugin

### 2.2.1. Introduction

If no existing plugin or coreplugin fulfils your requirements and if none offers close enough functionalities to justify an adaptation, you can write a new plugin.

Plugins main classes (client and/or server if any) must extend CartoWeb defined`ClientPlugin` and/or `ServerPlugin` classes which provide base plugin tools. For instance:

    class ClientYourPlugin extends ClientPlugin {

        /* here comes your plugin client class definition */

    }

### 2.2.2. Plugin or Coreplugin?

First of all you have to determine if you are about to design a simple plugin or a coreplugin. To be a coreplugin, your plugin must be really generic and present a great interest to the CartoWeb users community since it might be included in the upstream distribution. Contact CartoWeb development team for more info. In most cases it is better and sufficient to create a simple plugin.

To activate a coreplugin, update the `Cartoclient::getCorePluginNames()` method in `/client/Cartoclient.php` and/or the `ServerContext::getCorePluginNames()` one in `/server/ServerContext.php`. For instance:

    private function getCorePluginNames() {
    return array('images', 'location', 'layers', 'query', 'mapquery',
                 'tables', 'yourPluginName');
    }

To load a regular plugin, update the `loadPlugins` parameter from `client_conf/client.ini` and/or `server_conf/<mapId>/<mapId>.ini` as in following example:

    loadPlugins = auth, outline, exportHtml

### 2.2.3. How Plugins Are Called

As explained in Section 2.1, “ What are Plugins”, plugins are independent aggregations of PHP code that are called by the CartoWeb core classes to perform dedicated actions. Plugins are called several times during the program execution (entry points). Thus they can interact at various level of the application.

To determine what plugins must be called at what moment and to perform what action, plugins must implement one or more of the CartoWeb plugin interfaces (according to the object-oriented programing meaning). The interfaces define methods that will be triggered by the main program during its execution. For example, you can take a look at the following simplified `Cartoclient::doMain()` method ("main program") defined in `/client/Cartoclient.php`:

    private function doMain() {
        $this->callPluginsImplementing('InitUser', 'handleInit',
                                       $this->getMapInfo());

        if ($this->isRequestPost()) {
            $this->cartoForm =
                $this->httpRequestHandler->handleHttpRequest(
                                                    $this->clientSession,
                                                    $this->cartoForm);

            $request = new FilterRequestModifier($_REQUEST);
            $this->callPluginsImplementing('FilterProvider',
                                           'filterPostRequest', $request);
            $this->callPluginsImplementing('GuiProvider',
                                           'handleHttpPostRequest',
                                           $request->getRequest());
        } else {
            $request = new FilterRequestModifier($_REQUEST);
            $this->callPluginsImplementing('FilterProvider',
                                           'filterGetRequest', $request);
            $this->callPluginsImplementing('GuiProvider',
                                           'handleHttpGetRequest',
                                           $request->getRequest());
        }

        $mapRequest = $this->getMapRequest();
        $this->callPluginsImplementing('ServerCaller', 'buildRequest',
                                       $mapRequest);

        $this->mapResult = $this->getMapResultFromRequest($mapRequest);

        $this->callPluginsImplementing('ServerCaller', 'initializeResult',
                                       $this->mapResult);

        $this->callPluginsImplementing('ServerCaller', 'handleResult',
                                       $this->mapResult);

        $this->formRenderer->showForm($this);

        $this->callPluginsImplementing('Sessionable', 'saveSession');
        $this->saveSession($this->clientSession);
    }

`callPluginsImplementing($interfaceName, $methodName, $argument)` is run at various points of the program and make plugins implementing given <interfaceName> interface execute given <methodName> with given <argument> argument.

Of course interface-defined methods must be implemented in the matching plugins. Plugins can implements one or more CartoWeb interfaces.

Implementing interfaces is not mandatory when writing a plugin but not doing so will keep plugins from being implicitely called by the main program. As a result, methods from plugins with no interface implementation - also called "service plugins" - must be explicitely called by another piece of code (generally an other plugin).

    class ClientYourPlugin extends ClientPlugin
                           implements Sessionable, GuiProvider {

        /* here comes your plugin client class definition */

    }

For a comprehensive list of available client and server interfaces, see `/client/ClientPlugin.php` and `/server/ServerPlugin.php` files or take a look at the CartoWeb PHP API documentation.

(Get a high quality version of this diagram here.)

### 2.2.4. Plugin Creation Check-List

1. Determine if you will write a plugin or a coreplugin.
2. Create a `<yourPlugin>/` directory in `/projects/<yourProject>/plugins/` if you need a simple plugin. Directory name will be the plugin name. You can use whatever name you want except of course names of already existing plugins or coreplugins. Yet it is recommended to use lowercase letters, capitalizing only the first letter of each word that composes the name (eg. "yourPluginName"). In case of a coreplugin, there is no way to create a coreplugin in a project context. Coreplugins can only be integrated in the upstream application. It is not recommended to do so without CartoWeb developers agreement because of compatibility troubles that may occur when upgrading, etc.
3. Create subdirectories to store all plugin-related resources files and templates.
4. Create `client/`, `server/`, `common/` if your plugin as CartoClient, CartoServer and respectively common parts.
5. Create your main PHP classes files. Those files must be named using the first-letter-capitalized name of your plugin, prefixing it with "Client" or "Server" for client or server components (eg. `ClientYourPlugin.php`, `ServerYourPlugin.php`, `YourPlugin.php`).
6. Extend `ClientPlugin` and/or `ServerPlugin` CartoWeb classes in the matching above files and name the result classes using their files names (with no ".php"). For instance:

       <?php
       /**
        * @version $Id: ServerYourPlugin.php,v 1.8 2005/02/23 11:52:43 johndoe Exp $
        */

       class ServerYourPlugin extends ServerPlugin {

7. Make your classes implement needed interfaces and redefine corresponding methods. Note that the `common/` part class "YourPlugin" does not have to extend or implement any CartoWeb class or interface. It is used as a container for common data and treatment used by client and server classes.
8. If your plugin is implementing `ServerCaller` and `ClientResponder`, you will need to write the WSDL code for the plugin request and response. See Section 2.1, “ What are Plugins” for examples on how to write such WSDL documents.

   ### Note

   You need to add a `className` element in the WSDL file for a proper parsing from CartoWeb

9. Activate your plugin by adding its name to the `loadPlugins` of the matching project configuration files.

### 2.2.5. Automatic Files Inclusion

Main plugin PHP files (eg. `ClientYourPlugin.php`, `ServerYourPlugin.php`, `YourPlugin.php`) are automatically included and their contained classes and objects are directly accessible. Other files in `client/`, `server/` or `common/` are not and must be included explicitely in the main plugin PHP files.

Templates stored in the plugin `templates/` directory are also accessible directly by using PHP code similar to the followin one:

    $smarty = new Smarty_Plugin($this->getCartoclient(), $this);
    $smarty->assign('foo', 'bar');
    return $smarty->fetch('yourPlugin.tpl');

## 2.3. Adapting a Plugin

### 2.3.1. Approaches

If an already available plugin or coreplugin offers close functionalities to the ones you need, if you wish to slightly modify its behavior or simply want to adapt its output to your website layout, it is far easier to adapt it then to build a new one from scratch.

There are two levels of plugin adaptation. You can:

- override its HTML templates, resources (pictos, CSS or JS files) and its configuration as well. This approach is generally sufficient when you only need to adapt the layout.
- extend the main PHP classes to add your own methods or overload existing ones. This approach is required when you need to add some PHP code to the plugin.

Both approaches are not incompatible and may be combined to obtain desired result. See Section 2.3.4, “Combining Both Approaches” for more explanations.

### 2.3.2. Overriding a Plugin

Overriding a plugin is the simplest way to adapt it to your needs. It is done by duplicating the plugin files (at least the ones you want to adapt) in your project frame. For more information about projects handling, see Section 2.4, “Projects”.

This approach is recommended when you want to use your own versions of the plugin templates or resources. Moreover you can add any new resources files that will be called in your customized templates. However you will not be able to replace or add PHP files (except PHP pages in the plugin `htdocs/`. To adapt a plugin server-side behavior (PHP classes), you have to extend the plugin, which is explained in Section 2.3.3, “Extending a Plugin”.

Say for instance, you want customize the `layers` coreplugin by modifying its template `layers.tpl` and rewriting some of its JS tools (stored in `layers.js`). Than your project-adapted coreplugin will look like:

    /projects/<yourProjectName>/coreplugins/layers/
    /projects/<yourProjectName>/coreplugins/layers/templates/
    /projects/<yourProjectName>/coreplugins/layers/templates/layers.tpl
    /projects/<yourProjectName>/coreplugins/layers/htdocs/
    /projects/<yourProjectName>/coreplugins/layers/htdocs/js/
    /projects/<yourProjectName>/coreplugins/layers/htdocs/js/layers.js

If you don't need to override the CSS file, it is no use to create a `css/` directory containing a copy of the upstream `layers.css`.

If you want to neutralize a file, you can simply override it with a blank version. For instance to have a void output, create a template file with no content.

It is also possible to override the plugin configuration files by adding `<pluginName>.ini` files in the project configuration directories `client_conf/` and/or `server_conf/<mapId>/`. When the plugin is launched, upstream and project configuration files are merged so you don't need to duplicate the configuration parameters that stay unchanged with your adapted plugin.

### 2.3.3. Extending a Plugin

Extending a plugin is required when your adaptations involve deep changes such as additions or overloadings of methods in the plugin PHP classes.

To do so you will have to extend the plugin PHP classes in the object-oriented programing definition. Since plugin main classes are named using a plugin-name based convention (eg. `ClientLayers` and `ServerLayers` for the CartoClient and CartoServer main classes of the `layers` coreplugin) and since extended classes cannot have the same name than their parent class, you will have to rename your plugin. Any plugin name is OK (as long as it is not already used!) but it is recommended to use a <projectName><initialPluginName> separating words with caps.

Extended coreplugins directories and files must be saved in `/projects/<projectName>/coreplugins/<extendedPluginName>/` whereas extended simple plugins ones will be located in `/projects/<projectName>/plugins/<extendedPluginName>/`.

When writing your extended class, the first thing to do is to specify what plugin is replaced by the new one. This is done by overloading the `replacePlugin()` method. It should return the replaced plugin name. For instance, to extend the `layers` coreplugin CartoClient part in your `myProject` project, create a `ClientMyProjectLayers.php` as follows:

    <?php
    /**
     * @package CorePlugins
     * @version $Id: ClientMyProjectLayers.php,v 1.8 2005/02/23 11:52:43 johndoe Exp $
     */

    class ClientMyProjectLayers extends ClientLayers {

        public function replacePlugin() {
            return 'layers';
        }

        /* Add or overload methods here */

    }
    ?>

To be activated, extended plugins AND coreplugins must be explicitely be declared in the `loadPlugins` parameter of your project general configuration files:

    loadPlugins = exportPdf, auth, myProjectLayers

With no surprise, extended classes can take advantage of the tools provided by the interfaces their parent classes implement. By implementing additional interfaces, they will have access to complementary interactions as well. In that case, don't forget to overload the matching interface-defined methods in your extended class. For instance:

    ClientMyProjectLayers extends ClientLayers
                          implements ToolProvider {

    /* ... */

    }

### Tip

To use new config parameters in your extended plugin, add them in the .ini file of your project original plugin (eg. `/projects/<your_project>/client_conf/<original_plugin>.ini`).

### 2.3.4. Combining Both Approaches

If you need to modify/add templates or resources (overrriding) AND PHP classes (extension), you can combine both approaches by following the instructions of the two last sections.

Say you would like to customize the `images` coreplugin (mainmap size and formats management) to:

- update the layout (new pictos, new texts, new CSS),
- add some JS processing,
- add a new form field in a separated area of the CartoWeb interface.

First point is achieved by creating a `images/` directory in `/projects/yourProject/coreplugins/` and filling it with an overriden template `mapsizes.tpl`, a new `images.css` and some pictos:

    /projects/yourProject/coreplugins/images/
    /projects/yourProject/coreplugins/images/templates/mapsizes.tpl
    /projects/yourProject/coreplugins/images/htdocs/css/images.css
    /projects/yourProject/coreplugins/images/htdocs/gfx/button.png

    <!-- mapsizes.tpl -->
    <p>{t}Mapsize:{/t}
    <select name="mapsize" id="mapsize" onchange="javascript:checkMapsize();">
    {html_options options=$mapsizes_options selected=$mapsize_selected}
    </select>
    <input type="image" src="{r type=gfx plugin=images}button.png{/r}"
    alt="{t}Ok button{/t}" id="imagesButton" /></p>

For details about template `{r}` (resource) and `{t}` (translation) tags, see Section 17.2, “Internationalization” and Section 17.3, “Resources”.

`checkMapsize()` JavaScript function is not defined in the upstream coreplugin. So we have to add a JS file in our overriden plugin:

    /projects/yourProject/coreplugins/images/htdocs/js/
    /projects/yourProject/coreplugins/images/htdocs/js/images.js

    /* images.js */

    function checkMapsize() {
      alert('foobar');
    }

To add a new form field in a separated area and consequently in a separated template, there is no other way than to modify the `ClientImages` PHP class in order to call the additional template in a special method. The extension approach is thus required. Then create a `yourProjectImages/` directory in `/projects/yourProject/coreplugins/` as follows:

    /projects/yourProject/coreplugins/yourProjectImages/
    /projects/yourProject/coreplugins/yourProjectImages/client/
    /projects/yourProject/coreplugins/yourProjectImages/client/ClientYourProjectImages.php

It can seem a little tricky but the new template file (say `yourImages.tpl`) will not be stored in `/projects/yourProject/coreplugins/yourProjectImages/templates/` as one can expect it but in `/projects/yourProject/coreplugins/images/templates/` with the templates of the "overriden part" of the coreplugin. Remember: templates are stored in the overriden part and PHP classes in the extended part.

    <!-- yourImages.tpl -->

    <input type="text" name="testField" value="{$imagesTest}" />

    <?php
    /**
     * @version $Id: ClientYourProjectImages.php,v 1.8 2005/02/23 11:52:43 johndoe Exp $
     */

    class ClientYourProjectImages extends ClientImages {

        // indicates that we want to use current plugin instead of
        // regular images plugin
        public replacePlugin() {
            return 'images';
        }

        // overloaded method
        public function renderForm(Smarty $template) {
            // a {$image2} Smarty var must have been added in cartoclient.tpl
            $template->assign('image2', $this->drawNewImagesField());
            parent::renderForm($template);
        }

        // additional method
        private function drawNewImagesField() {
            $smarty = new Smarty_Plugin($this->getCartoclient(), $this);
            $smarty->assign('imagesTest', 'Foobar');
            return $smarty->fetch('yourImages.tpl');
        }
    }
    ?>

Don't forget to activate the extended plugin in `/projects/yourProject/client_conf/client.ini`:

    loadPlugins = yourProjectImages

## 2.4. Special Plugins

### 2.4.1. Export Plugins

Export plugins allow to export maps and data. Concepts described below provide tools to help writing such plugins.

In brief, export plugins follow these steps in order to generate an output:

- Retrieve last request that was sent to server
- Call all plugins to ask for request modification (eg. map resolution changes, keymap generation enabled/disabled, etc.)
- Call server to get a new modified result
- Use the result to generate output
- Return output in a standardized form

#### 2.4.1.1. Export Plugin Naming Convention

### Warning

Export plugins MUST have names that begin with the string "export", for instance `exportPdf`, `exportCsv` or `exportHtml`.

This rule does not apply to extended plugins since only the original plugin name is detected.

#### 2.4.1.2. `ExportPlugin`

Class `ExportPlugin` implements a special type of client plugin, with some specific functionnalities for export. It implements interface `GuiProvider` so child classes must implement corresponding methods. Class methods are:

- `getLastMapRequest`: returns last used request object. This is useful to prepare a new call to server in order to obtain data specific to export. This call is done in method `getExportResult`
- `getLastMapResult`: This can also be useful in some cases to have the last returned result obect
- `getExportResult`: executes call to server in order to obtain a modified result suitable for export generation. Calls all exportable plugins in order to modify request (see Section 2.4.1.4, “`Exportable` Interface”)
- `getExport` (abstract): contains export generation itself. Should prepare export configuration, call `getExportResult` and generate export in an `ExportOutput` object

#### 2.4.1.3. `ExportConfiguration`

Export configuration objects contain information on what is needed by export plugin to generate output. For instance, for a CSV export, no images are needed and it would be a waste of time to generate them.

Configuration is set in method `getExport`, then passed to method `getExportResult` in order to get modified result. Configuration is used by plugin to know how to modify request to retrieve useful data.

#### 2.4.1.4. `Exportable` Interface

Exportable interface declares a method `adjustExportMapRequest` which modifies a standard map request to a special export request. For instance, plugin Image uses `ExportConfiguration` object to know if maps are needed by export plugin. If not, image request is modified.

#### 2.4.1.5. Example

Plugin `exportCsv` is a good, simple example of export plugin.

Configuration is filled in method `getConfiguration`. No images are required to output a CSV file:

    protected function getConfiguration() {
        $config = new ExportConfiguration();
        $config->setRenderMap(false);
        $config->setRenderKeymap(false);
        $config->setRenderScalebar(false);
    }

Result rendering is done in method `getExport`. Note that no calls to methods `getLastMapRequest` or `adjustExportMapRequest` are needed, as those calls are handled by method `getExportResult`:

    protected function getExport() {
        $this->getExportResult($this->getConfiguration());

        // ...

        $output = new ExportOutput();
        $output->setContents($contents);
        return $output;
    }

Final output (headers + content) is done in method `output`. For instance:

    public function output() {
        header('Content-Type: text/html');
        print $this->getExport()->getContents();
        return '';
    }

### 2.4.2. Filters

Filter plugins can be used to modify parameters transfered from browser to CartoWeb client. These parameters can be part of a POST request (HTML forms) or a GET request (URL query string).

Once a new filter plugin has been developed, it can be activated by adding it to the `loadPlugins` variable in file `client_conf/client.ini`.

#### 2.4.2.1. Interface and Classes

Interface FilterProvider declares following methods:

- `filterPostRequest(FilterRequestModifier $request)`: modifies parameters transfered via a POST request
- `filterGetRequest(FilterRequestModifier $request)`: modifies parameters transfered via a GET request

Class `FilterRequestModifier` is used to get old values from the request and set new ones. It implements two main methods:

- `getValue($key)`: retrieves old value
- `setValue($key, $value)`: sets new value

#### 2.4.2.2. Available Parameters

This is the list of global parameters accepted by CartoWeb via GET

- `reset_session` : This will reset all session parameters to default value. Useful when you want to go back to the initial stats of the application

          http://www.yoursite.org/cartoweb3/htdocs/client.php?reset_session

- `prevent_save_session` : This will bypass the session save. Useful when you want to performe an action without effect on the actual state of the application. This was added specifically when using the mode=image global parameter

          http://www.yoursite.org/cartoweb3/htdocs/client.php?prevent_save_session

- `mode` : This will define the kind of output of CartoWeb. By default it will be html. Another possible output is `image`. In this case, CartoWeb will only output an image.

          http://www.yoursite.org/cartoweb3/htdocs/client.php?mode=image

  The image mode can be used with other plugin specific parameters:

          http://www.yoursite.org/cartoweb3/htdocs/client.php?mode=image&recenter_x=300000&recenter_y=5150000&recenter_scale=150000

This is the list of parameters that can be set in a filter plugin using function `setValue()` (in php file) or GET request (URL query string):

- Images plugin
  - `mapsize` - ID of the selected map size (see Section 8.1, “ Client-side Configuration ”)
  - `customMapsize` - this string parameter enables to specify a customized map size using the syntax `[width]x[height]`, `[width]` and `[height]` being positive integers (size in pixels). You may specify dimensions limitations in `client_conf/images.ini` (see Section 8.1, “ Client-side Configuration ”)
  - `drawMainmap` - boolean indicating if the mainmap must be drawn or not.
  - `drawKeymap` - boolean indicating if the keymap must be drawn or not.
  - `drawScalebar` - boolean indicating if the scalebar must be drawn or not.
- Location plugin
  - `recenter_bbox` - new bounding box, comma-separated coordinates, eg. "10.5,20,15.5,28"
  - `recenter_x` - re-centering: new x-coordinate
  - `recenter_y` - re-centering: new y-coordinate
  - `show_crosshair` - '0' or '1', default '0'. If '1' display a crosshair on (recenter_x, recenter_y).
  - `recenter_scale` - new scale
  - `id_recenter_layer` - re-centering on objects: layer to look for IDs
  - `id_recenter_ids` - re-centering on objects: list of IDs, comma-separated
  - `shortcut_id` - ID of the selected map size (see Section 7.2, “Server-side Configuration”)
- Query plugin
  - `query_layer` - layer to look for IDs
  - `query_select` - IDs of objects to add to selection
  - `query_unselect` - IDs of objects to remove from selection
  - `query_policy` - selection policy: 'POLICY_XOR', 'POLICY_UNION' or 'POLICY_INTERSECTION', default is 'POLICY_XOR'
  - `query_maskmode` - '0' or '1', default is '0'. If '1', will show selection as a mask.
  - `query_hilight` - '0' or '1', default is '1'. If '0', won't shows selection hilighted.
  - `query_return_attributes` - '0' or '1', default is '1'. If '0', won't return attributes other than IDs.
  - `query_return_table` - '0' or '1', default is '1'. If '0', won't return any table results.
  - `query_clear` - '0' or '1', default is '0'. If '1', previous query results are purged even if they were marked as persistent.
- Layers plugin
  - `switch_id` - id of the switch to be used when the children switching is activated (see Section 6.2.4, “Children Switching”).
  - `layer_select` - comma-separated list of layers or layerGroups id's that must be added into the activated layers list.
  - `layer_unselect` - comma-separated list of layers or layerGroups id's that must be removed from the activated layers list. In some cases, layerGroups children might have to be explicitely listed to actually be removed.

### Warning

Note that for Query plugin, display of extended selection must be disabled in client's `query.ini` in order to use above parameters (see Section 9.1, “Client-side Configuration”).

### Tip

To make CartoWeb outputs a map as a raw image file (no HTML generated), you may use the GET parameter `mode=image`. For instance:

    http://example.com/cartoweb3/client.php?mode=image&mapsize=2

#### 2.4.2.3. Example

The following class implements a filter which allows to recenter on an object while hilighting it:

    class ClientFilterIdrecenter extends ClientPlugin
                                 implements FilterProvider {

        public function filterPostRequest(FilterRequestModifier $request) {}

        public function filterGetRequest(FilterRequestModifier $request) {

            $id = $request->getValue('id');
            if (!is_null($id)) {
                $layer = 'grid_classhilight';
                $request->setValue('query_layer', $layer);
                $request->setValue('query_maskmode', '1');
                $request->setValue('query_select', $id);

                $request->setValue('id_recenter_layer', $layer);
                $request->setValue('id_recenter_ids', $id);
            }
        }
    }

### 2.4.3. Tables

Tables plugin is responsible for table formatting and display.

#### 2.4.3.1. Tables Structures

Tables plugin declares several structures to help plugin developer manage tables. These structures are:

- Class `Table` which includes in particular a list of rows (class `TableRow`)
- Class `TableGroup` which includes in particular a list of tables. Table groups are used for instance to separate table results coming from several plugins
- Class `TableFlags` which defines parameters that will be useful for a plugin using tables

Typically, a plugin using table will include a `TableFlags` in its request and a `TableGroup` in its result. This is the case for Query plugin, which is the only core plugin which uses tables.

#### 2.4.3.2. Setting Rules

Tables plugin maintains an object called the registry (one on client and one on server). This object allows to add table rules, which will describes how tables must be displayed.

It is recommended to add rules in plugin's `initialize()` method, so they are ready at the earliest stage. To obtain the registry object, first you have to get the Tables plugin object.

On client:

    public function initialize() {

        $tablesPlugin = $this->cartoclient->getPluginManager()->tables;
        $registry = $tablesPlugin->getTableRulesRegistry();

        // Add rules here
    }

On server, plugin manager is stored in `ServerContext` object:

        // ...
        $tablesPlugin = $this->serverContext->getPluginManager()->tables;
        // ...

Now you are ready to add rules. Next sections describe the different types of rules. Registry's method signature is explained for each type.

Once rules have been added in registry, they must be executed on tables. See Section 2.4.3.4, “Executing Rules” for a description of table rules execution.

##### 2.4.3.2.1. Column Selector

    public function addColumnSelector($groupId, $tableId, $columnIds)

Column selector rules allow to keep only a subset of columns from the source table. Parameter `$columnIds` should contain an array of column IDs determining which columns to keep.

##### 2.4.3.2.2. Column Unselector

    public function addColumnUnselector($groupId, $tableId, $columnIds)

Column unselector rules allow to keep only a subset of columns from the source table, by removing a list of columns. Parameter `$columnIds` should contain an array of column IDs determining which columns to remove.

##### 2.4.3.2.3. Group Filter

    public function addGroupFilter($groupId, $callback)

Group filter rules allow to modify group title. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('group_id', 'group_title')
        return 'group_new_title'

##### 2.4.3.2.4. Table Filter

    public function addTableFilter($groupId, $tableId, $callback)

Table filter rules allow to modify table title. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('table_id', 'table_title')
        return 'table_new_title'

##### 2.4.3.2.5. Column Filter

    public function addColumnFilter($groupId, $tableId,
                                    $columnId, $callback)

Column filter rules allow to modify column title. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('table_id', 'column_id', 'column_title')
        return 'column_new_title'

##### 2.4.3.2.6. Cell Filter

    public function addCellFilter($groupId, $tableId, $columnId,
                                  $inputColumnIds, $callback)

Cell filter rules allow to modify content of a cell. Values of columns given in parameter `$inputColumnIds` will be transfered to the callback method for cell content calculation. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('table_id', 'column_id',
                                     array ('column_1' => 'value_1',
                                            'column_2' => 'value_2'))
        return 'cell_value'

### Note

To return all column's data to the callback function, you can use NULL.

            $registry->addCellFilter('query', 'table_name', '*', NULL,
                       array('TableRulesClassName', 'callbackFunctionName'));

This will apply the callbackFunctionName function to all column of table table_name and the callback function will receive an array containing the values of all columns.

##### 2.4.3.2.7. Cell Filter (Batch)

    public function addCellFilterBatch($groupId, $tableId, $columnId,
                                       $inputColumnIds, $callback)

Cell filter rules used in batch mode allow to modify content of all cells of a given column. Values of columns given in parameter `$inputColumnIds` will be transfered to the callback method for cells content calculation. Values for all rows are transfered at the same time. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('table_id', 'column_id',
                                     array (
                                     '0' => array (
                                            'column_1' => 'value_1_row_1',
                                            'column_2' => 'value_2_row_1'),
                                     '1' => array (
                                            'column_1' => 'value_1_row_2',
                                            'column_2' => 'value_2_row_2')))
        return array ('0' => 'cell_value_row_1', '1' => 'cell_value_row_2')

##### 2.4.3.2.8. Row Unselector

    public function addRowUnselector($groupId, $tableId,
                                     $columnId, $rowIds)

Row unselector rules allow to remove some rows from a table. Parameter `rowIds` contains IDs of row that must be removed.

##### 2.4.3.2.9. Row Selector

    public function addRowSelector($groupId, $tableId,
                                     $columnId, $rowIds)

Row selector rules allow to keep only some rows from a table. Parameter `rowIds` contains IDs of row that must be kept.

##### 2.4.3.2.10. ColumnAdder

    public function addColumnAdder($groupId, $tableId,
                                   $columnPosition, $newColumnIds,
                                   $inputColumnIds, $callback)

Column adder rules allow to add one or more columns to the table. Parameter `$newColumnIds` should contain the list of new column IDs. Values of columns given in parameter `$inputColumnIds` will be transfered to the callback method for cell content calculation. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('table_id',
                                     array ('column_1' => 'value_1',
                                            'column_2' => 'value_2'))
        return array ('new_column_1' => 'cell_value_1',
                      'new_column_2' => 'cell_value_2')

Parameter `$columnPosition` indicates where the new columns must be inserted. It should be an instance of class `ColumnPosition`. Positions can be absolute or relative, with a positive or negative offset:

-     $position = new ColumnPosition(ColumnPosition::TYPE_ABSOLUTE, 1);

  The new columns will be added after the first column

-     $position = new ColumnPosition(ColumnPosition::TYPE_ABSOLUTE, -2);

  The new columns will be added just before the last column

-     $position = new ColumnPosition(ColumnPosition::TYPE_RELATIVE,
                                     0, 'column_1');

  The new columns will be added just before column 'column_1'

-     $position = new ColumnPosition(ColumnPosition::TYPE_RELATIVE,
                                     1, 'column_1');

  The new columns will be added just after column 'column_1'

##### 2.4.3.2.11. Column Adder (Batch)

    public function addColumnAdderBatch($groupId, $tableId,
                                        $columnPosition, $newColumnIds,
                                        $inputColumnIds, $callback)

Column adder rules used in batch mode allow to add one or more columns to the table, while calculating values for all newly added cells. Parameter `$newColumnIds` should contain the list of new column IDs. Values of columns given in parameter `$inputColumnIds` will be transfered to the callback method for cells content calculation. Values for all rows are transfered at the same time. Parameter `$callback` should contain a pointer to a callback method with the following signature:

    static function myCallbackMethod('table_id',
                                     array (
                                     '0' => array (
                                            'column_1' => 'value_1_row_1',
                                            'column_2' => 'value_2_row_1'),
                                     '1' => array (
                                            'column_1' => 'value_1_row_2',
                                            'column_2' => 'value_2_row_2')))
        return array (
               '0' => array (
                      'new_column_1' => 'cell_value_1_row_1',
                      'new_column_2' => 'cell_value_2_row_1'),
               '1' => array (
                      'new_column_1' => 'cell_value_1_row_2',
                      'new_column_2' => 'cell_value_2_row_2')))

See Section 2.4.3.2.10, “ColumnAdder” to know more about parameter `$columnPosition`.

##### 2.4.3.2.12. Column reorder

    public function addColumnReorder($groupId, $tableId, $columnIds)

Column reorder rule allow you to reorder the columns and their contents. Parameter `$columnIds` should contain an array of column IDs given the new column's order. Note that all the IDs must appear in `$columnIds` even they don't move.

#### 2.4.3.3. Precedence of Rules

Depending on rule type, rules are set for a group, a table or a column. Parameters (`$groupId`, `$tableId` or `$columnId`) can point to one object or to a group of object, using wildcard '*':

- 'column_1': rule will be executed on columns called 'column_1' only
- 'col*': rule will be executed on columns with name starting with 'col'
- '*': rule will be executed on any columns

For instance, following rule may be executed on groups with name starting with 'myGr', tables called 'myTable' and all columns:

    $registry->addColumnFilter('myGr*', 'myTable', '*',
                                         array($this, 'myCallbackMethod));

Only one rule of each type may be executed on one item. If two or more rules apply, most specific rule will be choosen. In the following rule definition, only the third rule will be executed on a table 'myTable' in a group 'myGroup':

    $registry->addColumnSelector('*', '*', array('column_1', 'column_2'));
    $registry->addColumnSelector('myGr*', '*', array('column_1'));
    $registry->addColumnSelector('myGr*', 'myTable', array('column_2'));
    $registry->addColumnSelector('myGroup', 'toto', array('column_3'));

#### 2.4.3.4. Executing Rules

##### 2.4.3.4.1. On Client

Each time a table group is created, it must be stored in Tables plugin in order to display it:

    $tablesPlugin = $this->cartoclient->getPluginManager()->tables;
    $tablesPlugin->addTableGroups($newTableGroup);

Tables rules are executed automatically at the same time.

##### 2.4.3.4.2. On Server

Rules execution must be done explicitly on server. A call to Tables plugin `applyRules` method is needed for each new table group before returning it to client:

    $tablesPlugin = $this->serverContext->getPluginManager()->tables;
    readyForClientTableGroups = $tablesPlugin->applyRules($newTableGroup);