Skip to content

Commit

Permalink
Improve plugins documentation; closes #3
Browse files Browse the repository at this point in the history
  • Loading branch information
trasher committed Feb 28, 2017
1 parent 1102d8f commit a96b8ab
Show file tree
Hide file tree
Showing 8 changed files with 500 additions and 4 deletions.
2 changes: 2 additions & 0 deletions source/devapi/dbmodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ What we can see here:

As stated in the above note, this is far from complete; but this is quite representative of the whole database schema.

.. _dbnaming_conventions:

Naming conventions
^^^^^^^^^^^^^^^^^^

Expand Down
130 changes: 130 additions & 0 deletions source/plugins/database.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
Database
--------

.. warning::

A plugin should **never** change core's database! It just add its own tables to manage its own data.

Of course, plugins relies on :doc:`GLPI database model <../devapi/dbmodel>` and must therefore respect :ref:`database naming conventions <dbnaming_conventions>`.

Creating, updating or removing tables is done by the plugin, at installation, update or uninstallation; functions added in the ``hook.php`` file will be used for that; and you will rely on the `Migration class <https://forge.glpi-project.org/apidoc/class-Migration.html>`_ provided from GLPI core. Please refer to this documentation do know more about various `Migration` possibilities.

Creating and updating tables
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Creating and updating tables must be done in the plugin installation process. You will add the required code to the ``plugin_{myplugin}_install``. As the same fucntion is used for both installation and update, you'll have to make tests to know what to do.

For example, we will create a basic table to store some configuration for our plugin:

.. code-block:: php
<?php
/**
* Install hook
*
* @return boolean
*/
function plugin_myexample_install() {
global $DB;
//instanciate migration with version
$migration = new Migration(100);
//Create table only if it does not exists yet!
if (!TableExists('glpi_plugin_myexample_configs')) {
//table creation query
$query = "CREATE TABLE `glpi_plugin_myexample_config` (
`id` INT(11) NOT NULL autoincrement,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->queryOrDie($query, $DB->error());
}
//execute the whole migration
$migration->executeMigration();
return true;
}
The update part is quite the same. Considering our previous example, we missed to add a field in the configuration table to store the config value; and we should add an index on the ``name`` column. The code will become:

.. code-block:: php
<?php
/**
* Install hook
*
* @return boolean
*/
function plugin_myexample_install() {
global $DB;
//instanciate migration with version
$migration = new Migration(100);
//Create table only if it does not exists yet!
if (!TableExists('glpi_plugin_myexample_configs')) {
//table creation query
$query = "CREATE TABLE `glpi_plugin_myexample_configs` (
`id` INT(11) NOT NULL autoincrement,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->queryOrDie($query, $DB->error());
}
if (TableExists('glpi_plugin_myexample_configs')) {
//missed value for configuration
$migration->addField(
'glpi_plugin_myexample_configs',
'value',
'string'
);
$migration->addKey(
'glpi_plugin_myexample_configs',
'name'
);
}
//execute the whole migration
$migration->executeMigration();
return true;
}
Of course, we can also add or remove tables in our upgrade process, drop fields, keys, ... Well, do just what you need to do :-)

Deleting tables
^^^^^^^^^^^^^^^

You will have to drop all plugins tables when it will be uninstalled. Just put your code into the ``plugin_{myplugin]_uninstall`` function:

.. code-block:: php
<?php
/**
* Uninstall hook
*
* @return boolean
*/
function plugin_myexample_uninstall() {
$tables = [
'configs'
];
foreach ($tables as $table) {
$tablename = 'glpi_plugin_myexample_' . $table;
//Create table only if it does not exists yet!
if (TableExists($tablename)) {
$DB->queryOrDie(
"DROP TABLE `$tablename`",
$DB->error()
);
}
}
return true;
}
17 changes: 16 additions & 1 deletion source/plugins/guidelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ Guidelines
Directories structure
^^^^^^^^^^^^^^^^^^^^^

Real structure will depend of what your plugin propose. See :doc:`requirements <requirements>` to find out what is needed.
Real structure will depend of what your plugin propose. See :doc:`requirements <requirements>` to find out what is needed. You may also want to :ref:`take a look at GLPI File Hierarchy Standard <fhs>`.

.. warning::

The main directory name of your plugin may contain only alphanumeric characters (no ``-`` or ``_`` or accentued characters or else).

The plugin directory structure should look like the following:

Expand Down Expand Up @@ -46,6 +50,17 @@ The plugin directory structure should look like the following:
* `MyPlugin.xml` and `MyPlugin.png` can be used to reference your plugin on the `plugins directory website <http://plugins.glpi-project.org>`_,
* the required `setup.php` and `hook.php` files.

Where to write files?
+++++++++++++++++++++

.. warning::

Plugins my never ask user to give them write access on their own directory!

The GLPI installation already ask for administrator to get write access on its ``files`` directory; just use ``GLPI_PLUGIN_DOC_DIR/{plugin_name}`` (that would resolve to ``glpi_dir/files/_plugins/{plugin_name}`` in default basic installations).

Make sure to create the plugin directory at install time, and to remove it on uninstall.

Versionning
^^^^^^^^^^^

Expand Down
11 changes: 10 additions & 1 deletion source/plugins/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@ Plugins

GLPI provides facilities to develop plugins, and there are many `plugins that have been already published <http://plugins.glpi-project.org>`_.

.. note::

Plugins are designed to add features to GLPI core.

This is a sub-directory in the ``plugins`` of GLPI; that would contains all related files.

Generally speaking, there is really a few things you have to do in order to get a plugin working; many considerations are up to you. Anyways, this guide will provide you some guidelines to get a plugins repository as consistent as possible :)

If you want to see more advanced examples of what it is possible to do with plugins, you can take a look at the `example plugin source code <http://github.com/pluginsGLPI/example/>`_.

.. toctree::
:maxdepth: 2

requirements
guidelines
requirements
database
objects
hooks
tips
167 changes: 167 additions & 0 deletions source/plugins/objects.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
Adding and managing objects
---------------------------

In most of the cases; your plugin will have to manage several objects

Define an object
++++++++++++++++

Objects definitions will be stored into the ``inc/`` directory of your plugin. File name will be the name of your class, lowercased; the class name will be the concatenation of your plugin name and your class name.

For example, if you want to create the ``MyObject`` in ``MyExamplePlugin``; you will create the ``inc/myobject.class.php`` file; and the class name will be ``MyExamplePluginMyObject``.

Your object will extends one of the :doc:`common core types <../devapi/mainobjects>` (``CommonDBTM`` in our example).

Extra operations are aslo described in the :doc:`tips and tricks page <tips>`, you may want to take a look at it.

Add a front for my object (CRUD)
++++++++++++++++++++++++++++++++

The goal is to build CRUD (Create, Read, Update, Delete) and list views for your object.

You will need:

* a class for your object (``inc/myobject.class.php``),
* a front file to handle display (``front/myobject.php``),
* a front file to handle form display (``front/myobject.form.php``).

First, create the ``inc/myobject.class.php`` file that looks like:

.. code-block:: php
<?php
class PluginMyExampleMyObject extends CommonDBTM {
public function showForm($ID, $options = []) {
global $CFG_GLPI;
$this->initForm($ID, $options);
$this->showFormHeader($options);
if (!isset($options['display'])) {
//display per default
$options['display'] = true;
}
$params = $options;
//do not display called elements per default; they'll be displayed or returned here
$params['display'] = false;
$out = '<tr>';
$out .= '<th>' . __('My label', 'myexampleplugin') . '</th>'
$objectName = autoName(
$this->fields["name"],
"name",
(isset($options['withtemplate']) && $options['withtemplate']==2),
$this->getType(),
$this->fields["entities_id"]
);
$out .= '<td>';
$out .= Html::autocompletionTextField(
$this,
'name',
[
'value' => $objectName,
'display' => false
]
);
$out .= '</td>';
$out .= $this->showFormButtons($params);
if ($options['display'] == true) {
echo $out;
} else {
return $out;
}
}
}
The ``inc/myobject.php`` file will be in charge to list objects. It should look like:

.. code-block:: php
<?php
include ("../../../inc/includes.php");
// Check if plugin is activated...
$plugin = new Plugin();
if (!$plugin->isInstalled('myexampleplugin') || !$plugin->isActivated('myexampleplugin')) {
Html::displayNotFoundError();
}
//check for ACLs
if (PluginMyExampleMyObject::canView()) {
//View is granted: display the list.
//Add page header
Html::header(
__('My example plugin', 'myexampleplugin'),
$_SERVER['PHP_SELF'],,
'assets',
'pluginmyexamplemyobject',
'myobject'
);
Search::show('PluginMyExampleMyObject');
Html::footer();
} else {
//View is not granted.
Html::displayRightError();
}
And finally, the ``front/myobject.form.php`` will be in charge of CRUD operations:

.. code-block:: php
<?php
include ("../../../inc/includes.php");
// Check if plugin is activated...
$plugin = new Plugin();
if (!$plugin->isInstalled('myexampleplugin') || !$plugin->isActivated('myexampleplugin')) {
Html::displayNotFoundError();
}
$object = new PluginMyExampleMyObject();
if (isset($_POST['add'])) {
//Check CREATE ACL
$object->check(-1, CREATE, $_POST);
//Do object creation
$newid = $object->add($_POST);
//Redirect to newly created object form
Html::redirect("{$CFG_GLPI['root_doc']}/plugins/front/myobject.form.php?id=$newid");
} else if (isset($_POST['update'])) {
//Check UPDATE ACL
$object->check($_POST['id'], UPDATE);
//Do object update
$object->update($_POST);
//Redirect to object form
Html::back();
} else if (isset($_POST['delete'])) {
//Check DELETE ACL
$object->check($_POST['id'], DELETE);
//Put object in dustbin
$object->delete($_POST);
//Redirect to objects list
$object->redirectToList();
} else if (isset($_POST['purge'])) {
//Check PURGE ACL
$object->check($_POST['id'], PURGE);
//Do object purge
$object->delete($_POST, 1);
//Redirect to objects list
Html::redirect("{$CFG_GLPI['root_doc']}/plugins/front/myobject.php");
} else {
//per default, display object
$withtemplate = (isset($_GET['withtemplate']) ? $_GET['withtemplate'] : 0;
$object->display(
[
'id' => $_GET['id'],
'withtemplate' => $withtemplate
]
);
}
11 changes: 9 additions & 2 deletions source/plugins/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ This is a minimalist example, for a plugin named `myexample` (functions names wi
* @return void
*/
function plugin_init_myexample() {
//some code here, like call to Plugin::registerClass(), populating PLUGIN_HOOKS? ...
global $PLUGIN_HOOKS;
//required!
$PLUGIN_HOOKS['csrf_compliant']['myexample'] = true;
//some code here, like call to Plugin::registerClass(), populating PLUGIN_HOOKS, ...
}
/**
Expand Down Expand Up @@ -92,6 +97,8 @@ This is a minimalist example, for a plugin named `myexample` (functions names wi

On the same model, you can use ``Plugin::messageMissingRequirement()`` to display internationalized message if any extension, plugin or GLPI parameter is missing.

Plugin informations provided in ``plugin_version_myexample`` method will be displayed in the GLPI plugins user interface.

.. _plugins_hookphp:

hook.php
Expand Down Expand Up @@ -142,7 +149,7 @@ You can then for example add a line in your ``.travis.yml`` file to automate che
.. code-block:: yaml
script:
- vendor/bin/phpcs -p --ignore=vendor --ignore=js --standard=vendor/glpi-project/coding-standard/GlpiStandard/ .
- vendor/bin/phpcs -p --ignore=vendor --standard=vendor/glpi-project/coding-standard/GlpiStandard/ .
.. note::

Expand Down

0 comments on commit a96b8ab

Please sign in to comment.