Utilização de filtros no Lumis Portal

Objetivos

  • Compreender conceitualmente os filtros no Lumis Portal
  • Entender suas possibilidades de configuração
  • Aprender a implementar uma classe de filtro customizado

Referências complementares

  • DOUI
  • Data Provider
  • Documentação sobre a interface de configuração de filtros via menu do right-click
  • Javadoc da classe TableSourceFilter

Conceito de filtros

Filtros são elementos DOUI que tem o objetivo de restringir o retorno de dados a um source segundo algum critério definido. Tipicamente, em cenários de sources do tipo table ou contentTable e utilizando o DataProvider padrão, os filtros serão convertidos em condições a serem aplicadas sobre a consulta SQL gerada, através de cláusulas “WHERE”.

Como o elemento responsável por popular um Source é o DataProvider, podemos deduzir que é ele também o elemento que possui a inteligência para identificar os filtros existentes em uma interface e considerá-los ou não na hora de obter os dados. Em outras palavras, quem de fato utiliza os filtros para restringir suas consultas é o DataProvider.

Assim, sources que utilizem DataProviders customizados irão ignorar os filtros definidos no source de uma interface a menos que a implementação do DataProvider explicitamente leia e utilize essa configuração de alguma forma, como o DataProvider padrão faz. Nada impede, por exemplo, que um DataProvider customizado que obtém os dados a partir de uma API leia e utilize a definição de filtros para buscar valores e passar parâmetros para a API utilizada, restringindo o retorno de dados da consulta.

Um filtro é sempre aplicado sobre um source específico de uma interface ou instância de interface, sendo que um source pode possuir mais de um filtro. A seguir veremos quais são as possibilidades de configuração para os filtros padrão do Lumis Portal.

Configurações

Um filtro é representado por um XML que contém todas as configurações necessárias para que seja corretamente aplicado em uma consulta, e pode ser aplicado sobre todas as instâncias de uma interface ou sobre uma instância de interface específica.

Para definir um filtro sobre uma interface, a definição XML do filtro deve ser utilizada na definição da interface no arquivo douidefinition.xml. Dessa forma, todas as instâncias dessa interface utilizarão o filtro definido.

No entanto também é possível customizar a configuração de filtros sobre uma determinada instância de interface, seja criando filtros novos sobre uma interface que não possua filtros em sua definição ou mesmo alterando a configuração existente na definição da interface. Essa é uma forma de permitir customizações sobre interfaces genéricas, aumentando a flexibilidade e capacidade de reuso dos serviços do Lumis Portal.

Definindo um filtro sobre o source de uma interface (no douidefinition.xml)

O conjunto de filtros de um source é representado pela tag “filters” que deve existir dentro da tag “source”, na definição da interface. Cada filtro é representado por uma tag “filter” dentro de “filters”. O exemplo abaixo ilustra a definição de sources de uma interface que possui filtros definidos para o seu primeiro source:


<sources>
<source id="default">
<filters>
<filter id="titulo" operator="like" />
<filter id="arquivado" hidden="true" operator="equal" value="0" />
</filters>
</source>
<source id="categorias" />
</sources>
 

Toda a configuração de um filtro é realizada através da aplicação de atributos e sub-tags à tag “filter”. As possibilidades de configuração, realizadas com atributos na definição de filtros, são as seguintes:

  • id = Identificador único do filtro no source. Também pode identificar o campo sobre o qual o filtro está associado, caso não seja definido no filtro o atributo fieldId.
  • fieldId = Identificador do campo sobre o qual o filtro será aplicado. Caso esse atributo não seja definido, o atributo id será utilizado para identificar o campo.
  • value = Valor fixo a ser considerado para a filtragem.
  • defaultValue = Valor default que será utilizado para a filtragem. Esse valor será desconsiderado caso o atributo value tiver sido especificado na definição do filtro ou tenha sido recebido um valor no request para ser utilizado na filtragem ou tenha sido definido pelo usuário um valor explícito.
  • name = Define um nome para o filtro. Esse nome será utilizado pelo controle de filtros para gerar um label para o filtro.
  • hidden = Valor boleano (“true” ou “false”) que indica se trata-se de um filtro escondido. Filtros escondidos não são exibidos pelo controle de filtros para o usuário, de modo que esse não tem como especificar explicitamente seu valor. Filtros escondidos obtém seus valores da própria definição do filtro através dos atributos value ou defaultValue ou podem receber um parâmetro com o valor para filtragem do request. Caso esse atributo não seja definido o seu valor default é “false”.
  • requestParameterName = Define o nome do parâmetro do request de onde o filtro tentará obter seu valor. Caso esse atributo não seja definido o nome do parâmetro do request será obtido do atributo id.
  • nullIfNotSpecified = Valor boleano (“true” ou “false”). Se for “true”, caso o valor do filtro não tenha sido passado o filtro permitirá que sejam retornados apenas os registros que possuam o valor NULL no campo sobre o qual o filtro é aplicado. Caso esse atributo não seja definido o seu valor default é “false”.
  • orNull = Valor boleano (“true” ou “false”). Se for “true”, o filtro sempre permitirá que sejam retornados os registros que possuam o valor NULL no campo sobre o qual o filtro é aplicado, além daqueles que possuam o valor especificado para o filtro. Caso esse atributo não seja definido o seu valor default é “false”.
  • operator = Define o operador utilizado para realizar a comparação do valor sendo filtrado com o valor do campo sobre o qual o filtro é aplicado. As opções são “equal”, “notEqual”, “like”, “likeCaseSensitive”, “greaterThan”, “greaterThanOrEqual”, “lessThan”, “lessThanOrEqual”, “isNull”, “isNotNull” e “in”. Nem todas as opções de operadores fazem sentido dependendo do tipo de campo e dos valores recebidos para filtragem. Também existe a opção de exibir múltiplos operadores que possam ser selecionados pelo usuário final no momento da filtragem, como veremos a seguir.
  • showItens = Utilizado para definir filtros cujo valor seja selecionado a partir de uma lista. Permite dois valores, “referenced” e “all”. Caso seja definido com o valor “referenced” o filtro será exibido na forma de um dropdown que exibirá todos os valores que existam no campo sobre o qual o filtro está definido, para ao menos um registro. Caso seja definido o valor “all”, o dropdown exibirá todos os valores possíveis para o campo, independente de existir um registro que possua esse valor definido ou não. Essa segunda opção faz sentido apenas para filtros aplicados sobre campos cujo valor seja obtido de outro source ou de uma lista de opções pré-definida.
  • renderBlankOption = Valor boleano (“true” ou “false”). Esse atributo só faz sentido para filtros cujo valor seja selecionado a partir de uma lista. Indica se deve ser exibida uma opção em branco no início da lista, permitindo que nenhum valor específico seja selecionado. Caso esse atributo não seja definido o seu valor default é “true”.
  • required = Valor boleano (“true” ou “false”). Define o filtro como obrigatório para o retorno de dados, ou seja, caso não seja especificado um valor para o filtro a consulta não será realizada e o source sobre o qual ele é aplicado não será populado. Caso esse atributo não seja definido o seu valor default é “false”.
  • dependentOnFilter = Define o filtro como dependente de outro filtro, através da utilização do id do outro filtro nesse atributo. Esse atributo só faz sentido para filtros cujo valor seja selecionado a partir de uma lista. Filtros dependentes só terão os seus valores populados quando tiver sido definido o valor do filtro do qual dependem. O efeito nesse caso é que só serão exibidos como opções para o filtro dependente valores que existam nos registros retornados após filtragem pelo filtro do qual dependem. Um cenário típico é a aplicação de dois filtros, uf e cidade, onde o filtro de cidade depende do filtro de uf. Nesse caso, após a seleção da uf, o dropdown do filtro cidade será populado exibindo apenas as cidades daquela uf.
  • selectField = Valor boleano (“true” ou “false”). Quando verdadeiro, permite que o usuário final selecione o campo sobre o qual deseja que o filtro seja aplicado. A lista dos campos disponíveis é definida em tags dentro da tag de definição do filtro, como veremos a seguir. Caso esse atributo não seja definido o seu valor default é “false”.
  • doLookup = Valor boleano (“true” ou “false”). Utilizado apenas para filtros sobre campos que façam lookup. Quando verdadeiro, os valores exibidos na lista de opções serão convertidos da mesma forma que os campos quando tem o atributo doLookup=”true” definidos. Dessa forma, campos que guardem identificadores de conteúdos de outros sources, por exemplo, irão exibir o nome do conteúdo ao invés do identificador. Caso esse atributo não seja definido o seu valor default é “false”.
  • valueInLookUp = Valor boleano (“true” ou “false”). Também utilzado sobre campos que façam lookup. Quando verdadeiro, o filtro irá considerar que o valor recebido para realizar a filtragem está convertido através de lookup. Por exemplo, que será recebido o nome de um conteúdo ao invés do identificador de fato armazenado no campo sobre o qual o filtro está aplicado. Caso esse atributo não seja definido o seu valor default é “false”.
  • inputControlType = Dependendo do tipo de campo, o controle de filtro gera automaticamente um tipo de controle para permitir ao usuário final especificar o seu valor. Por exemplo, para campos do tipo string é gerado um inputText, enquanto que para campos do tipo data é gerado um seletor de datas. Esse atributo permite sobrescrever a definição automática do controle gerado.
  • className = Permite a aplicação de classes customizadas para a definição do filtro. Mais abaixo veremos um exemplo de classe customizada e alguns cenários em que essa customização pode ser necessária.

Outras configurações são realizadas utilizando tags dentro da tag “filter” de definição do filtro. São elas:

  • maxRows = Limita o número de registros exibidos para o caso de filtros que exibam uma lista de opções para o usuário através da utilização do atributo showItens. Caso não especificado, o valor default é 999.
  • operators = Utilizado para exibir uma lista de operadores da qual o usuário final pode selecionar o operador a ser utilizado na filtragem. Dentro da tag “operators” devem ser definidas tantas tags “operator” quantos forem os operadores possíveis para seleção. Para cada tag “operator” devem ser especificados os atributos “name” (contém o nome ou símbolo que será exibido para o usuário final na lista de operadores) e “value” (onde deve ser especificado o operador a ser utilizado, sendo que os valores possíveis são os mesmos do atributo “operator” a ser especificado na tag “filter”).
  • selectFields = Utilizado para exibir uma lista de campos da qual o usuário final poderá selecionar o campo sobre o qual deseja que o filtro seja aplicado. Essa tag deve ser utilizada apenas quando for especificado o atributo selectField=”true” na definição do filtro. Dentro da tag “selectFields” devem ser definidas tantas tags “field” quantos forem os campos possíveis para seleção. Para cada tag “field” deve ser especificado o atributo id contendo o id do campo em questão.

Definindo um filtro sobre o source de uma instância de interface (via right-click)

Quando desejamos customizar a configuração de filtros para uma instância de interface, seja definindo filtros para uma interface que não possua filtros em sua configuração original ou modificando essa configuração original caso ela exista, podemos utilizar a opção “Filtros” do menu do right-click sobre essa instância de interface.

Todas as interfaces DOUI e Content já possuem automaticamente a possibilidade de configurar filtros, ordenação, campos e número de itens através do menu do right-click. Caso uma interface possua alguma customização desse menu, para que opção “Filtros” esteja disponível ele precisa estar explicitamente configurado, conforme o exemplo abaixo da interface de lista de fotos do serviço de álbum do Lumis Portal. No exemplo abaixo a configuração abaixo foi realizada para incluir o item Propriedades de Gerenciamento de Arquivos sem perder os itens Estilo, Campos, Ordenação e Filtros:


<interface id="photographList" name="STR_PHOTOGRAPHS_LIST" type="lum_contentList">
   <styles>
     <style path="lumis/service/album/style/PhotographList.xsl" name="STR_DEFAULT" id="list" />
   </styles>
   <menu>
     <item type="lum_style" name="STR_STYLE"/>
     <item type="lum_fields" name="STR_FIELDS"/>
     <item type="lum_orderBy" name="STR_ORDER_BY"/>
     <item type="lum_filters" name="STR_FILTERS"/>
     <item type="lum_fileManagementProperties" name="STR_FILE_MANAGEMENT_PROPERTIES"/>
   </menu>
</interface>

Ou seja, o item do tipo lum_filters permite que seja configurado, através do menu do right-click, os filtros definidos para uma instância de interface pontualmente. Essa customização é armazenada como propriedade da instância de interface e é utilizada pelo DataProvider padrão do DOUI e do Content (ou seja, dos sources do tipo “table” e “contentTable”) para restringir os resultados da consulta. Mais uma vez é importante ressaltar que, caso esteja sendo utilizado um data provider customizado, essa customização será ignorada a menos que explicitamente implementado nesse data provider.

Esse item de menu chama a interface que permite a customização dos filtros para a instância de interface em questão. Nessa interface é possível habilitar ou desabilitar a configuração original de filtros da definição da interface. Quando desabilitada, é possível alterar ou excluir filtros existentes e também criar novos filtros, através de configuração básica (que permite ao usuário selecionar visualmente algumas das opções mais comuns a serem aplicadas sobre o filtro) ou avançada (que permite ao usuário definir o XML do filtro como é feito na definição da interface).

É importante ressaltar que mesmo criando um filtro através da configuração básica, no final do processo será gerado um XML de definição do filtro que será utilizado na instância de interface. Essa interface de configuração permite, através da seleção do tipo de filtro após as configurações já terem sido definidas, que filtros básicos sejam convertidos em filtros avançados e vice-versa. No entanto, como nem todas as configurações de filtros avançados podem ser representadas visualmente nos filtros básicos, a conversão de avançado para básico pode resultar em perda de configurações realizadas no XML.

A criação de uma classe de filtro customizado

Em alguns casos, pode ser necessário que seja customizado algum comportamento de um filtro, em especial com relação a forma de tratar o valor a ser filtrado. Por exemplo pode ser necessário que um filtro aplique seu valor default apenas em uma determinada situação ou que ele calcule o valor que deve ser utilizado para filtragem programaticamente.

Para esse tipo de customização é possível a implementação de classes que representem os filtros, adequando o comportamento destes às necessidades do cenário em questão. Para que um filtro utilize uma classe customizada basta especificar, em sua definição, o atributo className com o nome completo da classe a ser utilizada. Os filtros padrão são representados pela classe TableSourceFilter e uma boa forma de criarmos filtros customizados é estender essa classe modificando apenas o comportamento que precisamos alterar para cada cenário.

Imaginemos o seguinte cenário em que temos um serviço DOUI utilizado em diversas áreas do Portal. Precisamos que em cada área sejam exibidos apenas os conteúdos relativos a aquela área. Como se trata de um serviço DOUI (e não Content) os conteúdos não são filtrados por instância de serviço. Também seria muito custoso customizar os filtros sobre cada instância de interface desse serviço pelo grande número de áreas existentes.

No entanto, é possível obter o valor que deve ser utilizado para filtrar os conteúdos de uma área de uma propriedade já existente no property bag de cada área. Nesse caso uma opção é implementarmos uma classe customizada de filtro que tenha a inteligência de obter o valor de uma determinada propriedade no property bag e utilizar o valor dessa propriedade na filtragem. Abaixo temos um exemplo de classe que possui esse comportamento:


package exemploPropertyBag;
 
import java.util.List;
 
import lumis.doui.table.filter.TableSourceFilter;
import lumis.portal.PortalException;
import lumis.portal.servicecontainer.ServiceContainerRequest;
import lumis.util.query.IQueryValue;
import lumis.util.query.QueryValue;
 
import org.w3c.dom.Node;
 
public class PropertyBagFilter extends TableSourceFilter
{
   @Override
   protected IQueryValue calculateFilterValue(Node filterNode) throws PortalException
   {
     // Obtém o request
     ServiceContainerRequest serviceContainerRequest = ((ServiceContainerRequest)getSource().getDouiContext().getRequest());
     // Obtém uma propriedade específica da property bag da página
     List<String> values= serviceContainerRequest.getPageWebResource().getProperties().get("NOME_DA_PROPRIEDADE");
     if(values != null)
     {
       // Obtém a primeira ocorrência de propriedade com esse nome
       String value = values.get(0);
       // Caso essa propriedade esteja definida, utiliza seu valor para definir o valor do filtro
       if(value != null)
         return new QueryValue(value);
     }
     // Caso a propriedade não esteja definido não especifica um valor para o filtro
     return null;
   }
}

No exemplo acima foi sobrescrito o método calculateFilterValue, que é o método da classe TableSourceFilter responsável por calcular o valor utilizado para a filtragem. Ao invés de simplesmente buscar um valor do request ou da definição do filtro, na customização acima esse valor foi obtido programaticamente das propriedades da página.

Para melhor compreensão da classe TableSourceFilter e conseqüentemente das demais possibilidades na customização de filtros, consulte o Javadoc disponível na documentação do Lumis Portal.

Principais pontos para abordagem prática

  • Configuração de filtros em uma interface para atender a um dado cenário
  • Implementação de uma classe de filtro

Autor: Marcelo Zeferino (Lumis)