ModulesEditor - Mini-IT/SnipeWiki GitHub Wiki
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.
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();
}
}
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.
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. |
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.
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.
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.
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
}
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]'
}
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');
Sometimes you need to modify the contents of core modules. The core API provides three methods for this.
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.
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.
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.