Glpi Developer Documentation
Glpi Developer Documentation
Documentation
Release 9.4 & dev
Teclib’
2 Coding standards 11
2.1 Indentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2 Spacing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.3 Control structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.5 true, false and null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.6 Including files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.7 PHP tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.8 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.9 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.10 Variables and Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.11 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.12 Variables types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.13 Quotes / double quotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.14 Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.15 Database queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.16 Checking standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3 Developer API 21
3.1 Main framework objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.3 Search Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.4 Massive Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.5 Rules Engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.6 Translations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.7 Right Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.8 Automatic actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3.9 Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.10 Extra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
i
4 Checklists 69
4.1 Review process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.2 Prepare next major release . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5 Plugins 71
5.1 Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.2 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.3 Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.4 Adding and managing objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.5 Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.6 Automatic actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.7 Massive Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.8 Tips & tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5.9 Notification modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6 Packaging 103
6.1 Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.2 Filesystem Hirerarchie Standard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.3 Apache Configuration File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.4 Logs files rotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6.5 SELinux stuff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6.6 Use system cron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
6.7 Using system libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
6.8 Using system fonts rather than bundled ones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
6.9 Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
ii
GLPI Developer Documentation Documentation, Release 9.4 & dev
Contents 1
GLPI Developer Documentation Documentation, Release 9.4 & dev
2 Contents
CHAPTER 1
1.1 Branches
1.2 Testing
There are more and more unit tests in GLPI; we use the atoum unit tests framework.
Every proposal must contains unit tests; for new features as well as bugfixes. For the bugfixes; this is not a strict
requirement if this is part of code that is not tested at all yet. See the unit testing section at the bottom of the page.
Anyways, existing unit tests may never be broken, if you made a change that breaks something, check your code, or
change the unit tests, but fix that! ;)
3
GLPI Developer Documentation Documentation, Release 9.4 & dev
Note: This lists current files and directories listed in the source code of GLPI. Some files are not part of distribued
archives.
• ajax
– *.php: Ajax components
• files Files written by GLPI or plugins (documents, session files, log files, . . . )
• front
– *.php: Front components (all displayed pages)
• css
– . . . : CSS stylesheets
– *.css: CSS stylesheets
• inc
– *.php: Classes, functions and definitions
• install
– mysql: MariaDB/MySQL schemas
– *.php: upgrades scripts and installer
• js
– *.js: Javascript files
• lib
– . . . : external Javascript libraries
• locales
– glpi.pot: Gettext’s POT file
– *.po: Gettext’s translations
– *.mo: Gettext’s compiled translations
• pics
– *.*: pictures and icons
• plugins:
1.4 Workflow
1.4.1 In short. . .
1.4. Workflow 5
GLPI Developer Documentation Documentation, Release 9.4 & dev
1.4.2 General
Most of the times, when you’ll want to contribute to the project, you’ll have to retrieve the code and change it before
you can report upstream. Note that I will detail here the basic command line instructions to get things working; but of
course, you’ll find equivalents in your favorite Git GUI/tool/whatever ;)
Just work with a:
A directory named glpi will bre created where you’ve issued the clone.
Then - if you did not already - you will have to create a fork of the repository on your github account; using the Fork
button from the GLPI’s Github page. This will take a few moments, and you will have a repository created, {you user
name}/glpi - forked from glpi-project/glpi.
Add your fork as a remote from the cloned directory:
You can replace my_fork with what you want but origin (just remember it); and you will find your fork URL from the
Github UI.
A basic good practice using Git is to create a branch for everything you want to do; we’ll talk about that in the sections
below. Just keep in mind that you will publish your branches on you fork, so you can propose your changes.
When you open a new pull request, it will be reviewed by one or more member of the community. If you’re asked
to make some changes, just commit again on your local branch, push it, and you’re done; the pull request will be
automatically updated.
Note: It’s up to you to manage your fork; and keep it up to date. I’ll advice you to keep original branches (such as
master or x.y/bugfixes) pointing on the upstream repository.
Tha way, you’ll just have to update the branch from the main repository before doing anything.
1.4.3 Bugs
If you find a bug in the current stable release, you’ll have to work on the bugfixes branch; and, as we’ve said already,
create a specific branch to work on. You may name your branch explicitely like 9.1/fix-sthing or to reference an
existing issue 9.1/fix-1234; just prefix it with {version}/fix-.
Generally, the very first step for a bug is to be filled in a ticket.
From the clone directory:
At this point, you’re working on an only local branch named 9.1/fix-api-callback. You can now work to solve the issue,
and commit (as frequently as you want).
At the end, you will want to get your changes back to the project. So, just push the branch to your fork remote:
Last step is to create a PR to get your changes back to the project. You’ll find the button to do this visiting your fork
or even main project github page.
Just remember here we’re working on some bugfix, that should reach the bugfixes branch; the PR creation will probably
propose you to merge against the master branch; and maybe will tell you they are conflicts, or many commits you do
not know about. . . Just set the base branch to the correct bugfixes and that should be good.
1.4.4 Features
Before doing any work on any feature, mays sure it has been discussed by the community. Open - if it does not exists
yet - a ticket with your detailled proposition. Fo technical features, you can work directly on github; but for work
proposals, you should take a look at our feature proposal platform.
If you want to add a new feature, you will have to work on the master branch, and create a local branch with the name
you want, prefixed with feature/.
From the clone directory:
You’ll notice we do no change branch on the first step; that is just because master is the default branch, and therefore
the one you’ll be set on just fafter cloning. At this point, you’re working on an only local branch named feature/my-
killer-feature. You can now work and commit (as frequently as you want).
At the end, you will want to get your changes back to the project. So, just push the branch on your fork remote:
There are several good practices regarding commit messages, but this is quite simple:
• the commit message may refer an existing ticket if any,
– just make a simple reference to a ticket with keywords like refs #1234 or see #1234",
– automatically close a ticket when commit will be merged back with keywords like closes #1234 or
fixes #1234,
• the first line of the commit should be as short and as concise as possible
• if you want or have to provide details, let a blank line after the first commit line, and go on. Please avoid very
long lines (some conventions talks about 80 characters maximum per line, to keep it lisible).
$ composer install
To add a new library, you will probably found the command line on the library documentation, something like:
1.4. Workflow 7
GLPI Developer Documentation Documentation, Release 9.4 & dev
Note: A note for the purists. . . In GLPI, there are both unit and functional tests; without real distinction ;-)
We use the atoum unit tests framework; see GLPI website if you wonder why.
atoum’s documentation in available at: http://docs.atoum.org
Warning: With atoum, test class must refer to an existing class of the project!
Tests must be run in an isolated environment. By default, atoum use a concurrent mode; that launches tests in a multi-
threaded environment. While it is possible to bypass this; this should not be done See http://docs.atoum.org/en/latest/
engine.html.
For technical reasons (mainly because of the huge session usage), GLPI unit tests are actually limited to one only
thread while running the whole suite; but while developing, the behavior should only be changed if this is really
needed.
Unlike PHPUnit, atoum is very strict on type hitting. This really makes sense; but often in GLPI types are not what
we should expect (for example, we often get a string and not an integer from counting methods).
1.5.3 Database
Each class that tests something in database must inherit from \DbTestCase. This class provides some helpers (like
login() or setEntity() method); and it also does some preparation and cleanup.
Each CommonDBTM object added in the database with its add() method will be automatically deleted after the test
method. If you always want to get a new object type created, you can use beforeTestMethod() or setUp()
methods.
Some bootstrapped data are provided (will be inserted on the first test run); they can be used to check defaults behaviors
or make queries, but you should never change those data! This lend to unpredictable further tests results.
When you use a property that has not been declared, you will have errors that may be quite difficult to understand.
Just remember to always declare property you use!
<?php
You can install atoum from composer (just run composer install from GLPI directory) or even system wide.
There are two directories for tests:
• tests/units for main core tests;
• tests/api for API tests.
You can choose to run tests on a whole directory, or on any file. You have to specify a bootstrap file each time:
If you want to run the API tests suite, you need to run a development server:
Running atoum without any arguments will show you the possible options. Most important are:
• -bf to set bootstrap file,
• -d to run tests located in a whole directory,
• -f to run tests on a standalone file,
• --debug to get extra informations when something goes wrong,
• -mcn limit number of concurrent runs. This is unfortunately mandatory running the whole test suite right now
:/,
• -ncc do not generate code coverage,
• --php to change PHP executable to use,
• -l loop mode.
Note that if you do not use the -ncc switch; coverage will be generated in the tests/code-coverage/ directory.
Coding standards
2.1 Indentation
• 3 spaces
• Max line width: 100
<?php
// base level
// level 1
// level 2
// level 1
// base level
2.2 Spacing
We’ve adopted “french spacing” rules in the code. The rule is:
• for simple punctuation (,, .): use one space after the punctuation sign
• for double punctuation (!, ?, :): use one space after and one space before the punctuation sign
• for opening punctuation ((, {, [): use one space before the punctuation sign
• for closing punctuation ( ), }, ]): use one space after the punctuation sign, excepted for line end, when followed
by a semi-colon (;)
Of course, this rules only aplies on the source code, not on the strings (translatable strings, comments, . . . )!
11
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
if ($test1) {
for ($i=0 ; $i<$end ; $i++) {
echo "test ".( $i<10 ? "0$i" : $i )."<br>";
}
}
if ($a==$b
|| ($c==$d && $e==$f)) {
...
} else if {
...
}
switch ($test2) {
case 1 :
echo "Case 1";
break;
case 2 :
echo "Case 2";
// No break here : because...
default :
echo "Default Case";
}
2.4 Arrays
Arrays must be declared using the short notation syntax ([]), long notation (array()) is forbidden.
Use include_once in order to include the file once and to raise warning if file does not exists:
include_once GLPI_ROOT."/inc/includes.php";
The PHP closing tag ?> must be avoided on full PHP files (so in most of GLPI’s files!).
2.8 Functions
<?php
function userName($a, $b = 'foo') {
//do something here!
}
Space after opening parenthesis and before closing parenthesis are forbidden. For parematers which have a default
value; add a space before and after the equel sign.
If parameters add block doc for these parameters, please see the Comments section for any example.
If function from parent add
<?php
function getMenuContent()
If it’s a new function, add in block doc (see the Comments section):
Some methods in the source code as declared as static; some are not.
For sure, you cannot make static calls on a non static method. In order to call such a method, you will have to get an
object instance, and the call the method on it:
<?php
It may be different calling static classes. In that case; you can either:
• call statically the method from the object; like MyObject::staticMethod(),
• call statically the method from an object instance; like $object::staticMethod(),
• call non statically the method from an object instance; like $object->staticMethod().
• use late static building; like static::staticMethod().
When you do not have any object instance yet; the first solution is probably the best one. No need to instanciate an
object to just call a static method from it.
2.8. Functions 13
GLPI Developer Documentation Documentation, Release 9.4 & dev
On the other hand; if you already have an object instance; you should better use any of the solution but the late static
binding. That way; you will save performances since this way to go do have a cost.
2.9 Classes
Note: even if GLPI does not use namespaces, some libs does, you will have to take care of that. You can also if you
wish use namespaces for PHP objects call.
For example, the folloging code:
<?php
try {
...
$something = new stdClass();
...
} catch (Exception $e{
...
}
• Variable names must be as descriptive and as short as possible, stay clear and concise.
• In case of multiple words, use the _ separator,
• Variables must be lower case,
• Global variables and constants must be UPPER case.
<?php
$user = 'glpi';
// put elements in alphabetic order
$users = array('glpi', 'glpi2', 'glpi3');
$users = array('glpi1' => 'valeur1',
(continues on next page)
$CFG_GLPI = array();
2.11 Comments
To be more visible, don’t put inline block comments into /* */ but comment each line with //. Put docblocks
comments into /** */.
Each function or method must be documented, as well as all its parameters (see Variables types below), and its return.
For each method or function documentation, you’ll need at least to have a description, the version it was introduced,
the parameters list, the return type; each blocks separated with a blank line. As an example, for a void function:
<?php
/**
* Describe what the method does. Be concise :)
*
* You may want to add some more words about what the function
* does, if needed. This is optionnal, but you can be more
* descriptive here:
* - it does something
* - and also something else
* - but it doesn't make coffee, unfortunately.
*
* @since 9.2
*
* @param string $param A parameter, for something
* @param boolean $other_param Another parameter
*
* @return void
*/
function myMethod($param, $other_param) {
//[...]
}
2.11. Comments 15
GLPI Developer Documentation Documentation, Release 9.4 & dev
7. @return,
8. @see,
9. @throw,
10. @todo,
Each parameter must be documented in its own line, begining with the @param tag, followed by the Variables types,
followed by the param name ($param), and finally with the description itself. If your parameter can be of different
types, you can list them separated with a | or you can use the mixed type; it’s up to you!
All parameters names and description must be aligned vertically on the longest (plu one character); see the above
example.
There are many question regarding the way to document a child method in a child class.
Many editors use the {@inheritDoc} tag without anything else. This is wrong. This inline tag is confusing for
many users; for more details, see the PHPDocumentor documentation about it. This tag usage is not forbidden, but
make sure to use it properly, or just avoid it. An usage exemple:
<?php
Something we can see quite often is just the usage of the @see tag to make reference to the parent method. This is
wrong. The @see tag is designed to reference another method that would help to understand this one; not to make a
reference to its parent (you can also take a look at PHPDocumentor documentation about it. While generating, parent
class and methods are automaticaly discovered; a link to the parent will be automatically added. An usage example:
<?php
/**
* Adds something
*
* @param string $type Type of thing
* @param string $value The value
*
* @return boolean
*/
public function add($type, $value) {
// [...]
}
/**
* Adds myType entry
*
* @param string $value The value
*
* @return boolean
* @see add()
*/
public function addMyType($value) {
return $this->addType('myType', $value);
}
Type Description
mixed A variable with undefined (or multiple) type
integer Integer type variable (whole number)
float Float type (point number)
boolean Logical type (true or false)
string String type (any value in "" or ' ')
array Array type
object Object type
ressource Resource type (as returned from mysql_connect function)
Inserting comment in source code for doxygen. Result : full doc for variables, functions, classes. . .
• You must use single quotes for indexes, constants declaration, translations, . . .
<?php
//for that one, you should use double, but this is at your option...
$a = "foo";
2.14 Files
• Queries must be written onto several lines, one statement item by line.
• All SQL words must be UPPER case.
• For MySQL, all item based must be slash protected (table name, field name, condition),
<?php
$query = "SELECT *
FROM `glpi_computers`
LEFT JOIN `xyzt` ON (`glpi_computers`.`fk_xyzt` = `xyzt`.`id`
AND `xyzt`.`toto` = 'jk')
WHERE @id@ = '32'
AND ( `glpi_computers`.`name` LIKE '%toto%'
OR `glpi_computers`.`name` LIKE '%tata%' )
ORDER BY `glpi_computers`.`date_mod` ASC
LIMIT 1";
In order to check some stabdards are respected, we provide some custom PHP CodeSniffer rules. From the GLPI
directory, just run:
If the above command does not provide any output, then, all is OK :)
An example error output would looks like:
FILE: /var/www/webapps/glpi/tests/HtmlTest.php
----------------------------------------------------------------------
FOUND 3 ERRORS AFFECTING 3 LINES
----------------------------------------------------------------------
40 | ERROR | [x] Line indented incorrectly; expected 3 spaces, found
| | 4
59 | ERROR | [x] Line indented incorrectly; expected 3 spaces, found
| | 4
64 | ERROR | [x] Line indented incorrectly; expected 3 spaces, found
| | 4
Developer API
Apart from the current documentation, you can also check the full PHP documentation of GLPI (generated with
apigen).
GLPI contains numerous classes; but there are a few common objects you’d have to know about. All GLPI classes are
in the inc directory.
3.1.1 CommonGLPI
This is the main GLPI object, most of GLPI or Plugins class inherit from this one, directly or not. The class is in the
inc/commonglpi.class.php file.
This object will help you to:
• manage item type name,
• manage item tabs,
• manage item menu,
• do some display,
• get URLs (form, search, . . . ),
• ...
See the full API documentation for CommonGLPI object for a complete list of methods provided.
3.1.2 CommonDBTM
This is an object to manage any database stuff; it of course inherits from CommonGLPI. The class is in the inc/
commondbtm.class.php file.
21
GLPI Developer Documentation Documentation, Release 9.4 & dev
It aims to manage database persistence and tables for all objects; and will help you to:
• add, update or delete database rows,
• load a row from the database,
• get table informations (name, indexes, relations, . . . )
• ...
The CommonDBTM object provides several of the available hooks.
See the full API documentation for CommonDBTM object for a complete list of methods provided.
3.1.3 CommonDropdown
This class aims to manage dropdown (lists) database stuff. It inherits from CommonDBTM. The class is in the inc/
commondropdown.class.php file.
It will help you to:
• manage the list,
• import data,
• ...
See the full API documentation for CommonDropdown object for a complete list of methods provided.
3.1.4 CommonTreeDropdown
This class aims to manage tree lists database stuff. It inherits from CommonDropdown. The class is in the inc/
commontreedropdown.class.php file.
It will mainly help you to manage the tree apsect of a dropdown (parents, children, and so on).
See the full API documentation for CommonTreeDropdown object for a complete list of methods provided.
3.1.5 CommonImplicitTreeDropdown
This class manages tree lists that cannot be managed by the user. It inherits from CommonTreeDropdown. The class
is in the inc/commonimplicittreedropdown.class.php file.
See the full API documentation for CommonTreeDropdown object for a complete list of methods provided.
3.1.6 CommonDBVisible
This class helps with visibility management. It inherits from CommonDBTM. The class is in the inc/
commondbvisible.class.php file.
It provides methods to:
• know if the user can view item,
• get dropdown parameters,
• ...
See the full API documentation for CommonDBVisible object for a complete list of methods provided.
3.1.7 CommonDBConnexity
This class factorizes database relation and inheritance stuff. It inherits from CommonDBTM. The class is in the inc/
commondbconnexity.class.php file.
It is not designed to be used directly, see CommonDBChild and CommonDBRelation.
See the full API documentation for CommonDBConnexity object for a complete list of methods provided.
3.1.8 CommonDBChild
This class manages simple relations. It inherits from CommonDBConnexity. The class is in the inc/
commondbchild.class.php file.
This object will help you to define and manage parent/child relations.
See the full API documentation for CommonDBChild object for a complete list of methods provided.
3.1.9 CommonDBRelation
This class manages relations. It inherits from CommonDBConnexity. The class is in the inc/
commondbrelation.class.php file.
Unlike CommonDBChild; it is designed to declare more complex relations; as defined in the database model. This is
therefore more complex thant just using a simple relation; but it also offers many more possibilities.
In order to setup a complex relation, you’ll have to define several properties, such as:
• $itemtype_1 and $itemtype_2; to set both itm types used;
• $items_id_1 and $items_id_2; to set field id name.
Other properties let you configure how to deal with entites inheritance, ACLs; what to log on each part on several
actions, and so on.
The object will also help you to:
• get search options and query,
• find rights in ACLs list,
• handle massive actions,
• ...
See the full API documentation for CommonDBRelation object for a complete list of methods provided.
3.1.10 CommonDevice
This class factorizes common requirements on devices. It inherits from CommonDropdown. The class is in the inc/
commondevice.class.php file.
It will help you to:
• import devices,
• handle menus,
• do some display,
• ...
See the full API documentation for CommonDevice object for a complete list of methods provided.
All common ITIL objects will help you with ITIL objects management (Tickets, Changes, Problems).
CommonITILObject
Handle ITIL objects. It inherits from CommonDBTM. The class is in the inc/commonitilobject.class.php
file.
It will help you to:
• get users, suppliers, groups, . . .
• count them,
• get objects for users, technicians, suppliers, . . .
• get status,
• ...
See the full API documentation for CommonITILObject object for a complete list of methods provided.
CommonITILActor
Handle ITIL actors. It inherits from CommonDBRelation. The class is in the inc/commonitilactor.class.
php file.
It will help you to:
• get actors,
• show notifications,
• get ACLs,
• ...
See the full API documentation for CommonITILActor object for a complete list of methods provided.
CommonITILCost
Handle ITIL costs. It inherits from CommonDBChild. The class is in the inc/commonitilcost.class.php
file.
It will help you to:
• get item cost,
• do some display,
• ...
See the full API documentation for CommonITILCost object for a complete list of methods provided.
CommonITILTask
Handle ITIL tasks. It inherits from CommonDBTM. The class is in the inc/commonitiltask.class.php file.
It will help you to:
• manage tasks ACLs,
• do some display,
• get search options,
• ...
See the full API documentation for CommonITILTask object for a complete list of methods provided.
CommonITILValidation
Handle ITIL validation process. It inherits from CommonDBChild. The class is in the inc/
commonitilvalidation.class.php file.
It will help you to:
• mange ACLs,
• get and set status,
• get counts,
• do some display,
• ...
See the full API documentation for CommonITILValidation object for a complete list of methods provided.
3.2 Database
Current GLPI database contains more than 250 tables; the goal of the current documentation is to help you to under-
stand the logic of the project, not to detail each table and possibility.
As on every database, there are tables, relations between them (more or less complex), some relations have descriptions
stored in a another table, some tables way be linked with themselves. . . Well, it’s quite common :) Let’s start with a
simple example:
3.2. Database 25
GLPI Developer Documentation Documentation, Release 9.4 & dev
Resultsets
All resultsets sent back from GLPI database should always be associative arrays.
Naming conventions
All tables and fields names are lower case and follows the same logic. If you do not respect that; GLPI will fail to find
relevant informations.
Tables
Tables names are linked with PHP classes names; they are all prefixed with glpi_, and class name is set to plural.
Plugins tables must be prefixed by glpi_plugin_; followed by the plugin name, another dash, and then pluralized
class name.
A few examples:
Fields
Warning: Each table must have an auto-incremented primary key named id.
Field naming is mostly up to you; exept for identifiers and foreign keys. Just keep clear and concise!
To add a foreign key field; just use the foreign table name without glpi_ prefix, and add _id suffix.
Warning: Even if adding a foreign key in a table should be perfectly correct; this is not the usual way things are
done in GLPI, see Make relations to know more.
A few examples:
Make relations
On most cases, you may want to made possible to link many different items to something else. Let’s say you want
to make possible to link a Computer, a Printer or a Phone to a Memory component. You should add foreign keys in
items tables; but on something as huge as GLPI, it maybe not a good idea.
Instead, create a relation table, that will reference the memory component along with a item id and a type, as for
example:
Again, this is a very simplified example of what already exists in the database, but you got the point ;)
In this example, itemtype would be Computer, Printer or Phone; items_id the id of the related item.
3.2. Database 27
GLPI Developer Documentation Documentation, Release 9.4 & dev
Indexes
In order to get correct performances querying database, you’ll have to take care of setting some indexes. It’s a nonsense
to add indexes on every fields in the database; but some of them must be defined:
• foreign key fields;
• fields that are very often used (for example fields like is_visible, itemtype, . . . ),
• primary keys ;)
You should just use the field name as key name.
3.2.2 Querying
Basic usage
<?php
foreach ($DB->request(...) as $id => $row) {
//... work on each row ...
}
$req = $DB->request(...);
if ($row = $req->next()) {
// ... work on a single row
}
$req = $DB->request(...);
if (count($req)) {
// ... work on result
}
Arguments
If the only option is a full SQL statement, it will be used. This usage is deprecated, and should be avoid when possible.
Note: To make a database query that could not be done using recommanded way (calling SQL functions such as
NOW(), ADD_DATE(), . . . for example), you can do:
<?php
$DB->request('SELECT id FROM glpi_users WHERE end_date > NOW()');
Without option
In this case, all the data from the selected table is iterated:
<?php
$DB->request(['FROM' => 'glpi_computers']);
// => SELECT * FROM `glpi_computers`
$DB->request('glpi_computers');
// => SELECT * FROM `glpi_computers`
Fields selection
You can use either the SELECT or FIELDS options, an additional DISTINCT option might be specified.
<?php
$DB->request(['SELECT' => 'id', 'FROM' => 'glpi_computers']);
// => SELECT `id` FROM `glpi_computers`
Using JOINs
You need to use criteria, usually a FKEY to describe how to join the tables.
3.2. Database 29
GLPI Developer Documentation Documentation, Release 9.4 & dev
You need to use criteria, usually a FKEY (or the ON equivalent), to describe how to join the tables:
<?php
$DB->request(['FROM' => ['glpi_computers', 'glpi_computerdisks'],
'FKEY' => ['glpi_computers'=>'id',
'glpi_computerdisks'=>'computer_id']]);
$DB->request(['glpi_computers', 'glpi_computerdisks'],
['FKEY' => ['glpi_computers'=>'id',
'glpi_computerdisks'=>'computer_id']]);
// => SELECT * FROM `glpi_computers`, `glpi_computerdisks`
// WHERE `glpi_computers`.`id` = `glpi_computerdisks`.`computer_id`
Left join
Using the LEFT JOIN option, with some criteria, usually a FKEY (or the ON equivalent):
<?php
$DB->request(['FROM' => 'glpi_computers',
'LEFT JOIN' => ['glpi_computerdisks' => ['FKEY' => ['glpi_computers'
˓→ => 'id',
'glpi_computerdisks
˓→' => 'computer_id']]]]);
Inner join
Using the INNER JOIN option, with some criteria, usually a FKEY (or the ON equivalent):
<?php
$DB->request(['FROM' => 'glpi_computers',
'INNER JOIN' => ['glpi_computerdisks' => ['FKEY' => ['glpi_computers'
˓→ => 'id',
'glpi_computerdisks
˓→' => 'computer_id']]]]);
Right join
Using the RIGHT JOIN option, with some criteria, usually a FKEY (or the ON equivalent):
<?php
$DB->request(['FROM' => 'glpi_computers',
'RIGHT JOIN' => ['glpi_computerdisks' => ['FKEY' => ['glpi_computers'
˓→ => 'id',
'glpi_computerdisks
˓→' => 'computer_id']]]]);
Join criterion
UNION queries
3.2. Database 31
GLPI Developer Documentation Documentation, Release 9.4 & dev
As you can see on the above example, a UNION ALL query is built. If you want your results to be deduplicated,
(standard UNION):
<?php
//...
//passing true as second argument will activate deduplication.
$union = new \QueryUnion([$sub1, $sub2], true);
//...
Warning: Keep in mind that deduplicate a UNION query may have a huge cost on database server.
Most of the time, you can issue a UNION ALL and dedup in the code.
Counting
<?php
$DB->request(['FROM' => 'glpi_computers', 'COUNT' => 'cpt']);
// => SELECT COUNT(*) AS cpt FROM `glpi_computers`
Grouping
Using the GROUPBY option, which contains a field name or an array of field names.
<?php
$DB->request(['FROM' => 'glpi_computers', 'GROUPBY' => 'name']);
// => SELECT * FROM `glpi_computers` GROUP BY `name`
Order
Using the ORDER option, with value a field or an array of fields. Field name can also contains ASC or DESC suffix.
<?php
$DB->request(['FROM' => 'glpi_computers', 'ORDER' => 'name']);
// => SELECT * FROM `glpi_computers` ORDER BY `name`
Request pager
<?php
$DB->request('glpi_computers', ['START' => 5, 'LIMIT' => 10]);
// => SELECT * FROM `glpi_computers` LIMIT 10 OFFSET 5"
Criteria
Simple criteria
<?php
$DB->request(['FROM' => 'glpi_computers', 'WHERE' => ['is_deleted' => 0]]);
// => SELECT * FROM `glpi_computers` WHERE `is_deleted` = 0
<?php
$DB->request('glpi_computers', ['OR' => ['is_deleted' => 0,
'name' => 'foo']]);
// => SELECT * FROM `glpi_computers` WHERE (`is_deleted` = 0 OR `name` = 'foo')"
Operators
Default operator is =, but other operators can be used, by giving an array containing operator and value.
<?php
$DB->request('glpi_computers', ['date_mod' => ['>' , '2016-10-01']]);
// => SELECT * FROM `glpi_computers` WHERE `date_mod` > '2016-10-01'
3.2. Database 33
GLPI Developer Documentation Documentation, Release 9.4 & dev
Known operators are =, !=, <, <=, >, >=, LIKE, REGEXP, NOT LIKE, NOT REGEX, & (BITWISE AND), and |
(BITWISE OR).
Aliases
You can use SQL aliases (SQL AS keyword). To achieve that, just write the alias you want on the table name or the
field name; then use it in your parameters:
<?php
$DB->request('glpi_computers AS c');
// => SELECT * FROM `glpi_computers` AS `c`
Aggregate functions
<?php
$DB->request(['SELECT' => ['COUNT' => 'field', 'bar'], 'FROM' => 'glpi_computers',
˓→'GROUPBY' => 'field']);
$DB->request(['SELECT' => ['bar', 'SUM' => 'amount AS total'], 'FROM' => 'glpi_
˓→computers', 'GROUPBY' => 'amount']);
Sub queries
<?php
$sub_query = new \QuerySubQuery([
'SELECT' => 'id',
'FROM' => 'subtable',
'WHERE' => [
'subfield' => 'subvalue'
]
]);
$DB->request(['FROM' => 'glpi_computers', 'WHERE' => ['field' => $sub_query]]);
// => SELECT * FROM `glpi_computers` WHERE `field` IN (SELECT `id` FROM `subtable`
˓→WHERE `subfield` = 'subvalue')
// => SELECT * FROM `glpi_computers` WHERE NOT `field` IN (SELECT `id` FROM
˓→`subtable` WHERE `subfield` = 'subvalue')
Even if we do our best to get as many things as possible implemented in the iterator, there are several things that are
missing. . . Consider for example you want to use the SQL NOW() function, or want to use a value based on another
field: there is no native way to achieve that.
Right now, there is a QueryExpression class that would permit to do such things on values (an not on fields since it is
not possible to use a class instance as an array key).
Warning: The QueryExpression class will pass raw SQL. You are in charge to escape name and values you use
into it!
<?php
$DB->request([
'FROM' => 'my_table',
'WHERE' => [
'date_end' => ['>', new \QueryExpression('NOW()')]
]
]);
// SELECT * FROM `my_table` WHERE `date_end` > NOW()
<?php
$DB->request([
'FROM' => 'my_table',
'WHERE' => [
'field' => new \QueryExpression(DBmysql::quoteName('other_field'))
]
(continues on next page)
3.2. Database 35
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
$DB->request([
'FROM' => 'my_table',
'WHERE' => [
'RAW' => [
'LOWER(' . DBmysql::quoteName('field') . ')' => strtolower('Value')
]
]
]);
// SELECT * FROM `my_table` WHERE LOWER(`field`) = 'value'
3.2.3 Updating
General
Escaping of data is currently provided automatically by the framework for all data passed from GET or POST; you do
not have to take care of them (this will change in a future version). You have to take care of escaping data when you
use values that came from elsewhere.
The WHERE part of UPDATE and DELETE methods uses the same criteria capabilities than SELECT queries.
Inserting a row
You can insert a row in the database using the insert() method:
<?php
$DB->insert(
'glpi_my_table', [
'a_field' => 'My value',
'other_field' => 'Other value'
]
);
// => INSERT INTO `glpi_my_table` (`a_field`, `other_field`) VALUES ('My value',
˓→Other value)
Updating a row
You can update rows in the database using the update() method:
<?php
$DB->update(
'glpi_my_table', [
'a_field' => 'My value',
'other_field' => 'Other value'
], [
'id' => 42
]
);
// => UPDATE `glpi_my_table` SET `a_field` = 'My value', `other_field` = 'Other value
˓→' WHERE `id` = 42
<?php
$DB->update(
'my_table', [
'my_field' => 'my value'
], [
'WHERE' => ['field' => 'value'],
'ORDER' => ['date DESC', 'id ASC'],
'LIMIT' => 1
]
);
Removing a row
You can remove rows from the database using the delete() method:
<?php
$DB->delete(
'glpi_my_table', [
'id' => 42
]
);
// => DELETE FROM `glpi_my_table` WHERE `id` = 42
On some cases, you may want to use prepared statements to improve performances. In order to achieve that, you will
have to create a query with some parameters (not named, since mysqli does not supports named parameters), then to
prepare it, and finally to bind parameters and execute the statement.
Let’s see an example with an insert statement:
3.2. Database 37
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
$insert_query = $DB->buildInsert(
'my_table', [
'field' => new Queryparam(),
'other' => new Queryparam()
]
);
// => INSERT INTO `glpi_my_table` (`field`, `other`) VALUES (?, ?)
$insert_stmt = $DB->prepare($insert_query);
Just like the buildInsert() method used here, buildUpdate and buildDelete methods are available. They take exactly
the same arguments as “non build” methods.
Note: Note the use of the Queryparam object. This is used for the builder to be aware you are not passing a value,
but a parameter (that must not be escaped nor quoted).
<?php
$it = new DBmysqlIterator();
$it->buildQuery([
'FROM' => 'my_table',
'WHERE' => [
'something' => new Queryparam(),
'foo' => 'bar'
]);
// => SELECT FROM `my_table` WHERE `something` = ? AND `foo` = 'bar'
// [...]
3.3.1 Goal
The Search class aims to provide a multi-criteria Search engine for GLPI Itemtypes.
It includes some short-cuts functions:
• show(): displays the complete search page.
• showGenericSearch(): displays only the multi-criteria form.
• showList(): displays only the resulting list.
Todo:
• datafields option
• difference between searchunit and delay_unit
• dropdown translations
• giveItem
• export
• fulltext search
Examples
To display the search engine with its default options (criteria form, pager, list):
<?php
$itemtype = 'Computer';
Search::show($itemtype);
If you want to display only the multi-criteria form (with some additional options):
<?php
$itemtype = 'Computer';
$p = [
'addhidden' => [ // some hidden inputs added to the criteria form
'hidden_input' => 'OK'
],
'actionname' => 'preview', //change the submit button name
'actionvalue' => __('Preview'), //change the submit button label
];
Search::showGenericSearch($itemtype, $p);
<?php
// display a list of users with entity = 'Root entity'
$itemtype = 'User';
$p = [
'start' => 0, // start with first item (index 0)
'is_deleted' => 0, // item is not deleted
'sort' => 1, // sort by name
'order' => 'DESC' // sort direction
'reset' => 'reset',// reset search flag
'criteria' => [
[
(continues on next page)
Note: GLPI saves in $_SESSION['glpisearch'][$itemtype] the last set of parameters for the cur-
rent itemtype for each search query. It is automatically restored on a new search if no reset, criteria or
metacriteria is defined.
Here is the list of possible keys which could be passed to control the search engine. All are optionals.
criteria An multi-dimensional array of criterion to filter the search. Each criterion array must provide:
• link: one of AND, OR, AND NOT or OR NOT logical operators, optional for first element,
• field: id of the searchoption,
• searchtype: type of search, one of:
– contains
– equals
– notequals
– lessthan
– morethan
– under
– notunder
• value: the value to search
Note: In order to find the field id you want, you may take a loook at the getsearchoptions.php tool script.
metacriteria Very similar to criteria parameter but permits to search in the search options of an itemtype linked
to the current (the softwares of a computer, for example).
Not all itemtype can be linked, see the getMetaItemtypeAvailable() method of the Search class to know which
ones could be.
The parameter need the same keys as criteria plus one additional:
• itemtype: second itemtype to link.
sort id of the searchoption to sort by.
order Either ASC for ending sorting or DESC for ending sorting.
start An integer to indicate the start point of pagination (SQL OFFSET).
is_deleted A boolean for display trash-bin.
reset A boolean to reset saved search parameters, see note below.
Each itemtype can define a set of options to represent the columns which can be queried/displayed by the search
engine. Each option is identified by an unique integer (we must avoid conflict).
Changed in version 9.2: Searchoptions array has been completely rewritten; mainly to catch duplicates and add a unit
test to prevent future issues.
To permit the use of both old and new syntaxes; a new method has been created, getSearchOptionsNew(). Old
syntax is still valid (but do not permit to catch dups).
The format has changed, but not the possible options and their values!
<?php
function getSearchOptionsNew() {
$tab = [];
$tab[] = [
'id' => 'common',
'name' => __('Characteristics')
];
$tab[] = [
'id' => '1',
'table' => self::getTable(),
'field' => 'name',
'name' => __('Name'),
'datatype' => 'itemlink',
'massiveaction' => false
];
...
return $tab;
}
Note: For reference, the old way to write the same search options was:
<?php
function getSearchOptions() {
$tab = array();
$tab['common'] = __('Characteristics');
(continues on next page)
$tab[1]['table'] = self::getTable();
$tab[1]['field'] = 'name';
$tab[1]['name'] = __('Name');
$tab[1]['datatype'] = 'itemlink';
$tab[1]['massiveaction'] = false;
...
return $tab;
}
Join parameters
To define join parameters, you can use one or more of the following:
beforejoin
Define which tables must be joined to access the field.
The array contains table key and may contain an additional joinparams. In case of nested
beforejoin, we start the SQL join from the last dimension.
Example:
<?php
[
'beforejoin' => [
'table' => 'mytable',
'joinparams' => [
'beforejoin' => [...]
]
]
]
jointype
Define the join type:
• empty for a standard jointype::
REFTABLE.`#linkfield#` = NEWTABLE.`id`
REFTABLE.`id` = NEWTABLE.`#linkfield#`
• itemtype_item for links using itemtype and items_id fields in new table::
REFTABLE.`id` = NEWTABLE.`items_id`
AND NEWTABLE.`itemtype` = '#ref_table_itemtype#'
• itemtype_item_revert (since 9.2.1) for links using itemtype and items_id fields in ref
table::
NEWTABLE.`id` = REFTABLE.`items_id`
AND REFTABLE.`itemtype` = '#new_table_itemtype#'
REFTABLE.`id` = NEWTABLE.`mainitems_id`
AND NEWTABLE.`mainitemtype` = 'new table itemtype'
NEWTABLE.`itemtype` = '#new_table_itemtype#'
• item_item for table used to link two similar items: glpi_tickets_tickets for example:
link fields are standardfk_1 and standardfk_2::
REFTABLE.`id` = NEWTABLE.`#fk_for_new_table#_1`
OR REFTABLE.`id` = NEWTABLE.`#fk_for_new_table#_2`
NEWTABLE.`id` = REFTABLE.`#fk_for_new_table#_1`
OR NEWTABLE.`id` = REFTABLE.`#fk_for_new_table#_2`
condition
Additional condition to add to the standard link.
Use NEWTABLE or REFTABLE tag to use the table names.
Changed in version 9.4.
An array of parameters used to build a WHERE clause from GLPI querying facilities. Was previously
only a string.
nolink
Set to true to indicate the current join does not link to the previous join/from (nested joinparams)
Data types
color
Use Html::showColorField() for modification
text
Simple text
string
Use a rich text editor for modification
ip
Any IP adress
mac
Available parameters (all optional):
• htmltext: boolean, escape the value (false by default)
number
Use a Dropdown::showNumber() for modification (in case of equals searchtype). For
contains searchtype, you can use < and > prefix in value.
Available parameters (all optional):
• width: html attribute passed to Dropdown::showNumber()
• min: minimum value (default 0)
• max: maximum value (default 100)
• step: step for select (default 1)
• toadd: array of values to add a the beginning of the dropdown
integer
Alias for numbe
count
Same as number but count the number of item in the table
decimal
Same as number but formatted with decimal
bool
Use Dropdown::showYesNo() for modification
itemlink
Create a link to the item
itemtypename
Use Dropdown::showItemTypes() for modification
Available parameters (all optional) to define available itemtypes:
• itemtype_list: one of $CFG_GLPI[“unicity_types”]
• types: array containing available types
language
You may want to control how to select and display your field in a searchoption. You need to set ‘datatype’ => ‘specific’
in your search option and declare these methods in your class:
getSpecificValueToDisplay Define how to display the field in the list.
Parameters:
• $field: column name, it matches the ‘field’ key of your searchoptions
• $values: all the values of the current row (for select)
• $options: will contains these keys:
– html,
– searchopt: the current full searchoption
getSpecificValueToSelect
Define how to display the field input in the criteria form and massive action.
Parameters:
• $field: column name, it matches the ‘field’ key of your searchoptions
• $values: the current criteria value passed in $_GET parameters
• $name: the html attribute name for the input to display
• $options: this array may vary strongly in function of the searchoption or from the massiveaction
or criteria display. Check the corresponding files:
– searchoptionvalue.php
– massiveaction.class.php
Simplified example extracted from CommonItilObject Class for glpi_tickets.status field:
<?php
function getSearchOptionsMain() {
$tab = [];
...
$tab[] = [
'id' => '12',
'table' => $this->getTable(),
'field' => 'status',
'name' => __('Status'),
'searchtype' => 'equals',
'datatype' => 'specific'
];
...
return $tab;
}
if (!is_array($values)) {
$values = array($field => $values);
}
switch ($field) {
case 'status':
return self::getStatus($values[$field]);
...
}
return parent::getSpecificValueToDisplay($field, $values, $options);
}
if (!is_array($values)) {
$values = array($field => $values);
}
$options['display'] = false;
switch ($field) {
case 'status' :
$options['name'] = $name;
$options['value'] = $values[$field];
return self::dropdownStatus($options);
...
}
return parent::getSpecificValueToSelect($field, $name, $values, $options);
}
The search class implements three methods which add some stuff to SQL queries before the searchoptions computa-
tion. For some itemtype, we need to filter the query or additional fields to it. For example, filtering the tickets you
cannot view if you do not have the proper rights.
GLPI will automatically call predefined methods you can rely on from your plugin hook.php file.
addDefaultSelect
addDefaultWhere
switch ($itemtype) {
case 'MyItemtype':
return Search::addLeftJoin(
$itemtype,
$ref_table,
$already_link_tables,
'newtable',
'linkfield'
);
}
return '';
}
addDefaultJoin
3.3.5 Bookmarks
The glpi_boomarks table stores a list of search queries for the users and permit to retrieve them.
The query field contains an url query construct from parameters with http_build_query PHP function.
The glpi_displaypreferences table stores the list of default columns which need to be displayed to a user for
an itemtype.
A set of preferences can be personal or global (users_id = 0). If a user does not have any personal preferences
for an itemtype, the search engine will use the global preferences.
3.4.1 Goals
The first option of the Actions button is Update. It permits to modify the fields content of the selected items.
The list of fields displayed in the sub list depends on the Search options of the current itemtype. By default, all
Search options are automatically displayed in this list. To forbid this display for one field, you must define the key
massiveaction to false in the Search options declaration, example:
<?php
$tab[] = [
'id' => '1',
'table' => self::getTable(),
'field' => 'name',
'name' => __('Name'),
'datatype' => 'itemlink',
'massiveaction' => false // <- NO MASSIVE ACTION
];
After the Update entry, we can declare additional specific massive actions for our current itemtype.
First, we need declare in our class a getSpecificMassiveActions method containing our massive action defi-
nitions:
<?php
...
function getSpecificMassiveActions($checkitem=NULL) {
$actions = parent::getSpecificMassiveActions($checkitem);
return $actions;
}
<?php
...
break;
}
return parent::showMassiveActionsSubForm($ma);
}
<?php
...
array $ids) {
switch ($ma->getAction()) {
case 'myaction_key':
$input = $ma->getInput();
if ($item->getFromDB($id)
&& $item->doIt($input)) {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
} else {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
$ma->addMessage(__("Something went wrong"));
}
}
return;
}
Besides an instance of MassiveAction class $ma, we have also an instance of the current itemtype $item and
the list of selected id ``$ids.
In this method, we could use some optional utility functions from the MassiveAction $ma object supplied in
parameter :
• itemDone, indicates the result of the current $id, see constants of MassiveAction class. If we miss this call,
the current $id will still be considered as OK.
• addMessage, a string to send to the user for explaining the result when processing the current $id
GLPI provide a set of tools to implements a rule engine which take criteria in input and output actions.
criteria and actions are defined by the user (and/or predefined at the GLPI installation).
Here is the list of base rules set provided in a staple GLPI:
• ruleimportentity: rules for assigning an item to an entity,
• ruleimportcomputer: rules for import and link computers,
• rulemailcollector: rules for assigning a ticket created through a mails receiver,
• ruleright: authorizations assignment rules,
• rulesoftwarecategory: rules for assigning a category to software,
• ruleticket: business rules for ticket.
Plugin could add their own set of rules.
3.5.1 Classes
Here is the minimal setup to have a working set. You need to add the following classes for describing you new
sub_type.
• inc/rulemytype.class.php
<?php
// optional right to apply to this rule type (default: 'config'), see Rights
˓→ management.
static $rightname = 'rule_mytype';
'GROUPS' => [
'table' => 'glpi_groups',
'field' => 'completename',
'name' => sprintf(__('%1$s: %2$s'), __('User'),
__('Group'));
'linkfield' => '',
'type' => 'dropdown',
'virtual' => true,
'id' => 'groups',
],
...
];
$criterias['GROUPS']['table'] = 'glpi_groups';
$criterias['GROUPS']['field'] = 'completename';
$criterias['GROUPS']['name'] = sprintf(__('%1$s: %2$s'), __
˓→('User'),
__('Group'));
$criterias['GROUPS']['linkfield'] = '';
$criterias['GROUPS']['type'] = 'dropdown';
$criterias['GROUPS']['virtual'] = true;
$criterias['GROUPS']['id'] = 'groups';
return $criterias;
}
...
];
return $actions;
}
}
• inc/rulemytypecollection.class.php
<?php
$input['GROUPS'][] = $group['id'];
}
}
}
...
return $input;
}
}
You need to also add the following php files for list and form:
• front/rulemytype.php
<?php
include ('../inc/includes.php');
$rulecollection = new RuleMytypeCollection($_SESSION['glpiactive_entity']);
include (GLPI_ROOT . "/front/rule.common.php");
• front/rulemytype.form.php
<?php
include ('../inc/includes.php');
$rulecollection = new RuleMytypeCollection($_SESSION['glpiactive_entity']);
include (GLPI_ROOT . "/front/rule.common.form.php");
<?php
...
$CFG_GLPI["rulecollections_types"] = [
'RuleImportEntityCollection',
'RuleImportComputerCollection',
'RuleMailCollectorCollection',
'RuleRightCollection',
'RuleSoftwareCategoryCollection',
'RuleTicketCollection',
'RuleMytypeCollection' // <-- My type is added here
];
<?php
function plugin_init_myplugin() {
...
$Plugin->registerClass(
'PluginMypluginRuleMytypeCollection',
['rulecollections_types' => true]
);
...
<?php
...
$output = $rules->processAllRules(
$input,
$output,
(continues on next page)
3.5.5 Dictionaries
<?php
...
'MonitorModel', 'MonitorType',
'NetworkEquipmentModel', 'NetworkEquipmentType
˓→',
'OperatingSystem', 'OperatingSystemServicePack
˓→',
'OperatingSystemVersion', 'PeripheralModel',
'PeripheralType', 'PhoneModel', 'PhoneType',
'Printer', 'PrinterModel', 'PrinterType',
'Software', 'OperatingSystemArchitecture',
'RuleMytypeCollection' // <-- My type is added
˓→here
);
3.6 Translations
Main GLPI language is british english (en_GB). All string in the source code must be in english, and marked as
translatable, using some convenient functions.
Since 0.84; GLPI uses gettext for localization; and Transifex is used for translations. If you want to help translating
GLPI, please register on transifex and join our translation mailing list
What the system is capable to do:
• replace variables (on LTR and RTL languages),
• manage plural forms,
• add context informations,
• ...
3.6. Translations 57
GLPI Developer Documentation Documentation, Release 9.4 & dev
3.6.1 Functions
There are several standard functions you will have to use in order to get translations. Remember the tranlsation domain
will be glpi if not defined; so, for plugins specific translations, do not forget to set it!
Note: All translations functions take a $domain as argument; it defaults to glpi and must be changed when you
are working on a plugin.
Simple translation
When you have a “simple” string to translate, you may use several functions, depending on the particular use case:
• __($str, $domain='glpi') (what you will probably use the most frequently): just translate a string,
• _x($ctx, $str, $domain='glpi'): same as __() but provide an extra context,
• __s($str, $domain='glpi'): same as __() but escape HTML entities,
• _sx($ctx, $str, $domain='glpi'): same as __() but provide an extra context and escape HTML
entities,
When you have a string to translate, but which rely on a count or something. You may as well use several functions,
depending on the particular use case:
• _n($sing, $plural, $nb, $domain='glpi') (what you will probably use the most frequently):
give a string for singular form, another for plural form, and set current “count”,
• _sn($str, $domain='glpi'): same as _n() but escape HTML entities,
• _nx($ctx, $str, $domain='glpi'): same as _n() but provide an extra context,
Handle variables
You may want to replace some parts of translations; for some reason. Let’s say you would like to display current page
on a total number of pages; you will use the sprintf method. This will allow you to make replacements; but without
relying on arguments positions. For example:
<?php
$pages = 20; //total number of pages
$current = 2; //current page
$string = sprintf(
__('Page %1$s on %2$s'),
$pages,
$total
);
echo $string; //will display: "Page 2 on 20"
In the above example, %1$s will always be replaced by 2; even if places has been changed in some translations.
Warning: You may sometimes see the use of printf() which is an equivalent that directly output (echo) the
result. This should be avoided!
3.7.1 Goals
3.7.2 Profiles
The Profile class (corresponding to glpi_profiles table) stores each set of rights.
A profile has a set of base fields independent of sub rights and, so, could:
• be defined as default for new users (is_default field).
• force the ticket creation form at the login (create_ticket_on_login field).
• define the interface used (interface field):
– helpdesk (self-service users)
– central (technician view)
<?php
...
define("READ", 1);
define("UPDATE", 2);
define("CREATE", 4);
define("DELETE", 8);
define("PURGE", 16);
define("ALLSTANDARDRIGHT", 31);
define("READNOTE", 32);
define("UPDATENOTE", 64);
define("UNLOCK", 128);
So, for example, to have the right to READ and UPDATE an itemtype, we’ll have a right value of 3.
As defined in this above block, we have a computation of all standards right = 31:
READ (1)
\+ UPDATE (2)
\+ CREATE (4)
\+ DELETE (8)
\+ PURGE (16)
= 31
If you need to extends the possible values of rights, you need to declare these part into your itemtype, simplified
example from Ticket class:
<?php
...
...
return $values;
}
...
The new rights need to be checked by your own functions, see check rights
Each itemtype class which inherits from CommonDBTM will benefit from standard right checks. See the following
methods:
• canView
• canUpdate
• canCreate
• canDelete
• canPurge
If you need to test a specific rightname against a possible right, here is how to do:
<?php
if (Session::haveRight(self::$rightname, CREATE)) {
// OK
}
In this snippet, the READ | CREATE do a bitwise operation to get the sum of these rights and the & SQL operator do
a logical comparison with the current value in the DB.
These classes permits to manage the relation between items and so have properties to propagate rights from their
parents.
<?php
...
}
...
}
3.8.1 Goals
Provide a scheduler for background tasks used by GLPI and its plugins.
The entry point of automatic actions is the file front/cron.php. On each execution, it executes a limited number
of automatic actions.
There are two ways to wake up the scheduler :
• when a user browses in GLPI (the internal mode)
• when the operating system’s scheduler calls front/cron.php (the external mode)
When GLPI generates an HTML page for a browser, it adds an invisible image generated by front/cron.php.
This way, the automatic action runs in a separate process and does not impact the user.
The automatic actions are defined by the CronTask class. GLPI defines a lot of them for its own needs. They are
created in the installation or upgrade process.
3.8.3 Implementation
Automatic actions could be related to an itemtype and the implemention is defined in its class or haven’t any itemtype
relation and are implemented directly into CronTask class.
When GLPI shows a list of automatic actions, it shows a short description for each item. The description is gathered
in the static method cronInfo() of the itemtype.
<?php
class QueuedNotification extends CommonDBTM {
// ...
/**
* Give cron information
*
* @param $name : automatic action's name
*
* @return arrray of information
**/
static function cronInfo($name) {
switch ($name) {
case 'queuednotification' :
return array('description' => __('Send mails in queue'),
'parameter' => __('Maximum emails to send at once'));
}
return [];
}
/**
* Cron action on notification queue: send notifications in queue
*
* @param CommonDBTM $task for log (default NULL)
*
* @return integer either 0 or 1
**/
static function cronQueuedNotification($task=NULL) {
global $DB, $CFG_GLPI;
if (!$CFG_GLPI["notifications_mailing"]) {
return 0;
}
$cron_status = 0;
(continues on next page)
// Send mail at least 1 minute after adding in queue to be sure that process on
˓→ it is finished
$send_time = date("Y-m-d H:i:s", strtotime("+1 minutes"));
$result = $eventclass::send($data);
if ($result !== false && count($result)) {
$cron_status = 1;
if (!is_null($task)) {
$task->addVolume($result);
}
}
}
return $cron_status;
}
// ...
If the argument $task is a CronTask object, the method must increment the quantity of actions done. In this example,
each notification type reports the wuantity of notification processed and is added to the task’s volume.
Automatic actions are defined in the empty schema located in install/mysql/. Use the existing sql queries
creating rows in the table glpi_crontasks as template for a new automatic action.
To handle upgrade from a previous version, the new automatic actions must be added in the appropriate update file
install/update_xx_to_yy.php.
<?php
// Register an automatic action
CronTask::register('QueuedNotification', 'QueuedNotification', MINUTE_TIMESTAMP,
array(
'comment' => '',
'mode' => CronTask::MODE_EXTERNAL
));
Note: The name of an automatic action is actually the method’s name without the prefix cron. In the example, the
method cronQueuedNotification implements the automatic action named QueuedNotification.
3.9 Tools
Differents tools are available on the tools folder; here is an non exhaustive list of provided features.
3.9.1 locale/
The locale directory contains several scripts used to maintain translations along with Transifex services:
• extract_template.sh is used to extract translated string to the POT file (before sending it to Transifex),
• locale\update_mo.pl compiles MO files from PO file (after they’ve been updated from transifex).
3.9.2 genapidoc.sh
Generate GLPI phpdoc using apigen. apigen command must be available in your path.
Generated documentation will be available in the api directory. Note that you can also look at the online version.
3.9.3 convert_search_options.php
Search options have changed in GLPI 9.2 (see PR #1396). This script is a helper to convert existing search options to
new way.
Note: The script output can probably not be used as is; but it would probably help you a lot!
3.9.4 make_release.sh
3.9. Tools 65
GLPI Developer Documentation Documentation, Release 9.4 & dev
3.9.5 modify_headers.pl
3.9.6 getsearchoptions.php
This script is designed to be called from a browser, or from the command line. It will display existing search options
for an item specified with the type argument.
For example, open http://localhost/glpi/tools/getsearchoptions.php?type=Computer, and
you will see search options for Computer type.
On command line, you can get the exact same result entering:
3.9.7 generate_bigdump.php
This script is designed to generate many data in your GLPI instance. It relies on the generate_bigdump.
function.php file.
Note: Following scripts are not yet documented. . . Feel free to open a pull request to add them!
• test_langfiles.php
• testmail.php
• testunit.php
• update_registered_ids.php: Purge history with some criteria
Warning: Those tools are outdated, and kept for reference, or need some work to be working again. Use them at
your own risks, or do not use them at all :)
phpunit/
This directory contains a set of unit tests that have not really been integrated in the project. Since, some unit tests have
been rewritten, but not everything has been ported :/
php.vim
A vimfile for autocompletion and highlithing in VIM. This one is very outaded; it should be replaced with a most
recent version, or being removed.
3.10 Extra
The extra config/local_define.php file will be loaded if present. It permit you to change some GLPI frame-
work configurations.
Logging level is declared with the GLPI_LOG_LVL constant; and rely on available Monolog levels. The default log
level will change if debug mode is enabled on GUI or not. To change logging level to ERROR, add the following to
your local_define.php file:
<?php
define('GLPI_LOG_LVL', \Monolog\Logger::ERROR);
Note: Once you’ve declared a logging level, it will always be used. It will no longer take care of the debug mode.
In some cases, during development, you may want to test notifications that can be sent. Problem is you will have
to make sure you are not going to sent fake email to your real users if you rely on a production database copy for
example.
3.10. Extra 67
GLPI Developer Documentation Documentation, Release 9.4 & dev
You can define a unique email recipient for all emails that will be sent from GLPI. Original recipient address will be
added as part of the message (for you to know who was originally targetted). To get all sent emails delivered on the
[email protected] email addresse, use the GLPI_FORCE_MAIL in the local_define.php file:
<?php
define('GLPI_FORCE_MAIL', '[email protected]');
CSRF checks will prevent for example a same form to be sent twice. While this is the expected behavior for the appli-
cation, this may be a pain during development or debugging. You can therefore use the GLPI_USE_CSRF_CHECK
constant in the local_define.php file:
<?php
define('GLPI_USE_CSRF_CHECK', 0);
Checklists
Here is the process you must follow when you are reviewing a PR.
1. Make sure the destination branch is the correct one:
• master for new features,
• xx/bugfixes for bug fixes
2. Check if unit tests are not failing,
3. Check if coding standards checks are not failing,
4. Review the code itself. It must follow GLPI’s coding standards,
5. Using the Github review process, approve, request changes or just comment the PR,
• If some new methods are added, or if the request made important changes in the code, you should ask the
developer to write some more unit tests
6. A PR can be merged if two developers approved it, or if one developer approved it more than one day ago,
7. A bugfix PR that has been merged into the xx/bugfixes branch must be reported on the master branch. If the
master already contains many changes, you may have to change some code before doing this. If changes are
consequent, maybe should you open a new PR against the master branch for it,
8. Say thanks to the contributor :-)
69
GLPI Developer Documentation Documentation, Release 9.4 & dev
Once a major release has been finished, it’s time to think about the next one!
You’ll have to remember a few steps in order to get that working well:
• bump version in config/define.php
• create SQL empty script (copying last one) in install/mysql/glpi-{version}-empty.sql
• change empty SQL file calls in inc/toolbox.class.php (look for the $DB->runFile call)
• create a PHP migration script copying provided template install/update_xx_xy.tpl.php
– change its main comment to reflect reality
– change method name
– change version in displayTitle and setVersion calls
• add the new case in install/update.php and tools/cliupdate.php; that will include your new
PHP migration script and then call the function defined in it
• change the include and the function called in the --force option part of the tools/cliupdate.php
script
That’s all, folks!
70 Chapter 4. Checklists
CHAPTER 5
Plugins
GLPI provides facilities to develop plugins, and there are many plugins that have been already published.
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.
5.1 Guidelines
Real structure will depend of what your plugin propose. See requirements to find out what is needed. You may also
want to take a look at GLPI File Hierarchy Standard.
Warning: The main directory name of your plugin may contain only alphanumeric characters (no - or _ or
accentued characters or else).
• MyPlugin
– front
* ...
71
GLPI Developer Documentation Documentation, Release 9.4 & dev
– inc
* ...
– locale
* ...
– tools
* ...
– README.md
– LICENSE
– setup.php
– hook.php
– MyPlugin.xml
– MyPlugin.png
– ...
– ...
• front will host all PHP files directly used to display something to the user,
• inc will host all classes,
• if you internationalize your plugin, localization files will be found under the locale directory,
• if you need any scripting tool (like something to extract or update your translatable strings), you can put them
in the tools directory
• a README.md file describing the plugin features, how to install it, and so on,
• a LICENSE file contaiing the license,
• MyPlugin.xml and MyPlugin.png can be used to reference your plugin on the plugins directory website,
• the required setup.php and hook.php 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.
5.1.2 Versionning
We recommand you to use semantic versionning for you plugins. You may find existing plugins that have adopted
another logic; some have reasons, others don’t. . . Well, it is up to you finally :-)
Whatever the versionning logic you adopt, you’ll have to be consistent, it is not easy to change it without breaking
things, once you’ve released something.
72 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
5.1.3 ChangeLog
Many projects make releases without providing any changlog file. It is not simple for any end user (whether a developer
or not) to read a repository log or a list of tickets to know what have changed between two releases.
Keep in mind it could help users to know what have been changed. To achieve this, take a look at Keep an ChangeLog,
it will exaplin you some basics and give you guidelines to maintain sug a thing.
Just like GLPI, you should use the composer tool to manage third party libraries for your plugin.
5.2 Requirements
• plugin will be installed by creating a directory in the plugins directory of the GLPI instance,
• plugin directory name should never change,
• each plugin must at least provides setup.php and hook.php files,
• if your plugin requires a newer PHP version than GLPI one, or extensions that are not mandatory in core; it is
up to you to check that in the install process.
5.2.1 setup.php
The plugin’s setup.php file will be automatically loaded from GLPI’s core in order to get its version, to check pre-
requisites, etc.
This is a good practice, thus not mandatory, to define a constant name {PLUGINNAME}_VERSION in this file.
This is a minimalist example, for a plugin named myexample (functions names will contain plugin name):
<?php
define('MYEXAMPLE_VERSION', '1.2.10');
/**
* Init the hooks of the plugins - Needed
*
* @return void
*/
function plugin_init_myexample() {
global $PLUGIN_HOOKS;
//required!
$PLUGIN_HOOKS['csrf_compliant']['myexample'] = true;
/**
* Get the name and the version of the plugin - Needed
(continues on next page)
5.2. Requirements 73
GLPI Developer Documentation Documentation, Release 9.4 & dev
/**
* Optional : check prerequisites before install : may print errors or add to message
˓→after redirect
*
* @return boolean
*/
function plugin_myexample_check_prerequisites() {
//do what the checks you want
return true;
}
/**
* Check configuration process for plugin : need to return true if succeeded
* Can display a message only if failure and $verbose is true
*
* @param boolean $verbose Enable verbosity. Default to false
*
* @return boolean
*/
function plugin_myexample_check_config($verbose = false) {
if (true) { // Your configuration check
return true;
}
if ($verbose) {
echo "Installed, but not configured";
}
return false;
}
Plugin informations provided in plugin_version_myexample method will be displayed in the GLPI plugins
user interface.
Requirements checking
Since GLPI 9.2; it is possible to provide some requirement informations along with the informations array. Those
informations are not mandatory, but we encourage you to migrate :)
74 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
Warning: Even if this has been deprecated for a wile, many plugins continue to provide a minGlpiVersion
entry in the informations array. If this value is set; it will be automatically used as minimal GLPI version.
In order to set your requirements, add a requirements entry in the plugin_version_myexample informa-
tions array. Let’s say your plugin is compatible with a version of GLPI comprised between 0.90 and 9.2; with a
minimal version of PHP set to 7.0. The method would look like:
<?php
function plugin_version_myexample() {
return [
'name' => 'Plugin name that will be displayed',
'version' => MYEXAMPLE_VERSION,
'author' => 'John Doe and <a href="http://foobar.com">Foo Bar</a>',
'license' => 'GLPv3',
'homepage' => 'http://perdu.com',
'requirements' => [
'glpi' => [
'min' => '0.90',
'max' => '9.2'
],
'php' => [
'min' => '7.0'
]
]
];
}
5.2. Requirements 75
GLPI Developer Documentation Documentation, Release 9.4 & dev
• the mysqli extension is mandatory; extension_loaded() function will be used for check;
• the fileinfo extension is mandatory; class_exists() function will be used for check;
• the json extension is madatory; function_exists() function will be used for check;
• the imap extension is not mandatory.
Note: Optionnal extensions are not yet handled in the checks function; but will probably be in the future. You can
add them to the configuration right now :)
Without using automatic requirements; it’s up to you to check with something like the following in the
plugin_myexample_check_prerequisites:
Warning: Automatic requirements and manual checks are not exclusive. Both will be played! If you want to
use automatic requirements with GLPI >= 9.2 and still provide manual checks for older versions; be careful not to
indicate different versions.
<?php
// Version check
if (version_compare(GLPI_VERSION, '9.1', 'lt') || version_compare(GLPI_VERSION, '9.
˓→2', 'ge')) {
if (method_exists('Plugin', 'messageIncompatible')) {
//since GLPI 9.2
Plugin::messageIncompatible('core', 9.1, 9.2);
} else {
echo "This plugin requires GLPI >= 9.1 and < 9.2";
}
return false;
}
Note: Since GLPI 9.2, you can rely on Plugin::messageIncompatible() to display internationalized mes-
sages when GLPI or PHP versions are not met.
On the same model, you can use Plugin::messageMissingRequirement() to display internationalized mes-
sage if any extension, plugin or GLPI parameter is missing.
76 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
5.2.2 hook.php
This file will contains hooks that GLPI may call under some user actions. Refer to core documentation to know more
about available hooks.
For instance, a plugin need both an install and an uninstall hook calls. Here is the minimal file:
<?php
/**
* Install hook
*
* @return boolean
*/
function plugin_myexample_install() {
//do some stuff like instanciating databases, default values, ...
return true;
}
/**
* Uninstall hook
*
* @return boolean
*/
function plugin_myexample_uninstall() {
//to some stuff, like removing tables, generated files, ...
return true;
}
This will install the latest version of the coding-standard used in GLPI core. If you want to use an loder version of the
checks (for example if you have a huge amount of work to fix!), you can specify a version in the above command like
glpi-project/coding-standard:0.5. Refer to the coding-standard project changelog to know more ;)
You can then for example add a line in your .travis.yml file to automate checking:
script:
- vendor/bin/phpcs -p --ignore=vendor --standard=vendor/glpi-project/coding-
˓→standard/GlpiStandard/ .
Note: Coding standards and theirs checks are enabled per default using the empty plugin facilities
5.2. Requirements 77
GLPI Developer Documentation Documentation, Release 9.4 & dev
5.3 Database
Warning: A plugin should never change core’s database! It just add its own tables to manage its own data.
Of course, plugins rely on GLPI database model and must therefore respect database naming 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 provided from GLPI core. Please
refer to this documentation do know more about various Migration possibilities.
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 function 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:
<?php
/**
* Install hook
*
* @return boolean
*/
function plugin_myexample_install() {
global $DB;
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:
<?php
/**
* Install hook
(continues on next page)
78 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
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'
);
}
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 :-)
You will have to drop all plugins tables when it will be uninstalled. Just put your code into the
plugin_{myplugin]_uninstall function:
<?php
/**
* Uninstall hook
*
* @return boolean
*/
function plugin_myexample_uninstall() {
(continues on next page)
5.3. Database 79
GLPI Developer Documentation Documentation, Release 9.4 & dev
return true;
}
In most of the cases; your plugin will have to manage several objects
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 common core types (CommonDBTM in our example).
Extra operations are aslo described in the tips and tricks page, you may want to take a look at it.
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:
<?php
class PluginMyExampleMyObject extends CommonDBTM {
public function showForm($ID, $options = []) {
global $CFG_GLPI;
(continues on next page)
80 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
$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:
<?php
include ("../../../inc/includes.php");
Html::displayNotFoundError();
}
(continues on next page)
Search::show('PluginMyExampleMyObject');
Html::footer();
} else {
//View is not granted.
Html::displayRightError();
}
Html::displayNotFoundError();
}
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
(continues on next page)
82 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
5.5 Hooks
GLPI provides a certain amount of “hooks”. Their goal is for plugins (mainly) to work on certain places of the
framework; like when an item has been added, updated, deleted, . . .
This page describes current existing hooks; but not the way they must be implemented from plugins. Please refer to
the plugins development documentation.
Usage
Aside from their goals or when/where they’re called; you will see three types of different hooks. Some will receive an
item as parameter, others an array of parameters, and some won’t receive anything. Basically, the way they’re declared
into your plugin, and the way you’ll handle that will differ.
All hooks called are defined in the setup.php file of your plugin; into the $PLUGIN_HOOKS array. The first key
is the hook name, the second your plugin name; values can be just text (to call a function declared in the hook.php
file), or an array (to call a static method from an object):
<?php
//call a function
$PLUGIN_HOOKS['hook_name']['plugin_name'] = 'function_name';
//call a static method from an object
$PLUGIN_HOOKS['other_hook']['plugin_name'] = ['ObjectName', 'methodName'];
Without parameters
Those hooks are called without any parameters; you cannot attach them to any itemtype; basically they’ll permit youi
to display extra informations. Let’s say you want to call the display_login hook, in you setup.php you’ll add
5.5. Hooks 83
GLPI Developer Documentation Documentation, Release 9.4 & dev
something like:
<?php
$PLUGIN_HOOKS['display_login']['myPlugin'] = 'myplugin_display_login';
You will also have to declare the function you want to call in you hook.php file:
<?php
/**
* Display informations on login page
*
* @return void
*/
public function myplugin_display_login () {
echo "That line will appear on the login page!";
}
The hooks that are called without parameters are: display_central, post_init init_session,
change_entity, change_profile`, display_login and add_plugin_where.
Those hooks will send you an item instance as parameter; you’ll have to attach them to the itemtypes you want to
apply on. Let’s say you want to call the pre_item_update hook for Computer and Phone item types, in your
setup.php you’ll add something like:
<?php
$PLUGIN_HOOKS['pre_item_update']['myPlugin'] = [
'Computer' => 'myplugin_updateitem_called',
'Phone' => 'myplugin_updateitem_called'
];
You will also have to declare the function you want to call in you hook.php file:
<?php
/**
* Handle update item hook
*
* @param CommonDBTM $item Item instance
*
* @return void
*/
public function myplugin_updateitem_called (CommonDBTM $item) {
//do everything you want!
//remember that $item is passed by reference (it is an abject)
//so changes you will do here will be used by the core.
if ($item::getType() === Computer::getType()) {
//we're working with a computer
} elseif ($item::getType() === Phone::getType()) {
//we're working with a phone
}
}
The hooks that are called with item as parameter are: item_empty, pre_item_add, post_prepareadd,
item_add, pre_item_update, item_update, pre_item_purge, pre_item_delete,
item_purge, item_delete, pre_item_restore, item_restore, autoinventory_information,
item_add_targets, item_get_events, item_action_targets, item_get_datas.
84 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
These hooks will work just as the hooks with item as parameter expect they will send you an array of parameters
instead of only an item instance. The array will contain two entries: item and options, the first one is the item
instance, the second options that have been passed:
<?php
/**
* Function that handle a hook with array of parameters
*
* @param array $params Array of parameters
*
* @return void
*/
public function myplugin_params_hook(array $params) {
print_r($params);
//Will display:
//Array
//(
// [item] => Computer Object
// (...)
//
// [options] => Array
// (
// [_target] => /front/computer.form.php
// [id] => 1
// [withtemplate] =>
// [tabnum] => 1
// [itemtype] => Computer
// )
//)
}
The hooks that are called with an array of parameters are: post_item_form, pre_item_form,
pre_show_item, post_show_item, pre_show_tab, post_show_tab, item_transfer.
Some hooks will receive a specific array as parameter, they will be detailled below.
Unclassified
5.5. Hooks 85
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
$hook_params = [
'sub_type' => 'an item type',
'rule_id' => 'tule id',
'input' => array(), //original input
'output' => array() //output modified by rule
];
86 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
Notifications
Usage
Functions hooks declarations are the same than standards hooks one. The main difference is that the hook will wait as
output what have been passed as argument.
5.5. Hooks 87
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
/**
* Handle hook function
*
* @param array $$data Array of something (assuming that's what wer're receiving!)
*
* @return array
*/
public function myplugin_updateitem_called ($data) {
//do everything you want
//return passed argument
return $data;
}
Existing hooks
unlock_fields After a fields has been unlocked. Will receive the $_POST array used for the call.
restrict_ldap_auth Aditional LDAP restrictions at connection. Must return a boolean. The dn string is passed
as parameter.
undiscloseConfigValue Permit plugin to hide fields that should not appear from the API (like configuration
fields, etc). Will receive the requested fields list.
infocom Additional infocom informations oin an item. Will receive an item instance as parameter, is expected to
return a table line (<tr>).
retrieve_more_field_from_ldap Retrieve aditional fields from LDAP for a user. Will receive the current
fields lists, is expected to return a fields list.
retrieve_more_data_from_ldap Retrieve aditional data from LDAP for a user. Will receive current fields
list, is expected to return a fields list.
display_locked_fields To manage fields locks. Will receive an array with item and header entries. Is
expected to output a table line (<tr>).
migratetypes Item types to migrate, will receive an array of types to be updated; must return an aray of item
types to migrate.
Some hooks are automated; they’ll be called if the relevant function exists in you plugin’s hook.php file. Required
function must be of the form plugin_{plugin_name}_{hook_name}.
MassiveActionsFieldsDisplay Add massive actions. Will receive an array with item (the item type) and
options (the search options) as input. These hook have to output its content, and to return true if there is some
specific output, false otherwise.
dynamicReport Add parameters for print. Will receive the $_GET array used for query. Is expected to return an
array of parameters to add.
AssignToTicket Declare types an ITIL object can be assigned to. Will receive an empty array adn is expected to
return a list an array of type of the form:
<?php
return [
'TypeClass' => 'label'
];
88 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
return [
'Class::method' => 'label'
];
getDropDown To declare extra dropdowns. Will not receive any parameter, and is expected to return an array of the
form:
<?php
return [
'Class::method' => 'label'
];
rulePrepareInputDataForProcess Provide data to process rules. Will receive an array with item (data
used to check criteria) and params (the parameters) keys. Is expected to retrun an array of rules.
executeActions Actions to execute for rule. Will receive an array with output, params ans action keys. Is
expected to return an array of actions to execute.
preProcessRulePreviewResults
use_rules
<?php
array(
'rule_itemtype' => 'name fo the rule itemtype',
'values' => array(
'input' => 'input array',
'params' => 'array of parameters'
)
);
ruleImportComputer_addGlobalCriteria Add global criteria for computer import. Will receive an array
of global criteria, is expected to return global criteria array.
ruleImportComputer_getSqlRestriction Adds SQL restriction to links. Will receive an array of the
form:
5.5. Hooks 89
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
array(
'where_entity' => 'where entity clause',
'input' => 'input array',
'criteria' => 'complex cirteria array',
'sql_where' => 'sql where clause as string',
'sql_from' => 'sql from clause as string'
)
5.6.1 Goals
Plugins may need to run automatic actions in background, or at regular interval. GLPI provides a task scheduler for
itself and its plugins.
A plugin must implement its automatic action the same way as GLPI does, except the method is located in a plugin’s
itemtype. See crontasks.
A plugin must register its automatic action the same way as GLPI does in its upgrade process. See crontasks.
Plugins can use the core’s massive actions for its own itemtypes.
They just need to aditionnaly define a hook in their init function (setup.php):
90 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
function plugin_init_example() {
$PLUGIN_HOOKS['use_massive_action']['example'] = 1;
}
But they can also add specific massive actions to core’s itemtypes. First, in their hook.php file, they must de-
clare a new definition into a plugin_pluginname_MassiveActions function, ex addition of new action for
Computer:
<?php
function plugin_example_MassiveActions($type) {
$actions = [];
switch ($type) {
case 'Computer' :
$myclass = PluginExampleExample;
$action_key = 'DoIt';
$action_label = __("plugin_example_DoIt", 'example');
$actions[$myclass.MassiveAction::CLASS_ACTION_SEPARATOR.$action_key]
= $action_label;
break;
}
return $actions;
}
Next, in the class defined int the definition, we can use the showMassiveActionsSubForm and
processMassiveActionsForOneItemtype in the same way as core documentation for massive actions:
<?php
switch ($ma->getAction()) {
case 'DoIt':
echo __("fill the input");
echo Html::input('myinput');
echo Html::submit(__('Do it'), array('name' => 'massiveaction'))."</span>
˓→ ";
return true;
}
return parent::showMassiveActionsSubForm($ma);
}
switch ($ma->getAction()) {
case 'DoIt' :
$input = $ma->getInput();
if ($item->getFromDB($id)
&& $item->doIt($input)) {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
} else {
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
$ma->addMessage(__("Something went wrong"));
}
}
return;
}
parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
}
}
In order to add a new tab on a core object, you will have to:
• register your class against core object(s) telling it you will add a tab,
• use getTabNameForItem() to give tab a name,
• use displayTabContentForItem() to display tab contents.
First, in the plugin_init_{plugin_name} function, add the following:
<?php
//[...]
Plugin::registerClass(
'PluginMyExampleMyClass', [
'addtabon' => [
'Computer',
'Phone'
]
]
);
//[...]
<?php
function getTabNameForItem(CommonGLPI $item, $withtemplate=0) {
switch ($item::getType()) {
case Computer::getType():
case Phone::getType():
return __('Tab from my plugin', 'myexampleplugin');
(continues on next page)
92 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
switch ($item::getType()) {
case Computer::getType():
//display form for computers
$this->displayTabContentForComputer($item);
break;
case Phone::getType():
$this->displayTabContentForPhone($item);
break;
}
if ($item->getType() == 'ObjetDuCoeur') {
$monplugin = new self();
$ID = $item->getField('id');
// j'affiche le formulaire
$monplugin->nomDeLaFonctionQuiAfficheraLeContenuDeMonOnglet();
}
return true;
}
On the above example, we have used two different methods to display tab, depending on item type. You could of
course use only one if there is no (or minor) differences at display.
In order to add a new tab on your plugin object, you will have to:
• use defineTabs() to register the new tab,
• use getTabNameForItem() to give tab a name,
• use displayTabContentForItem() to display tab contents.
Then, in your inc/myclass.php:
<?php
function defineTabs($options=array()) {
$ong = array();
//add main tab for current object
$this->addDefaultFormTab($ong);
//add core Document tab
$this->addStandardTab(__('Document'), $ong, $options);
return $ong;
}
(continues on next page)
/**
* Définition du nom de l'onglet
**/
function getTabNameForItem(CommonGLPI $item, $withtemplate=0) {
switch ($item::getType()) {
case __CLASS__:
return __('My plugin', 'myexampleplugin');
break;
}
return '';
}
/**
* Définition du contenu de l'onglet
**/
static function displayTabContentForItem(CommonGLPI $item, $tabnum=1,
˓→$withtemplate=0) {
switch ($item::getType()) {
case __CLASS__:
self::myStaticMethod();
break;
}
return true;
}
On the same model you create one tab, you may add several tabs.
<?php
function getTabNameForItem(CommonGLPI $item, $withtemplate=0) {
$ong = [
__('My first tab', 'myexampleplugin'),
___('My second tab', 'myexampleplugin')
];
return $ong;
}
switch ($tabnum) {
case 1 : //"My first tab"
//do something
break;
case 2 : //"My second tab""
//do something else
break;
}
return true;
}
94 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
<?php
function plugin_myexampleplugin_getDropdown() {
return ['PluginMyExampleMyObject' => PluginMyExampleMyObject::getTypeName(2)];
}
A few steps are required to setup the mode. In the init method (setup.php file); register the mode:
<?php
public function plugin_init_sms {
//[...]
if ($plugin->isActivated('sms')) {
Notification_NotificationTemplate::registerMode(
Notification_NotificationTemplate::MODE_SMS, //mode itself
__('SMS', 'plugin_sms'), //label
'sms' //plugin name
);
}
//[...]
}
In order to make you new notification active, you will have to declare a notifications_{MODE} variable in the
main configuration: You will add it at install time, and remove it on uninstall. . . In the hook.php file:
<?php
function plugin_sms_install() {
Config::setConfigurationValues('core', ['notifications_sms' => 0]);
(continues on next page)
function plugin_sms_uninstall() {
$config = new Config();
$config->deleteConfigurationValues('core', ['notifications_sms']);
return true;
}
5.9.2 Settings
You will probably need some configuration settings to get your notifications mode to work. You can register and
retrieve additional configuration values using core Config object:
<?php
//set configuration
Config::setConfigurationValues(
'plugin:sms', //context
[ //values
'server' => '',
'port' => ''
]
);
//get configuration
$conf = Config::getConfigurationValues('plugin:sms');
//$conf will be ['server' => '', 'port' => '']
That said, we need to create a class to handle the settings, and a front file to display them. The class must be named
PluginSmsNotificationSmsSetting and must be in the inc/notificationsmssetting.class.
php. It have to extends the NotificationSetting core class :
<?php
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
/**
* This class manages the sms notifications settings
*/
class PluginSmsNotificationSmsSetting extends NotificationSetting {
96 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
$conf = Config::getConfigurationValues('plugin:sms');
$params = [
'display' => true
];
$params = array_merge($params, $options);
if ($CFG_GLPI['notifications_sms']) {
//TODO
$out .= "<tr><td colspan='4'>" . __('SMS notifications are not implemented
˓→yet.', 'sms') . "</td></tr>";
} else {
$out .= "<tr><td colspan='4'>" . __('Notifications are disabled.') . " <a
˓→href='{$CFG_GLPI['root_doc']}/front/setup.notification.php'>" . _('See configuration
˓→') . "</td></tr>";
}
$options['candel'] = false;
if ($CFG_GLPI['notifications_sms']) {
$options['addbuttons'] = array('test_sms_send' => __('Send a test SMS to you
˓→', 'sms'));
//Ignore display parameter since showFormButtons is now ready :/ (from all but
˓→ tests)
echo $out;
$this->showFormButtons($options);
}
}
The front form file, located at front/notificationsmssetting.form.php will be quite simple. It handles
the display of the configuration form, update of the values, and test send (if any):
<?php
include ('../../../inc/includes.php');
Session::checkRight("config", UPDATE);
$notificationsms = new PluginSmsNotificationSmsSetting();
if (!empty($_POST["test_sms_send"])) {
PluginSmsNotificationSms::testNotification();
Html::back();
(continues on next page)
Html::header(Notification::getTypeName(Session::getPluralNumber()), $_SERVER['PHP_SELF
˓→'], "config", "notification", "config");
Html::footer();
5.9.3 Event
Once the new mode has been enabled; it will try to raise core events. You will need to create an event
class named PluginSmsNotificationEventSms that implements NotificationEventInterface and extends
NotificationEventAbstract <https://forge.glpi-project.org/apidoc/class-NotificationEventAbstract.html> in the inc/
notificationeventsms.php.
Methods to implement are:
• getTargetFieldName: defines the name of the target field;
• getTargetField: populates if needed the target field to use. For a SMS plugin, it would retrieve the phone
number from users table for example;
• canCron: whether notification can be called from a crontask. For the SMS plugins, it would be true. It is set
to false for ajax based events; because notifications are requested from user browser;
• getAdminData: as global admin is not a real user; you can define here the data used to send the notification;
• getEntityAdminData: same as the above, but for entities admins rather than global admin;
• send: method that will really send data.
The raise method declared in the interface is implemented in the abstract class; since it should be used as it for
every mode. If you want to do extra process in the raise method, you should override the extraRaise method.
This is done in the core to add signatures in the mailing for example.
Note: Notifications uses the QueueNotification to store its data. Each notification about to be sent will be
stored in the relevant table. Rows are updated once the notification has really be send (set is_deleted to 1 and
update sent_time.
En example class for SMS Events would look like the following:
<?php
class PluginSmsNotificationEventSms implements NotificationEventInterface {
98 Chapter 5. Plugins
GLPI Developer Documentation Documentation, Release 9.4 & dev
if (!isset($data[$field])
&& isset($data['users_id'])) {
// No phone set: get one for user
$user = new user();
$user->getFromDB($data['users_id']);
$data[$field] = $user->fields[$phone_field];
break;
}
}
}
if (!isset($data[$field])) {
//Missing field; set to null
$data[$field] = null;
}
return $field;
}
$iterator = $DB->request([
'FROM' => 'glpi_entities',
'WHERE' => ['id' => $entity]
]);
$admins = [];
return $admins;
}
(continues on next page)
5.9.4 Notification
Finally, create a NotificationSms class that implements the NotificationInterface in the inc/
notificationsms.php file.
Methods to implement are:
• check: to validate data (checking if a mail address is well formed, . . . );
• sendNotification: to store raised event notification in the QueueNotification;
• testNotification: used from settings to send a test notification.
Again, the SMS example:
<?php
class PluginSmsNotificationSms implements NotificationInterface {
return true;
}
function sendNotification($options=array()) {
$data = array();
$data['itemtype'] = $options['_itemtype'];
$data['items_id'] = $options['_items_id'];
$data['notificationtemplates_id'] = $options['_
˓→notificationtemplates_id'];
$data['entities_id'] = $options['_entities_id'];
(continues on next page)
$data['sendername'] = $options['fromname'];
$data['name'] = $options['subject'];
$data['body_text'] = $options['content_text'];
$data['recipient'] = $options['to'];
$data['mode'] = Notification_NotificationTemplate::MODE_SMS;
if (!$mailqueue->add(Toolbox::addslashes_deep($data))) {
Session::addMessageAfterRedirect(__('Error inserting sms notification to
˓→queue', 'sms'), true, ERROR);
return false;
} else {
//TRANS to be written in logs %1$s is the to email / %2$s is the subject of
˓→the mail
Toolbox::logInFile("notification",
sprintf(__('%1$s: %2$s'),
sprintf(__('An SMS notification to %s was added
˓→to queue', 'sms'),
$options['to']),
$options['subject']."\n"));
}
return true;
}
}
Packaging
Various Linux distributions provides packages (deb, rpm, . . . ) for GLPI (Debian, Mandriva, Fedora, Redhat/CentOS,
. . . ) and for some plugins. You may want to take a look at Remi’s package for Fedora/RHEL to rely on a concrete
example.
Here is some information about using and creating package:
• for users to understand how GLPI is installed
• for support to understand how GLPI work on this installation
• for packagers
6.1 Sources
GLPI public tarball is designed for ends-user; it will not fit packaging requirements. For example, this tarball bundle
a lot of third party libraries, it does not ships unit tests, etc.
A better candidate would be to retrieve directly a tarball from github as package source.
Most distributions requires that packages follows the FHS (Filesystem Hierarchy Standard):
• /etc/glpi for configuration files: config_db.php and config_db_slave.php. Prior to 9.2 release,
other files stay in glpi/config; begining with 9.2, those files have been moved;
• /usr/share/glpi for the web pages (read only dir);
• /var/lib/glpi/files for GLPI data and state information (session, uploaded documents, cache, cron,
plugins, . . . );
• /var/log/glpi for various GLPI log files.
Please refer to GLPI installation documentation in order to get GLPI paths configured.
103
GLPI Developer Documentation Documentation, Release 9.4 & dev
<Directory /usr/share/glpi>
Options None
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Allow from All
</IfModule>
</Directory>
<Directory /usr/share/glpi/install>
# 15" should be enough for migration in most case
php_value max_execution_time 900
php_value memory_limit 128M
</Directory>
<Directory /usr/share/glpi/locales>
<IfModule mod_authz_core.c>
# Apache 2.4
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
(continues on next page)
<Directory /usr/share/glpi/install/mysql>
<IfModule mod_authz_core.c>
# Apache 2.4
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Deny from All
</IfModule>
</Directory>
<Directory /usr/share/glpi/scripts>
<IfModule mod_authz_core.c>
# Apache 2.4
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order Deny,Allow
Deny from All
</IfModule>
</Directory>
For SELinux enabled distributions, you need to declare the correct context for the folders.
As an example, on Redhat based distributions:
• /etc/glpi and /var/lib/glpi: httpd_sys_script_rw_t, the web server need to write the config
file in the former and various data in the latter;
GLPI provides an internal cron for automated tasks. Using a system cron allow a more consistent and regular execu-
tion, for example when no user connected on GLPI.
Note: cron.php should be run as the web server user (apache or www-data)
You will need a crontab file, and to configure GLPI to use system cron. Sample cron configuration file (/etc/cron.
d/glpi):
# GLPI core
# Run cron from to execute task even when no user connected
*/4 * * * * apache /usr/bin/php /usr/share/glpi/front/cron.php
To tell GLPI it must use the system crontab, simply define the GLPI_SYSTEM_CRON constant to true in the
config_path.php file:
<?php
//[...]
Since most distributions prefers the use of system libraries (maintained separately); you can’t rely on the vendor
directory shipped in the public tarball; nor use composer.
The way to handle third party libraries is to provide an autoload file with paths to you system libraries. You’ll find all
requirements from the composer.json file provided along with GLPI:
<?php
$vendor = '##DATADIR##/php';
// Dependencies from composer.json
// "ircmaxell/password-compat"
// => useless for php >= 5.5
//require_once $vendor . '/password_compat/password.php';
// "jasig/phpcas"
require_once '##DATADIR##/pear/CAS/Autoload.php';
// "iamcal/lib_autolink"
require_once $vendor . '/php-iamcal-lib-autolink/autoload.php';
// "phpmailer/phpmailer"
require_once $vendor . '/PHPMailer/PHPMailerAutoload.php';
// "sabre/vobject"
require_once $vendor . '/Sabre/VObject/autoload.php';
// "simplepie/simplepie"
require_once $vendor . '/php-simplepie/autoloader.php';
// "tecnickcom/tcpdf"
require_once $vendor . '/tcpdf/tcpdf.php';
// "zendframework/zend-cache"
(continues on next page)
Note: In the above example, the ##DATADIR## value will be replaced by the correct value (/usr/share/php
for instance) from the specfile using macros. Adapt with your build system possibilities.
<?php
//[...]
define('GLPI_FONT_FREESANS', '/path/to/FreeSans.ttf');
6.9 Notes
If you want to help us improve the current documentation, feel free to open pull requests! You can see open issues and
join the documentation mailing list.
Here is a list of things to be done:
Todo:
• datafields option
• difference between searchunit and delay_unit
• dropdown translations
• giveItem
• export
• fulltext search