Modules
Here you'll find everything needed to start developing your first module, from structure to update routine & packaging.
What is a module ?
Quick start: copy module-skeleton, a small working module (one table, one editor form, one mobile page, and
tools/pack.shto build the zip), then rename it. The Job module in the core is a full-sized example.
Modules are the core of SiberianCMS, they run frontend, backend, features & even more.
A module can be a single front page with new features or settings, or a complete feature for SiberianCMS to be used in your Mobile apps and managed in your App editor.
Here you can find a module skeleton to start with Siberian module Skeleton, Job
Modules are installed in the app/local/modules folder, for more information about the inheritance see this page module/core-inheritance
Structure
Folder structure
ModuleName
├─ Controller
│ └─ Default.php /** If you need to override the default controller Classes */
├─ controllers
│ ├─ Backoffice
│ │ └─ ModuleNameController.php
│ └─ Mobile
│ └─ ModuleNameController.php
├─ features
│ └─ modulename
│ ├─ feature.json
│ ├─ assets
│ │ └─ templates // Ionic templates, copied to the apps
│ ├─ icons
│ ├─ js
│ └─ scss
├─ init.php // see "Init" in Basic usage
├─ Form
├─ Model
├─ View
├─ resources
│ ├─ db
│ │ ├─ data
│ │ └─ schema
│ ├─ media
│ │ └─ library
│ ├─ translations
│ │ ├─ default
│ │ │ └─ mymodule.po
│ │ ├─ en
│ │ └─ [...]
│ └─ design
└─ package.json
Basics
package.json
The package.json is used by the Installer to know the requirements, and routines to run during the installation/update process
type is one of module, layout, icons or template; dependencies.system.type is SAE, MAE or PE, and the install is refused if the running edition or version is lower than required.
{
"name": "ModuleName",
"description": "Module description",
"type": "module",
"version": "1.0",
"dependencies": {
"system": {
"type": "SAE",
"version": "5.1.0"
},
"modules": {
"OtherModule": "2.1.0"
}
}
}
resources/db/schema
We use resources/db/schema/table_name.php to describe the table schema; below an example of a basic table.
This file(s) reflects your database at it's latest version, each time you update your module, the local schema is compared against this file and updating
Note: the schema only add fields if they are new or missing, fields are never removed.
Only files named table_name.php are read from this folder (the file name, without .php, is the table name). Any other file, such as an install.sql, a .DS_Store, a backup copy or a sub folder, is ignored; files named *.ec.php (encrypted modules) are skipped as well. Older versions created such a stray file as a table of that name, and the install failed with Unable to create table 'install.sql'.
<?php
/**
* Schema definition for "table_name"
*/
$schemas = (!isset($schemas)) ? [] : $schemas;
$schemas["table_name"] = [
"mytable_id" => [
"type" => "int(11) unsigned",
"auto_increment" => true,
"primary" => true,
],
"admin_id" => [
"type" => "int(11) unsigned",
"is_null" => true,
"foreign_key" => [
"table" => "admin",
"column" => "admin_id",
"name" => "FK_MY_MODULE_ADMIN_ID_ADMIN_AMIN_ID", // Must be unique!
"on_update" => "CASCADE",
"on_delete" => "CASCADE",
],
],
"name" => [
"type" => "varchar(50)",
"default" => "default_value",
"index" => [
"key_name" => "IDX_MYTABLE_NAME",
"index_type" => "BTREE",
"is_null" => false,
"is_unique" => false,
],
],
"created_at" => [
"type" => "datetime",
],
"updated_at" => [
"type" => "datetime",
],
];
List of available options
| Key | Type | Usage, values, comment |
|---|---|---|
| type | String | int(11) unsigned, varchar(50), datetime, etc ... |
| auto_increment | Boolean | The column is AUTO_INCREMENT. Only the presence of the key is checked, so leave it out instead of setting it to false |
| primary | Boolean | If multiple columns are defined as primary they will be part of a composite primary key. Only the presence of the key is checked |
| is_null | Boolean | The column accepts NULL (DEFAULT NULL when there is no default). Only the presence of the key is checked, so leave it out instead of setting it to false |
| unique | Boolean | The column is UNIQUE |
| default | mixed | Default value of the column |
| collation | String | Collation of the column, ex: utf8mb4_unicode_ci |
| foreign_key | Array() | See the Foreign key options below |
| index | Array() | See the Index options below. It must be an array: true is not valid (PHP 8 throws a TypeError when the column is added to an existing table) |
Foreign Key options
| Key | Type | Usage, values, comment |
|---|---|---|
| table | String | Referenced table |
| column | String | Referenced column in table |
| name | String | Foreign key name (ex: FK_TABLENAME_KEYNAME_REFTABLENAME_REFKEYNAME) |
| on_update | String | NO ACTION, SET NULL, CASCADE, RESTRICT |
| on_delete | String | NO ACTION, SET NULL, CASCADE, RESTRICT |
Index options
| Key | Type | Usage, values, comment |
|---|---|---|
| key_name | String | Index name, use the same on multiple columns for composite indexes |
| index_type | String | BTREE, HASH |
| is_null | Boolean | |
| is_unique | Boolean |
resources/db/data
This folder is used to insert default values when installing, or updating a Module.
The php files of this folder are executed when installing and/or updating the module, they should reflect the required data as the latest version. Installing or updating the same version again must be safe: write the data as upserts (insertOrUpdate(), a lookup before an insert), never as a blind insert.
Which files run:
| File name | Behaviour |
|---|---|
anything.php |
Executed on every install and update |
*install.php |
Backward compatibility for old modules: executed only the first time the module is installed |
*.ec.php |
Never executed (encrypted modules) |
A name whose last character before .php is a digit or a dot |
Never executed. This is the old 1.2.3.php version style, and it is detected with a pattern (/.*([0-9\.]+)\.php$/), so feature1.php or step2.php are skipped as well |
Never name a file install.php, *install.php or %VERSION%.php (%VERSION% is a version string), and do not end a file name with a digit.
Best practice:
-
If your module is about to install multiple features, split them into multiple files named with letters only,
- ex:
features_core.php,features_extra.php, etc... (notfeature1.php,feature2.php)
- ex:
features/modulename/feature.json
This file is the key for every In app features. Every features/*/feature.json of a module is read by the Installer, the js/scss/css listed in files are minified into one lazy-loaded bundle (features/<code>/<code>.bundle.min.js) and the assets folder is copied to the apps.
{
"name": "ModuleName",
"code": "modulename",
"version": "1.0.0",
"category": "contact",
"model": "ModuleName_Model_MainClass",
"desktop_uri": "modulename/application/",
"routes": [
{
"root": true,
"state": "modulename-home",
"controller": "ModuleNameHome",
"url": "modulename/mobile_home/index/value_id/:value_id",
"template": "l1/home.html",
"cache": false
},
{
"state": "modulename-view",
"controller": "ModuleNameView",
"url": "modulename/mobile_view/index/value_id/:value_id/item_id/:item_id",
"template": "l1/view.html",
"cache": false
}
],
"layouts": [
1
],
"icons": [
"icons/modulename1-flat.png",
"icons/modulename2-flat.png",
"icons/modulename3-flat.png"
],
"files": [
"js/services/modulename.js",
"js/factory/modulename.js",
"js/controllers/modulename.js",
"scss/modulename.scss"
],
"compile": true,
"use_account": true,
"only_once": true,
"load_on_start": false,
"on_start_factory": null
}
All paths in icons and files are relative to the folder of the feature.json. A route template is relative to features/modulename/assets/templates/ (here assets/templates/l1/home.html).
List of options
| Key | Type | Usage, values, comment |
|---|---|---|
| name | String | Your feature name |
| code | String | This code is unique to your Feature, Module |
| category | String | social, media, contact, monetization, customization, integration, events, misc, or _service for a service module (no option is created in the editor) |
| version | String | Not used here see package.json for the version |
| model | String | Default model class used in the editor |
| desktop_uri | String | Default controller class uri used in the editor |
| routes | Array | Ionic states of the feature, see Route options. The route with "root": true is the entry point of the feature |
| icons | Array | Icons of the feature, relative to the feature.json folder |
| files | Array | js, scss and css files bundled with the feature (a file path containing .. is ignored) |
| layouts | Array | Layouts offered for the feature in the editor |
| compile | Boolean | Leave true by default |
| use_account | Boolean | If the feature requires user to login set true |
| only_once | Boolean | Whether an app may have this feature only once, or more |
| is_ajax | Boolean | Defaults to true |
| load_on_start | Boolean | If the feature must be loaded right after the Application starts |
| on_start_factory | String | When load_on_start is set to true, Factory.onStart function will be executed, where Factory is the factory class name |
| open_callback_class, lazy_load | String | Angular factory whose openCallback(feature) is called when the feature is opened instead of a route, and the comma separated bundles to load before |
| social_sharing, use_nickname, use_ranking, use_civility, use_mobile, use_birthdate, use_critical_push | Boolean | Feature capabilities, all default to false |
| position, custom_fields | Integer, Array | Optional, position of the feature in its category, extra option fields |
name, code, model, desktop_uri, routes and icons are mandatory: a feature.json missing one of them is silently ignored.
Route options
| Key | Type | Usage, values, comment |
|---|---|---|
| state | String | Ionic state name |
| url | String | Appended to BASE_PATH |
| controller | String | Angular controller |
| template | String | Template path, relative to assets/templates/ |
| templateHTML | String | Inline template, used instead of template |
| layouts | Object | Template folder per layout id (plus a default), used with template |
| externalTemplate | Boolean | template is a full path, not relative to assets/templates/ |
| root | Boolean | Entry point of the feature |
| cache | Boolean | View cache, enabled unless set to false |
| autoregister | Boolean | Set to false to not register the state automatically |
Translations
Siberian uses .po files, which are Gettext source files.
First create a new file named mymodule.po then place this file in the translations/default folder
If you want to ship your module with translations, place files in directories named with the locale code, example: english will be in translations/en/mymodule.po
Below as an example the contact.po file.
Be sure to not use existing filename which are located in SIBERIANCMS_ROOT/languages/base otherwise your translations won't work.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2019-03-28T15:24:21+01:00\n"
"PO-Revision-Date: 2019-03-28T15:24:21+01:00\n"
"Language: \n"
msgctxt "contact"
msgid "Cover image"
msgstr "Illustration"
msgctxt "contact"
msgid "Name"
msgstr "Nom"
#, mobile
msgctxt "contact"
msgid "Facebook"
msgstr "Facebook"
#, mobile
msgctxt "contact"
msgid "Twitter"
msgstr "Twitter"
#, mobile
msgctxt "contact"
msgid "Website"
msgstr "Site web"
msgctxt "contact"
msgid "String to translate"
msgstr "Phrase à traduire"
msgctxt "contact"
msgid "Hello, I'm %s"
msgstr "Bonjour je suis %s"
List of options
| Key | Type | Usage, values, comment |
|---|---|---|
| #, mobile | String | Add this line if the string is translated inside the Mobile app |
| msgctxt | String | This is you context key, generally it's the module code |
| msgid | String | The original string, used as a key |
| msgstr | String | In default folder, msgstr = msgid, in translation files, msgstr is the translated sentence |
To have the strings of your module extracted into your default file, register it in your init.php and set $_config["extract"] = true; in config.php while developing (see Job/init.php).
<?php
use Siberian\Translation;
$init = function ($bootstrap) {
Translation::registerExtractor("mymodule", "MyModule", path("/app/local/modules/MyModule/resources/translations/default/mymodule.po"));
};
How to translate
Inside editor & backoffice
<?php
// Simple translation
echo p__("context_key", "String to translate");
// Will render [en]"String to translate" , [fr]"Phrase à traduire", as per the .po file below
// sprintf translation
$userName = "John";
echo p__("context_key", "Hello, I'm %s", $userName);
// Will render [en]"Hello, I'm John", [fr]"Bonjour je suis John", as per the .po file below
Inside mobile apps, note key & context are swapped compared to the php function
// From the controllers, include $translate factory
$translate.instant("Hello, I'm %s", "context_key");
// From any template
<div class="item">
{{ "Hello, I'm %s" | translate:"context_key" }}
</div>
Design
Only the flat design is shipped today (design_code() always returns flat).
Everything you need to organize, style & interact with your Feature is located in the design folder
ModuleName
├─ [...]
├─ resources
│ └─ design
│ ├─ desktop
│ │ └─ flat
│ │ ├─ css
│ │ │ └─ modulename.css
│ │ ├─ js
│ │ │ └─ modulename.js
│ │ ├─ images
│ │ │ └─ customization
│ │ │ └─ layout
│ │ │ └─ modulename
│ │ │ ├─ layout-1.png
│ │ │ └─ layout-2.png
│ │ ├─ layout
│ │ │ └─ modulename.xml
│ │ └─ template
│ │ ├─ company
│ │ │ └─ [...]
│ │ └─ modulename
│ │ ├─ index.phtml
│ │ └─ application
│ │ ├─ edit.css
│ │ ├─ edit.js
│ │ └─ edit.phtml
│ └─ email
│ ├─ layout
│ └─ template
└─ [...]
modulename.xml example
This is the minimum required information in the default layout.xml, the file should be named as the module itself, lower-cased so job.xml in our case.
<?xml version="1.0" encoding="UTF-8"?>
<layout>
<modulename_application_edit>
<views>
<content class="application_view_customization_features_edit_tabbareditor"
template="application/customization/features/edit/tabbar_editor.phtml" />
<content_editor class="core_view_default"
template="modulename/application/edit.phtml" />
</views>
</modulename_application_edit>
</layout>
The tag name is the request path with underscores: modulename_application_edit is the action edit of the application controller of the modulename module (see Job/resources/design/desktop/flat/layout/job.xml).
The section content is the default for the feature editor tab, you should not need to change it.
The section content_editor should point to the feature editor template
here: the short path modulename/application/edit.phtml referring to ModuleName/resources/design/template/modulename/application/edit.phtml
- Use only the short path.
Cache
While developing a module you will have to manually rebuild what we call design cache
You can do so with the Siberian cli by running ./cli cache:design sae (replace sae with your edition, sae|mae|pe)
Note: when installing and/or updating a module with the regular zip package, the cache is automatically cleared for the users.
Background images
Editor side
In the file edit.phtml which handles the Editor UI for your module, you must add a background section like below.
<div class="background-images-import">
<?php echo $this->importBackground($option_value); ?>
</div>
<script type="text/javascript">
$(document).ready(function () {
// Bind form, only if the current form is not binded from a parent (this could bind it twice)
bindForms('.background-images-import');
});
</script>
This is as easy as the code below, as everything is linked to the feature, and cropped/saved automatically for you.
Application / Ionic
Then to be sure your background images inside your Application pages, you must ensure all views & lists your
<ion-view sb-page-background>
<ion-content>
{{ "My Awesome Content!" |translate:"module_name" }}
</ion-content>
</ion-view>
As the old directive sb-background-image is deprecated/empty it will not affect anymore your features.
Package
When you are done with your module, it's time to pack !
zip everything at the root of your module and you're done !
ModuleName.zip
├─ Controller
├─ controllers
│ └─[...]
├─ init.php
├─ features
├─ Model
├─ View
├─ resources
│ └─[...]
└─ package.json
Disable a Module
While in production modules can't be uninstalled, but can be disabled in Backoffice > Settings > Advanced > Modules
