Writing a GLPI plugin to reopen orders

Hello.



Today we will talk about how I wrote the GLPI plugin to work with new orders created on the basis of already closed orders, or rather reopening closed orders, if additional requests from the user are received for them.

Background



For almost a month now I have been digging into the source code of the GLPI helpdesk system. And when studying this open source solution, I drew attention to the negligible amount of information for developers. The vast majority of the material is related to GLPI configuration within the interface. A little less, but you can still find information on setting up the server. And just one article (and the one in English) tells about the development of plugins for this miracle system.



From the official manuals - a couple of resources on readthedocs, plus a manual generated from phpdoc.



From the community - a chat in the telegram and a foreign forum.

The input parameters are as follows.



There is a GLPI helpdesk system. Applications from users are sent to a dedicated mailbox connected to the system. The answer of the support service also arrives in the mail. In general, all notifications about the progress of the application from the first call to the decision and closing are sent to the mail.



However, if the user considers that his problem has not been properly resolved and decides to respond to the letter about closing the request, then instead of reopening, a new request is created, and the old ticket is simply attached to it. Actually, this problem had to be solved.



So let's go.



The first step is to install the plugin generator- it greatly simplifies life by creating the skeleton of our future plugin. This can be done using the following algorithm:



  • (/path/to/glpi/plugins)
  • :



    $ ./plugin.sh YourPluginName 1.0


    YourPluginName — , 1.0 — .



And that's it, the frame is ready.



Going into the directory of the created plugin, you will see a bunch of files, of which only setup.php and hook.php are really needed. If you plan to use additional libraries, you can leave composer.json. I didn't need these, so I left only 2 necessary files.



There are two required functions in the setup.php file - plugin_init_yourpluginname and plugin_version_yourpluginname . The first initializes the plugin, the second returns information about it - name, author, version, etc.



With the second, everything is more or less clear. Therefore, we will briefly focus on the first function. It works with the global variable $ PLUGIN_HOOKS... Surely everyone knows what hooks are, but if someone is not in the know, then this is the so-called. system hooks that the plugin pulls. You can read more about hooks in the official manual .



Each plugin must register the csrf_compliant hook , otherwise you simply won't be able to activate it, there will be an error like this:





I don't know what this parameter is, until I figured out the system to the end. If there are GLPI experts among the readers, write in the comments.




In addition to the obligatory hook, you can register many others, for example, set a link to the settings page:



$PLUGIN_HOOKS['config_page']['yourpluginname'] = 'config.php';


Plugin classes are also registered in this function:

Plugin::registerClass(PluginYourpluginnameConfig::class);
, , , GLPI , . : Plugin + + . , , .



: PluginYourpluginnameConfig, config.class.php. inc ( , , , , — — , - ).



.

Let's go back to our hooks. I need to track the creation of a new cancellation ticket , so I'll register the pre_item_add hook handler . You can pass parameters to the handler, this is done using an associative array, where the key is the object of the desired entity (in my case, Ticket), and the value is the name of the handler function.



As a result, in my case, the two required functions of the setup.php file look like this:



/**
 * Init hooks of the plugin.
 * REQUIRED
 *
 * @return void
 */
function plugin_init_advtickets() {
   global $PLUGIN_HOOKS;

   Plugin::registerClass(PluginAdvticketsEvent::class);

   $PLUGIN_HOOKS['csrf_compliant']['advtickets'] = true;

   $PLUGIN_HOOKS['pre_item_add']['advtickets'] = [
       Ticket::class => 'plugin_advtickets_pre_item_add'
   ];

}

/**
 * Get the name and the version of the plugin
 * REQUIRED
 *
 * @return array
 */
function plugin_version_advtickets() {
   return [
      'name'           => 'Adv Tickets',
      'version'        => PLUGIN_ADVTICKETS_VERSION,
      'author'         => 'Roman Gonyukov',
      'license'        => '',
      'homepage'       => 'https://github.com/stayfuneral/advtickets',
      'requirements'   => [
         'glpi' => [
            'min' => '9.2',
         ]
      ]
   ];
}


The documentation says that it is possible to register static methods of classes as a handler, in which case they should look like this (by the way, it is not at all necessary to pass any parameters to the handler):



/*     */

//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'];


The handler functions themselves are stored in the hook.php file. The functions for installing and uninstalling the plugin are also stored there. The same requirements for function names are plugin_yourpluginname_function_name.



By the way, I tried to register a static method to handle the hook and pass the Ticket object to the parameters, but for some reason nothing worked for me. But with the usual function, everything worked out. Therefore, in order not to litter hook.php with unnecessary code, I registered a class that contains a hook handler method, and in the required function I simply called this method:



// hook.php

/**
 * @param Ticket $ticket
 *
 * @return bool
 */
function plugin_advtickets_pre_item_add(Ticket $ticket)
{
    return PluginAdvticketsEvent::pre_item_add_ticket($ticket);
}


If you need to install tables in a database, then the official documentation has a separate paragraph on this topic. I didn't need the tables, so I'm skipping this moment.



Well, we sort of figured out the installation / removal of the plugin, registering and calling hook handlers, now it's time to figure out how we will process the new request.



To do this, as mentioned above, I registered the PluginAdvticketsEvent class (file inc / event.php), which contains a single pre_item_add_ticket method .



class PluginAdvticketsEvent extends CommonDBTM
{
    static function pre_item_add_ticket(Ticket $ticket)
    {
        global $DB;

// ..    ,      input
        $fields = $ticket->input;

//      ,       ( ) 
        if($fields['_link']['tickets_id_2']) {

            $relatedTicketId = $fields['_link']['tickets_id_2'];

            $relatedTicketParamsForUpdate = [
                'itemtype' => \Ticket::class, //  ,    
                'items_id' => $relatedTicketId, // id 
                'users_id' => $fields['_users_id_requester'], // id  
                'users_id_editor' => 0,
                'content' => $fields['content'],  
                'date' => date('c'),
                'date_mod' => date('c'),
                'date_creation' => date('c'),
                'is_private' => 0,
                'requesttypes_id' => $fields['requesttypes_id'], //  (helpdesk, email  ..)
                'timeline_position' => 4,
                'sourceitems_id' => 0,
                'sourceof_items_id' => 0
            ];

//           (    , ),        . ,        glpi_itilfollowups
            $DB->insert('glpi_itilfollowups', $relatedTicketParamsForUpdate);

//     ,    "".        glpi_tickets
            $DB->update('glpi_tickets', [
                'status' => 1
            ], [
                'id' => $relatedTicketId
            ]);

// ..     ,   input  false.       .
            $ticket->input = false;

            return $ticket->input;
        }
    }
}


That's all. Thank you for attention. Source code, as always on github .



Useful links:



GLPI Official Website

Official Forums

Telegram Chat

Example Plugin

Documentation for

APIDoc Developers

Documentation on Plugin Creation

Article on GLPI Plugin Creation



All Articles