11 out 2016

Modificando o Comportamento Padrão do Liferay

É de conhecimento de todos que trabalham com Liferay sua capacidade de personalização em muitos aspectos, sendo o mais visível deles o layout. Quando se fala em construir um site ou até mesmo um portal a primeira preocupação é com a aparência e usabilidade (look and feel em inglês), e optando por algum produto pré pronto como o Liferay surge a dúvida: Será que vou conseguir deixar meu site/portal do jeito que eu quero?

Essa pergunta é muito válida pois existem muitos produtos que oferecem uma gama até razoável de opções e uma implementação realmente rápida e barata porém quando tentamos sair um pouco que seja do que ela oferece a implementação disso pode chegar a ser tão custosa que torna se inviável, ou seja, você terá que se contentar com o que a ferramenta oferece ou procurar alguma outra que case melhor com as necessidades do projeto. Então caros amigos, respondendo a pergunta anterior, SIM! quando se fala de Liferay estamos falando de uma ferramenta altamente customizável e estilizavel sendo necessário apenas conhecer o caminho das pedras para realizar grandes mudanças com um esforço relativamente baixo.

Hoje estou aqui para falar especificamente do aspecto usabilidade do portal (o feel do look and feel). Quando acessamos um site pela primeira vez damos de cara com o a interface do mesmo. Nossa experiência é baseada primeiramente em um layout que pode ser bonito/agradável ou desagradável e também na usabilidade que vai desde intuitivo até muito complexo ou limitado. Todas essas impressões geralmente estão ligadas ao front end da aplicação, ou seja, html, CSS, javascript e no caso do Liferay, Velocity e/ou Freemaker e essas tecnologias por sua vez estão todas atreladas a parte da aplicação que chamamos de tema. O tema para o Liferay é um plugin que encapsula a maior parte dos styles da aplicação (CSS), a parte estrutural (velocity templates) e também das funcionalidades javascript que é o ponto focal deste post.

Após esta pequena introdução vamos ao que interessa. Em aplicações de grande porte é necessário tomar cuidado com as bibliotecas a serem utilizadas para não termos problemas de compatibilidade e no caso do Liferay, apesar de ele vir “preparado” para poder ser importado o famoso framework JQuery o padrão a ser utilizado é o AlloyUI do Yahoo. Muitas vezes para simples utilização em portlets ou até mesmo hooks o JQuery supre perfeitamente nossas necessidades porém quando nos vemos frente a frente com alguma customização mais pesada de alguma funcionalidade padrão do Liferay se faz necessário um pouco de conhecimento de sua biblioteca padrão, nosso amigo Alloy, e neste ponto eu gostaria de recomendar um post do NATE CAVANAUGH que é um dos Interface Engeneer do Liferay e certamente alguém que conhece muito da parte visual do produto, segue o link:

https://web.liferay.com/pt/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-elements-and-events

Tendo sido feitas as devidas apresentações, temos alguns conceitos interessantes a introduzir aqui sendo que começarei com o de módulos:

Todas as funcionalidades importantes do Liferay estão implementadas em forma de módulos, ou seja, não estão acopladas, elas são chamadas em algum ponto do código e dependendo da ação que está sendo executada o mesmo módulo pode executar diferentes papés como no caso de eventHandling onde o resultado pode diferir dependendo do tipo de evento por exemplo. Para ilustrar um pouco do que estou falando, vamos supor que exista em uma máquina uma instancia do Liferay portal versão 6.2 bundled em tomcat, considerando a pasta do tomcar como raiz, se irmos no diretório webapps/ROOT/html/js/liferay veremos os seguintes arquivos:

modules-alloy

Esses arquivos são os módulos disponíveis na aplicação, cada um com suas utilidades específicas. Agora, vamos supor que queremos realizar a mudança do menu que por padrão é acionado por mouseover para um clique, quais seriam os passos necessários?

Primeiro precisamos identificar qual o módulo responsável pelos eventos que queremos mudar, no nosso caso, o objeto a ter o comportamento alterado se chama navigation, e o arquivo responsavel por ele é o navigation_interaction.js. Dentro deste aquivo existe um objeto NavigationInteraction que possui um atributo prototype que contém várias funções, focaremos nelas para entender seu comportamento.

As funções disponíveis são: initializer, _handleExit, _handleKey, _handleKeyDown, _handleLeft, _handleRight, _handleShowNavigationMenu, _hideMenu, _initChildMenuHandlers, _initNodeFocusManager, _onMouseToggle e _showMenu. Apartir deste ponto precisamos do conhecimento prévio de velocity pois precisaremos entender o fluxo básico do evento de mouseover do menu, para me ater ao exemplo não passaremos uma por uma, apenas nas que será necessário sobreescrever, atenção a palavra “sobreescrever” pois não é recomendavel alterar esses arquivos diretamente na aplicação deployada. Para isto usaremos nosso tema que tem o poder de sobreescrever modulos da aplicação.

Não será abordado neste post como criar um tema, para isto sugiro o próprio material do site do Liferay

https://dev.liferay.com/develop/tutorials/-/knowledge_base/6-2/creating-a-theme-project-in-the-plugins- sdk

Dentro do tema, caso ele tenha sido criado com o sdk do Liferay, existirá um arquivo js chamado main.js. Esse arquivo contém inicialmente o seguinte código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
AUI().ready(
    'liferay-hudcrumbs', 'liferay-navigation-interaction', 'liferay-sign-in-modal',
    function(A) {
                var navigation = A.one('#navigation');
                if (navigation) {
           navigation.plug(Liferay.NavigationInteraction);
        }
                var siteBreadcrumbs = A.one('#breadcrumbs');
        if (siteBreadcrumbs) {
           siteBreadcrumbs.plug(A.Hudcrumbs);
        }
        var signIn = A.one('li.sign-in a');
        if (signIn && signIn.getData('redirect') !== 'true') {
           signIn.plug(Liferay.SignInModal);
        }
    }
);

O conteúdo dele ainda não é relevante para nós, o importante no momento é saber que ele funciona como qualquer outro arquivo js e o usaremos para criar um novo módulo que sobreescreve as funcionalidades necessárias.

Tendo analisado o arquivo navigation_interaction.js percebemos duas funcionalidades que são interessantes para nossa demanda, _initChildMenuHandlers e _onMouseToggle. A primeira por delegar o evento de mouseenter e mouseleave

1
navigation.delegate(['mouseenter', 'mouseleave'], instance._onMouseToggle, '> li', instance);

E a segunda por disparar o evento dependendo do tipo do mesmo:

1
2
3
4
5
6
var eventType = 'hideNavigationMenu';
if (event.type == 'mouseenter') {
  eventType = 'showNavigationMenu';
}
mapHover.menu = event.currentTarget;
Liferay.fire(eventType, mapHover);

Tendo identificado os responsaveis pelo por disparar e delegar o evento temos dois passos importantes a seguir pela frente.

Primeiro precisamos mexer um pouco na estrutura do menu. Como dito anteriormente o Liferay tem uma parte estrutural que geralmente é feita com apache velocity e que também é feita de forma “modular”, o componente a ser alterado é o navigation.vm que fica dentro da pasta templates, mudaremos o seguinte trecho de código de:

1
2
3
4
<li class="$nav_item_css_class" id="layout_$nav_item.getLayoutId()" role="presentation">
    <a class="$nav_item_link_css_class" href="$nav_item.getURL()" role="menuitem">
        <span>$nav_item.icon() $nav_item.getName() $nav_item_caret</span>
    </a>

Para

1
2
3
4
<li class="$nav_item_css_class menuParent" id="layout_$nav_item.getLayoutId()" role="presentation">
    <a class="$nav_item_link_css_class" role="menuitem">
        <span>$nav_item.icon() $nav_item.getName() $nav_item_caret</span>
    </a>

As mudanças ocorridas foram: retirado atributo de href da tag

1
<a>

e adicionada classe menuParent na tag

1
<li>

Ou seja, retiramos a função de click para o menu parent o que é muito importante agora que o usuário terá que clicar para expandir o menu e não queremos que cada vez que isso ocorra o mesmo seja mandado para páginas que não queira e também adicionamos a classe que usaremos para delegar o evento.

Agora que terminamos com a parte estrutural prosseguiremos para o javascript. Dentro do arquivo main iremos colocar o seguinte trecho de código com comentários em todos os seus pontos importantes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
AUI.add(
    'liferay-navigation-interaction-click',
    function(A) {
        A.mix(
            Liferay.NavigationInteraction.prototype,
            {
                _initChildMenuHandlers: function(navigation) {
                    var instance = this;
            if (navigation) {
                        //DELEGA O EVENTO PARA OS ELEMENTOOS QUE CONTEM A CLASSE menuParent E CHAMA A FUNÇÃO _onMouseToggle
                        navigation.delegate('click', instance._onMouseToggle, '.menuParent', instance);
                        navigation.delegate('keydown', instance._handleKeyDown, 'a', instance);
                    }
                },
                _onMouseToggle: function(event) {
                    var instance = this;
                    var mapHover = instance.MAP_HOVER;
                    //VALIDA SE O MENU JA ESTÁ ABERTO
                    if(!A.one(event.currentTarget._node).hasClass("actMenu")){
                        //CASO ALGUM MENU JÁ ESTEJA ABERTO ELE O FECHA
                        var act = A.one("#navigation").one(".actMenu");
                        if(act){
                            try{
                                //DISPARA O EVENTO DE HIDE MENU
                                Liferay.fire('hideNavigationMenu', event.currentTarget);
                                try{
                                    //REMOVE A CLASSE DE actMenu
                                    A.one(".actMenu").removeClass("actMenu");
                                }catch(err){}
                            }catch(e){}
                        }
                        //SETA O TIPO DE EVENTO PARA SHOW NAVIGATION E EM SEGUIDA O DISPARA
                        eventType = 'showNavigationMenu';
                        mapHover.menu = event.currentTarget;
                        Liferay.fire(eventType, event.currentTarget);
                    }else{
                        //CASO O MENU JÁ ESTEJA ABERTO ELE O FECHA
                        var hasActClass = A.one(event.currentTarget._node).hasClass("actMenu");
                        if(hasActClass &amp;&amp; !clickChildren){
                            Liferay.Dockbar.dockBar.hide();
                            eventType = 'hideNavigationMenu';
                            mapHover.menu = event.currentTarget;
                            try{
                                 Liferay.fire(eventType, event.currentTarget);
                            }catch(e){}
                            A.one(event.currentTarget._node).removeClass("actMenu");
                        }
                    }
                },
            },
            true
        );
    },
    '',
    {
        requires: ['event-click', 'event-outside', 'liferay-navigation-interaction']
    }
);

Falando de forma resumida, o módulo delega o evento para a classe criada na estrutura e utilizando de validações gerencia o click do menu para que ele expanda e retraia conforme o usuário clica em algum parent.

O passo final agora é importar o módulo recém criado (liferay-navigation-interaction-click) no lugar do antigo liferay-navigation-interaction dentro do arquivo main.js:

1
2
3
AUI().ready(
    'liferay-hudcrumbs', 'liferay-sign-in-modal', 'aui-autocomplete', 'liferay-navigation-interaction-click',
    function(A) {

E está feito, esta é apenas uma das formas de realizar esta atividade, dependendo da necessidade do usuário é possivel inserir outros eventos e validações muito mais complexas para manipularmos o comportamento padrão dos módulos do Liferay e não somente do menu, cada um dos arquivos citados anteriormente corresponde a alguma funcionalidade que também pode ser manipulada dentro do tema.

Espero que o as informações apresentadas tenham sido úteis e até a próxima.

Leave a Comment