Criando plugins

De Wiki Expresso V3
Ir para: navegação, pesquisa

Conteúdo

Conceito geral

O que plugins fazem:

  • Plugins adicionam funcionalidades.
  • Plugins podem consumir serviços de qualquer módulo.
  • Plugins podem alterar funcionalidades existentes em

outros módulos.

  • Plugins são injeções de dependências.

Plugins disponíveis no ExpressoV3:

  • Plugins de camada
  • Plugins de inicialização da aplicação
  • Plugins de usuários e grupos
  • Plugins de filtros de consultas
  • Plugin de validação de endereço IP
  • Plugin de estratégia para AccessLog

Plugins de camada

Conceito

O módulo Tinebase é o núcleo comum de todos os módulos no Expresso 3.

Por isso, todas as mudanças nele tem impacto nos demais.

Se você precisar que uma funcionalidade específica seja executada no Tinebase, crie um plugin de camada.

Um plugin de camada é uma classe cujo método é invocado de forma indireta por um objeto das camadas de frontend, controller e backend.

Criando um plugin de camada

Para criar um plugin de camada você deve:

  • Criar uma classe em uma biblioteca que siga o padrão Zend Framework 1, dentro da pasta library.
  • Registrar o plugin na camada que deve receber a funcionalidade, acrescentando uma linha como uma das seguintes:
Tinebase_Frontend_Abstract::attachPlugin('nomeDoMétodo', 'NomeDaClasse');
Tinebase_Controller_Abstract::attachPlugin('nomeDoMétodo', 'NomeDaClasse');
Tinebase_Backend_Abstract::attachPlugin('nomeDoMétodo', 'NomeDaClasse');

O método registrado poderá ser chamado a partir de qualquer instância da classe herdeira da abstração.

Exemplo de plugin de camada

Quero poder chamar o método verifyCertificate() a partir da classe Tinebase_Frontend_Http, que é herdeira de Tinebase_Frontend_Abstract.

Implemento o método na classe Custom_Plugins_VerifyCertificate (que está no arquivo library\Custom\Plugins\VerifyCertificate)

Registro no arquivo init_plugins.php

Tinebase_Frontend_Abstract::attachPlugin('verifyCertificate', 'Custom_Plugins_VerifyCertificate');

Com isso é possível fazer a seguinte chamada ao método verifyCertificate() a partir da instância de Tinebase_Frontend_Http.

Plugins de inicialização da aplicação

O método initialize() da classe Setup_Initialize invoca o método initialize applyCustomizations() das aplicações que possuem plugins.

O registro do plugin será feito no arquivo init_plugins.php e deverá ser feito da seguinte forma:

<Aplicacao>_Setup_Initialize::addPlugin(<nome da classe de plugin>);

Ex: inclusão do plugin Custom_Tinebase_Setup_Initialize:

Tinebase_Setup_Initialize::addPlugin('Custom_Tinebase_Setup_Initialize’);

OBS: a classe de plugin criada deverá implementar a interface Setup_Initialize_Custom_Interface

Plugins de usuários e grupos

Para adicionar um plugin para usuários, faça a chamada ao seguinte método no arquivo init_plugins.php:

Tinebase_User::addPlugin('[NomeDaClasse]');

Para adicionar um plugin para grupos, faça a chamada ao seguinte método no arquivo init_plugins.php:

Tinebase_Group::addPlugin('[NomeDaClasse]');

Plugin de validação de endereço IP

É possível configurar um validador para endereços IP chamando o seguinte método no arquivo init_plugins.php:

Tinebase_Session::setIpAddressValidator('[Validador]');

Validador é uma classe que estende Zend_Validate_Abstract.

Plugin de estratégia para AccessLog

Para modificar a lógica que a classe Tinebase_AccessLog usa para obter o IP remoto do cliente, você deve chamar o seguinte método no arquivo init_plugins.php:

Tinebase_AccessLog::setStrategy('[NomeDaClasse]');

A classe injetada deverá implementar o método getRemoteIpAddress().

Plugins de filtros de consultas

Os plugins de filtros de consultas são adicionados no arquivo init_plugins.php, como no exemplo:

Tinebase_Frontend_Json_Abstract::addPlugin('Addressbook_Model_ContactFilter’, 'Serpro_Model_ContactFilter’);

A classe Serpro_Model_ContactFilter deve estar na pasta library.

Vamos supor que criemos um novo filtro de consulta para a classe ContactFilter do Addressbook, que esteja em library/Serpro/Addressbook/Model/ContactFilter.php.

No arquivo init_plugins.php nós incluimnos a seguinte linha:

Tinebase_Frontend_Json_Abstract::addFilterModelPlugin('Addressbook_Model_ContactFilter’, 'Serpro_Addressbook_Model_ContactFilter);

A partir daí o filtro de Serpro_Addressbook_Model_ContactFilter será usado no lugar do padrão definido por Addressbook_Model_ContactFilter.

Plugin de customização da instalação (programado para release ExpressoBr.20160513)

O modelo de dados do Expresso não deve ser alterado deliberadamente. Para se beneficiar da importação de correções e melhorias do Tine 2.0, o modelo de dados de ambos devem estar sincronizados. Mas é fato que cada ambiente tem necessidades específicas que exigem customizações na base de dados. Mas como essas necessidades são específicas, elas não devem ser integradas ao modelo padrão do Expresso a menos que sejam integradas ao Tine 2.0. Se isso não ocorrer, deve ser feita então uma customização da instalação/atualização por meio de plugins!


Para executar operações adicionais na instalação da aplicação, como a inclusão de campos em tabelas, você pode usar injetar uma classe de customização em Setup_Controller. Essa classe tem de herdar de Setup_Initialize_Custom_Interface.

Vamos supor que queiramos customizar a instalação e escrevemos o que queremos no método applyCustomizations da classe Custom_Setup_Controller.

No arquivo init_plugins.php adicionamos esta linha:

Setup_Controller::addPlugin('Custom_Setup_Controller');

Na instalação da aplicação, após a instalação dos módulos serão executados os plugins injetados em Setup_Controller.

Execução de customizações do banco de dados pela linha de comando

É possível forçar a execução de customizações do banco de dados pela interface CLI do Expresso, usando o seguinte comando:

php setup.php --apply_customizations

Esse comando executa os métodos dos plugins de customização descritos nesta seção.

Isso permite que uma base criada pelo Tine 2.0 seja transformada em uma base do Expresso, por exemplo.

Exemplo de customização de instalação

A classe abaixo é um exemplo real de como customizar a instalação do Expresso.

Declarando uma classe de customização de instalação

Uma classe que customiza a instalação implementa Setup_Initialize_Custom_Interface O método applyCustomizations() será executado automaticamente se essa classe for injetada no arquivo init_plugins.php

class Custom_Setup_Controller implements Setup_Initialize_Custom_Interface
{
    /**
     * @see Setup_Initialize_Custom_Interface::applyCustomizations
     */
    public static function applyCustomizations()
    {
        $backend = Setup_Backend_Factory::factory();

        self::_update_6($backend);
        self::_update_7($backend);
        self::_update_8($backend);
        //TODO remove calls below when task #14299 is integrated
        self::_updateTemp($backend);
        $setup = new Tinebase_Setup_Update_Release7($backend);
        $setup->setApplicationVersion('Tinebase', '7.6');
    }

Adicionando um campo a uma tabela

Eventualmente, você pode querer criar um campo que ainda não existe no modelo de dados do Expresso, mas ele pode surgir em releases futuras. Para evitar conflitos, sempre verifique antes.

    /**
     * update to 5.11
     * - add ldapSettings (name, host, account, ...) for container
     * @param Setup_Backend_Interface $backend
     * @return void
     */
    protected static function _update_6(Setup_Backend_Interface $backend)
    {
        if (! $backend->columnExists('backend_options', 'container')){
            $declaration = new Setup_Backend_Schema_Field_Xml('
                <field>
                    <name>backend_options</name>
                    <type>text</type>
                    <default>NULL</default>
                </field>
            ');
            $backend->addCol('container', $declaration);
        }
    }

Adicionando índices

Se o DBA descobriu que o Expresso não criou índices detectados como necessários para o ambiente onde ele está instalado, você pode incluir essa criação. Índices podem ser necessários ou não de acordo com o perfil de uso de cada tabela.

    /**
     * update indexes
     * add index by name and status for applicattions table
     * add index by created_by for container table
     * - added passwordhash to access_log
     * @param Setup_Backend_Interface $backend
     * @return void
     */
    protected static function _update_7(Setup_Backend_Interface $backend)
    {
        try {
            $declaration = new Setup_Backend_Schema_Index_Xml('
            <index>
                <name>name-status</name>
                <field>
                    <name>name</name>
                </field>
                <field>
                    <name>status</name>
                </field>
            </index>
            ');
            $backend->addIndex('applications', $declaration);

            $declaration = new Setup_Backend_Schema_Index_Xml('
            <index>
                <name>created_by</name>
                <field>
                    <name>created_by</name>
                </field>
            </index>
            ');
            $backend->addIndex('container', $declaration);

            $declaration = new Setup_Backend_Schema_Index_Xml('
            <index>
                <name>type-created_by-name</name>
                <field>
                    <name>type</name>
                </field>
                <field>
                    <name>created_by</name>
                </field>
                <field>
                    <name>name</name>
                </field>
            </index>
            ');
            $backend->addIndex('container', $declaration);

            $declaration = new Setup_Backend_Schema_Index_Xml('
            <index>
                <name>account_type</name>
                <field>
                    <name>account_type</name>
                </field>
            </index>
            ');
            $backend->addIndex('preferences', $declaration);
        } catch (Exception $e) { // If indexes already exist, there is no anything to do
        }

        if (! $backend->columnExists('passwordhash', 'access_log')){
            $declaration = new Setup_Backend_Schema_Field_Xml('
                 <field>
                    <name>passwordhash</name>
                    <type>text</type>
                    <length>255</length>
                    <notnull>false</notnull>
                 </field>
             ');
            $backend->addCol('access_log', $declaration);
        }
    }


Adicionando tabelas

    /**
     * add filter acl table
     * TODO remove method when task #14299 is integrated
     * @param Setup_Backend_Interface $backend
     */
    protected static function _addFilterAclTable(Setup_Backend_Interface $backend)
    {
        if ($backend->tableExists('filter_acl')){
            return;
        }
        $xml = $declaration = new Setup_Backend_Schema_Table_Xml('<table>
            <name>filter_acl</name>
            <version>1</version>
            <declaration>
                <field>
                    <name>id</name>
                    <type>text</type>
                    <length>40</length>
                    <notnull>true</notnull>
                </field>
                <field>
                    <name>record_id</name>
                    <type>text</type>
                    <length>40</length>
                    <notnull>true</notnull>
                </field>
                <field>
                    <name>account_type</name>
                    <type>text</type>
                    <length>32</length>
                    <default>user</default>
                    <notnull>true</notnull>
                </field>
                <field>
                    <name>account_id</name>
                    <type>text</type>
                    <length>40</length>
                    <notnull>true</notnull>
                </field>
                <field>
                    <name>account_grant</name>
                    <type>text</type>
                    <length>40</length>
                    <notnull>true</notnull>
                </field>
                <index>
                    <name>record_id-account-type-account_id-account_grant</name>
                    <primary>true</primary>
                    <field>
                        <name>id</name>
                    </field>
                    <field>
                        <name>record_id</name>
                    </field>
                    <field>
                        <name>account_type</name>
                    </field>
                    <field>
                        <name>account_id</name>
                    </field>
                    <field>
                        <name>account_grant</name>
                    </field>
                </index>
                <index>
                    <name>id-account_type-account_id</name>
                    <field>
                        <name>record_id</name>
                    </field>
                    <field>
                        <name>account_type</name>
                    </field>
                    <field>
                        <name>account_id</name>
                    </field>
                </index>
                <index>
                    <name>filter_acl::record_id--filter::id</name>
                    <field>
                        <name>record_id</name>
                    </field>
                    <foreign>true</foreign>
                    <reference>
                        <table>filter</table>
                        <field>id</field>
                        <ondelete>cascade</ondelete>
                    </reference>
                </index>
            </declaration>
        </table>');

        $backend->createTable('filter_acl', $declaration);
    }

Configurações de plugins

Se o plugin tiver alguma configuração a ser feita, ele deve informar Tinebase_PluginManager, por meio do seguinte método em sua inicialização:

      Tinebase_PluginManager::addPluginConfigItem(
            'plugins_[IDENTIFICADOR DO ITEM]',
            '[tipo de campo do ExtJS]',
            '[RÓTULO DO ITEM DE CONFIGURAÇÃO EM INGLÊS]'
        );

o [IDENTIFICADOR DO ITEM] é um nome que irá determinar a estrutura da configuração no arquivo config.inc.php. Os underscores representam dimensões dentro de um array.

Assim, por exemplo, o item plugins_mobile_redirect, no arquivo config_inc_php fica:

'plugins' => array(
    'mobile' => array(
        'redirect' => FALSE
    )
),

E na aplicação, para recuperá-lo, chamamos:

Tinebase_Config::getInstance()->plugins->mobile->redirect.

A tradução para português do [RÓTULO DO ITEM DE CONFIGURAÇÃO EM INGLÊS] deve ser feita no arquivo pt_BR.po em Tinebase/translations.

Há uma lista de tipos de campo ExtJS o xType em http://docs.sencha.com/extjs/4.0.7/#!/api/Ext.Component.

Arquitetura do expressov3.11.png

Inicialização de plugins

Se alguma ação contida em um plugin tiver de ser executada na inicialização da aplicação, antes da requisição ser processada, então isso deve ser feito com uma chamada ao seguinte método:

Tinebase_PluginManager::initPlugins(array('[CLASSE]'));

As classes contidas no array do argumento devem implmementar o método init()


Apresentação sobre plugins

http://pt.slideshare.net/flaviogomesdasilvalisboa/sistema-de-plugins-do-expressov3-no-espere-faa-o-seu

Ferramentas pessoais
Espaços nominais

Variantes
Ações
Navegação
Ferramentas