ModulesEditor - Mini-IT/SnipeWiki GitHub Wiki

Editor Modules

Overview

Most of the code written for the editor (like for the server) is inside of the editor modules. Modules have access to all of the editor low-level functionality like sending notifications to server or making SQL queries into database.

The module functionality description lies in its exports. Exports, are structures describing handlers for client requests based on the request type (called editor action). An export can hold some webpage blocks to render, a link to an SQL-based query like INSERT or UPDATE or a link to a method handler.

Creating a module

The first thing you need to do is create a module class. It should extend the parametric ModuleEdit class supplying your editor class as a parameter. Nothing is required beyond that. Here is the minimal example:

package modules;

class Test extends snipe.edit.ModuleEdit<ServerTest>
{
  public function new(s: ServerTest)
    {
      super(s);
      name = "test";
      exports = [
        ];
    }
}

Note the module name. All client requests are routed to this module using its name. Each client request has to have an action that is used to find an export from "exports" array of that module and do the work described there.

The next thing you need to do is make an editor application class. The editor application is more simple in nature than the server one so there are no additional steps for initialization beyond adding the module class to an array.

Example editor application class:

import snipe.edit.EditServer;

class ServerTest extends EditServer
{
  public function new()
    {
      super();
      moduleClasses = [ 
        modules.Test,
        ];
    }


  static var inst: ServerTest;
  static function main() 
    {
      inst = new ServerTest();
      inst.init();
    }
}

Module exports

All requests by the client are handled by module exports. The module export type is defined in _ModuleExport. All of its fields are optional and some are grouped. The exports defined in "exports" module property should have "action" field defined. This will be used as an ID to route the client request to.

The HTTP request variable "a" holds the action name. For example, to use the export with an action "test.test", you will need to call the editor URL like this: "https://your-editor.com/index.n?a=test.test".

There are four main types of exports: webpage, SQL query, file upload and direct method call. Some export parameters are common for all types, some are specific for a single type.

Common parameters

Field Description
action Editor action ID in "<module>.<action>" format.
back Sets the back link. The behavior changes whether the export contains page blocks. If it does, setting this will output a "Back" link at the end of the page (HTML.back()). If it does not, it will redirect the browser to the given link (HTML.goBack()).
isDisabledInProduction If enabled, this export is disabled in production. The production status is controlled by server.isProduction configuration variable.
noHeaders If enabled, it disables page headers and footers for this export.
reload If set, it will send reload the notification to the cache server after handling the request. If the export also has UPDATE or INSERT query, the reload notification will be sent with last row ID as parameter. Notifications are described in detail in [[Notifications
title Page title.

Page display mode

Field Description
blocks Page blocks list.
simpleForms If enabled, all forms in this export are simplified (no jQuery usage). This can be needed when the forms that contain drop-down lists with hundreds of items.

The page blocks are described in the Page Blocks article.

SQL query mode

Field Description
table Database table name. Used with INSERT, UPDATE or DELETE actions.
insert If set, the editor will execute the INSERT SQL query with given parameters. Parameter values are taken from the request variables.
insertParams If set, the editor will execute the INSERT SQL query with "params" field set to JSON encoded string of the anonymous object with given parameters. Parameter values are taken from request variables.
update If set, the editor will execute the UPDATE SQL query with given parameters. Parameter values are taken from the request variables.
updateParams If set, the editor will execute the UPDATE SQL query with "params" field set to JSON encoded string of the anonymous object with given parameters. Parameter values are taken from the request variables.
delete If enabled, the editor will execute the DELETE SQL query with row ID taken from id request variable.

INSERT SQL query usage example (taken from quest chains module and modified):

{
  action: "core/chain.add",
  table: "QuestChains",
  insert: [ 'name' ],
  insertParams: [ 'checkbox:isDaily', 'checkbox:isRepeatable' ]
}

This module export will insert a record into "QuestChains" database table with fields "name" and "params" set. The "params" field will contain something like this: {"isDaily":true,"isRepeatable":false}.

Note the "checkbox:isDaily" parameter. This parameter is typed and uses the request parameter parsing system. You can read more about it below in the "Parsing request parameters" section.

UPDATE SQL query usage example (taken from quest chains module and modified):

{
  action: "core/chain.update",
  table: "QuestChains",
  update: [ 'name', 'parentid', 'tags', 'checkbox:isenabled' ],
  updateParams: [ 'checkbox:isDaily' ] 
}

This example will execute an UPDATE SQL query on the "QuestChains" database table row with an ID given in HTTP request parameters. The row fields that will be updated are "name", "parentid", "tags", "isenabled" and "params". The "params" field will contain something like this: {"isDaily":true}.

DELETE SQL query usage example (taken from quest chains module and modified):

{
  action: "core/chain.del",
  table: "QuestChains",
  delete: true
}

Both UPDATE and DELETE SQL queries take table row ID from "id" request parameter automatically, and show an error if it's not found.

Uploading files

Field Description
upload If set, the editor will try to upload a file and save it with the given name. The file path is relative to neko.Web.getCwd()).

Usage example (this is only the actual uploading part, you will also need an upload form):

{
  action: "core/item.upload",
  upload: "client/items/[id].swf",
  back: "core/item.edit&id=[id]"
}

Note the [id] usage in strings. Parsing the strings is described below but the short explanation is that this will try to find HTTP request parameter called id and replace the marker in the string with it.

Direct method call mode

Field Description
isMethod If enabled, this export will call the module method specified in action.

Usage example:

{
  action: "core/item.add",
  isMethod: true
}
  function add(vars: Vars)
    {
      // logic
    }

Parsing strings

Some of the string fields in module exports support adding HTTP request parameters into them. For that you can use the [<parameter name>] tag. Page blocks string fields also use this feature extensively. If you want to manually parse a string, you can do it with the ModuleManager.parse() method.

Usage example:

{
  action: "core/calendar.updateStage&id=[id]&stageID=[stageID]",
  back: 'core/calendar.edit&id=[id]'
}

Parsing request parameters

Any HTTP request handler will need to attain the request parameters. Since they are strings by default, you will need to convert them to appropriate classes. The Vars class has everything you might need, and a couple of useful methods that might require some explanation.

The first of these useful methods is Vars.getTyped(). It accepts a single parameter: a string declaring the parameter type and name in <parameter type>:<parameter name> format. It returns an object that contains both the name and value of the parameter.

Usage:

  // int value
  vars.getTyped('test');
  vars.getTyped('int:test');

  // float value
  vars.getTyped('float:test');

  // JSON encoded string, returns anonymous object
  vars.getTyped('json:test');

  // checkbox, returns boolean 
  vars.getTyped('checkbox:test');

The other useful method is Vars.getTypedSQL(). It accepts the same parameter: a string declaring the parameter type and name in <parameter type>:<parameter name> format. It returns the same object that contains both the name and value of the parameter. The difference is that the value returned is a quoted string ready for insertion into SQL queries.

Usage:

  // value without any manipulation
  vars.getTypedSQL('test');

  // int value without any manipulation
  vars.getTypedSQL('int:test');

  // checkbox, convert value into SQL boolean string (`'t'` or `'f'`)
  vars.getTypedSQL('checkbox:test');

  // date and time, convert date/time string DD.MM.YYYY HH:MM -> YYYY-MM-DD HH:MM
  vars.getTypedSQL('datetime:test');

  // current date and time (`now()`)
  vars.getTypedSQL('now:test');

Module extensions

Sometimes you need to modify the contents of core modules. The core API provides three methods for this.

Registering function to run after module export execution

The first method is ModuleManager.registerActionPost(). It allows executing of a function after the handling of module export. The function will be passed as the same HTTP request parameters object that the export received. The method should be called in module constructor.

Usage example:

  public function new(s: ServerTest)
    {
      // ...

      server.moduleManager.registerActionPost('core/ua.update', updateAttributePost);
    }


  function updateAttributePost(vars: Vars)
    {
      // logic
    }

This example will register the function to call after any user attribute that was updated in the user editor.

Adding new blocks to export

The next method is ModuleManager.registerBlocks(). It allows adding page blocks to module export. If you want to add these blocks to specific positions in page layout, you can set the position property of each block to the needed value. The page blocks that are contained in the module export blocks field, are numbered from 0 forward by default. Set the block position to 0, if you want to add it to the top.

You can, of course, always do this manually using the ModuleManager.getModuleExport() and directly changing the structure instead but that is not recommended.

Usage example:

  public function new(s: ServerTest)
    {
      // ...

      server.moduleManager.registerBlocks('core/user.edit', [
        {
          htmlPre: '<h3>Pets</h3>',
          position: 20,
          type: BLOCK_FORM,
          title: "Add pet",
          action: "pet.add&userid=[id]",
          inputs: [
            { title: "Name", name: "petid", type: "select",
              query: "SELECT ID AS Value, Name " +
                "FROM Pets ORDER BY Name" },
          ]
        },

        {
          position: 20,
          type: BLOCK_FUNC,
          func: listPets
        },
        ]);
    }

This example will add a form to add a pet and a list of pets to the end of user edit page.

Replacing a block in module export

The third method is ModuleManager.replaceBlockByAction(). As you've probably guessed, it replaces the page block in the module export using its action ID.

Usage example:

  public function new(s: ServerTest)
    {
      // ...

      server.moduleManager.replaceBlockByAction('core/user.edit',
        'core/inventory.add',
        {
          htmlPre: "<h3>Inventory</h3>",
          type: BLOCK_FORM,
          action: "core/inventory.add&userid=[id]",
          inputs: [
            { title: "Item", name: "itemid", type: "select",
              query: "SELECT ID AS Value, Name " +
                "FROM Items WHERE GroupID = 23 ORDER BY Name" },
            { title: "Amount", name: "amount" },
          ]
        });
      }

This example replaces the form for adding items to user inventory with a more limited form that only allows items from a specific group to be added.

⚠️ **GitHub.com Fallback** ⚠️