Escrevendo um plugin GLPI para reabrir pedidos

Olá.



Hoje vamos falar sobre como eu escrevi o plugin GLPI para trabalhar com novos pedidos criados com base em pedidos já fechados, ou melhor, reabrir pedidos fechados, caso sejam recebidos pedidos adicionais do usuário para eles.

Histórico



Por quase um mês, tenho pesquisado o código-fonte do sistema de helpdesk GLPI. E ao estudar essa solução de código aberto, chamei a atenção para a quantidade insignificante de informações para os desenvolvedores. A grande maioria do material está relacionada à configuração do GLPI na interface. Um pouco menos, mas você ainda pode encontrar informações sobre como configurar o servidor. E apenas um artigo (e o outro em inglês) fala sobre o desenvolvimento de plugins para este sistema milagroso.



Dos manuais oficiais - alguns recursos sobre readthedocs, mais um manual gerado a partir do phpdoc.



Da comunidade - um bate-papo no telegrama e um fórum estrangeiro.

Os parâmetros de entrada são os seguintes.



Existe um sistema de helpdesk GLPI. Os aplicativos dos usuários são enviados para uma caixa de correio dedicada conectada ao sistema. A resposta do serviço de suporte também chega pelo correio. Em geral, todas as notificações sobre o andamento do pedido desde a primeira chamada até a decisão e o encerramento são enviadas para o correio.



No entanto, se o usuário considerar que seu problema não foi resolvido adequadamente e decidir responder à carta sobre o fechamento do aplicativo, em vez de reabrir um novo tíquete é criado e o tíquete antigo é simplesmente vinculado a ele. Na verdade, esse problema precisava ser resolvido.



Então vamos.



O primeiro passo é instalar o gerador de plugins- simplifica muito a vida, criando o esqueleto do nosso futuro plugin. Isso pode ser feito usando o seguinte algoritmo:



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



    $ ./plugin.sh YourPluginName 1.0


    YourPluginName — , 1.0 — .



E é isso, o quadro está pronto.



Indo para o diretório do plugin que você criou, você verá um monte de arquivos, dos quais apenas setup.php e hook.php são realmente necessários. Se você planeja usar bibliotecas adicionais, pode deixar composer.json. Eu não precisava deles, então deixei apenas 2 arquivos necessários.



Existem duas funções necessárias no arquivo setup.php - plugin_init_yourpluginname e plugin_version_yourpluginname . O primeiro inicializa o plugin, o segundo retorna informações sobre ele - nome, autor, versão, etc.



Com o segundo, tudo fica mais ou menos claro. Portanto, vamos nos concentrar brevemente na primeira função. Funciona com a variável global $ PLUGIN_HOOKS... Certamente todo mundo sabe o que são ganchos, mas se alguém não sabe, esse é o assim chamado. ganchos de sistema que o plugin puxa. Você pode ler mais sobre ganchos no manual oficial .



Cada plugin deve registrar o gancho csrf_compliant , caso contrário, você simplesmente não será capaz de ativá-lo, haverá um erro como este:





Não sei o que é esse parâmetro, até que descobri o sistema até o fim. Se houver especialistas em GLPI entre os leitores, escreva nos comentários.




Além do gancho obrigatório, você pode registrar muitos outros, por exemplo, definir um link para a página de configurações:



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


As classes de plug-in também são registradas nesta função:

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



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



.

Voltemos aos nossos ganchos. Preciso acompanhar a criação de um novo tíquete de cancelamento, portanto, registrarei um manipulador de gancho pre_item_add . Você pode passar parâmetros para o manipulador, isso é feito usando um array associativo, onde a chave é o objeto da entidade desejada (no meu caso, Ticket), e o valor é o nome da função do manipulador.



Como resultado, no meu caso, as duas funções necessárias do arquivo setup.php se parecem com isto:



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


A documentação diz que é possível registrar métodos estáticos de classes como um manipulador, caso em que eles devem se parecer com isto (aliás, não é necessário passar nenhum parâmetro para o manipulador):



/*     */

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


As próprias funções do manipulador são armazenadas no arquivo hook.php. As funções para instalar e desinstalar o plugin também estão armazenadas lá. Os mesmos requisitos para nomes de funções são plugin_yourpluginname_function_name.



A propósito, tentei registrar um método estático para lidar com o gancho e passar o objeto Ticket para os parâmetros, mas por algum motivo nada funcionou para mim. Mas com a função normal, tudo deu certo. Portanto, para não encher o hook.php com código desnecessário, registrei uma classe que contém um método de manipulador de gancho e, na função necessária, simplesmente chamei este método:



// hook.php

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


Se você precisar instalar tabelas em um banco de dados, a documentação oficial possui um parágrafo separado sobre este tópico. Eu não precisava das mesas, então estou pulando este momento.



Bem, descobrimos como instalar / desinstalar o plug-in, registrar e chamar manipuladores de gancho, agora é hora de descobrir como processaremos uma nova solicitação.



Para fazer isso, conforme mencionado acima, registrei a classe PluginAdvticketsEvent (arquivo inc / event.php), que contém um único método pre_item_add_ticket .



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;
        }
    }
}


Isso é tudo. Obrigado pela atenção. Código-fonte, como sempre no github .



Links úteis:



GLPI Official Website

Fóruns oficiais

Telegram Chat

Exemplo de

documentação de plug-in para

desenvolvedores APIDoc

Documentação sobre criação de plug- ins Artigo sobre criação de plug- ins

GLPI



All Articles