Criação de serviços no Lumis Portal usando o DOUI Framework

Objetivos

  • Apresentar conceitualmente o DOUI Framework, e demonstrar as principais vantagens de seu uso na criação de serviços.
  • Apresentar, em linhas gerais, a estrutura do DOUI, suas funcionalidades e principais pontos de extensão.

Pré-requisitos

  • Estrutura básica do Lumis Portal.

Referências complementares

  • Ciclo de vida de uma interface DOUI.
  • XML gerado por uma interface.
  • Data Providers.
  • Controles.
  • Process actions.
  • Post load processors.
  • Controles de validação.
  • Content.
  • Criação de novas permissões.
  • Filtros.
  • Validadores.

Conceituação

Cada portal pode ser considerado único em diversos aspectos: arquitetura de informação, público alvo, layout, integração com sistemas legados e repertório de serviços, dentre vários outros. Ainda assim, geralmente exibem características comuns que poderiam ser generalizadas, especialmente em relação a suas funcionalidades.

Para dar agilidade à construção e evolução de serviços, o Lumis Portal disponibiliza um serviço genérico e extensível, que acaba por funcionar como um verdadeiro ‘framework’ de construção de serviços: o DOUI (‘Data-Oriented User Interface’).

O DOUI possui diversos pontos de extensão e configuração, criando uma base genérica sobre a qual diferentes serviços podem ser criados. Por conta de sua extrema adaptabilidade, o DOUI é muitas vezes considerado complexo; planejado de forma a se adequar a diversos cenários e servir como um grande serviço ‘em branco’ nos mais diferentes cenários, trata-se de uma funcionalidade bastante extensa e que necessita de algum tempo para ser dominada.

O uso do DOUI na construção de serviços traz uma série de benefícios, dentre os quais:

  • Agilidade de construção, manutenção e evolução de serviços.
  • Possibilidade de construção de serviços com pouca ou nenhuma necessidade de geração de código Java.
  • Funcionalidades comumente utilizadas encontram-se pré-definidas e prontas para uso, tendo sido otimizadas e testadas em diversos cenários.
  • Atualização a cada nova versão do Lumis Portal, com melhorias e correções que são automaticamente utilizadas por todos os serviços derivados do DOUI.

Serviços derivados deste ‘framework’ podem ter suas customizações feitas a partir de pontos de configuração, plenamente declarativos, ou a partir da criação de classes Java em substituição ou extensão às nativamente disponibilizadas.

Estas possibilidades serão exploradas no decorrer deste documento.

Fluxo de informações DOUI

Páginas em uma solução construída com o Lumis Portal são compostas por instâncias de interfaces de serviço. A figura a seguir mostra uma página típica de um portal, identificando as interfaces de serviços que a compõem.

image002.gif

Quando uma página é requisitada, o Lumis Portal requisita a geração dos conteúdos de cada uma de suas interfaces, para que a página possa então ser montada e enviada ao usuário para visualização. No caso de uma interface de serviço DOUI, um fluxo específico é iniciado.

No caso de interfaces de exibição de dados (render):

  • Fontes de dados (sources) são instanciadas.
  • Elementos que compõem a interface (controles) são instanciados e restauram seu estado (populando seus valores de um source ou do ‘Request’).
  • Controles atualizam o XML da interface com seus valores.
  • O XML é transformado em HTML, através do estilo (XSL) cadastrado na interface.

Estes conceitos serão apresentados em detalhes a seguir; a figura a seguir ilustra, de forma simplificada, o papel de cada um destes elementos.

image004.gif

Obviamente, o exemplo não contempla todas as estruturas necessárias à geração de uma interface. Em uma interface de serviço, são necessárias definição da estrutura de dados que a alimenta, da lógica de negócio que seleciona os dados (com eventuais filtragem e ordenação de informações) e de diversas informações complementares. Estas definições serão, também, apresentadas neste documento. Por ora, é necessário apenas compreender que interfaces são definidas pelos controles e que seus dados provêm de uma ou mais fontes de dados.

O resultado da renderização de uma interface (DOUI ou não) é um trecho HTML retornado para o Lumis Portal, que o integra aos demais trechos que compõem a página.

É importante que uma interface não seja confundida com o serviço propriamente dito; ela é, na verdade, parte da lógica do serviço ou uma visão de como suas informações são apresentadas.

image006.gif

Um serviço que trate dos ‘contatos’ de uma intranet pode precisar adquirir dados das tabelas de funcionários, setores e cargos. Estas informações podem ser mostradas sob a forma de listas, listas rápidas, detalhes do funcionário e interfaces administrativas, entre outras que podem ser criadas. Cada interface mencionada trata de parte do que seria a funcionalidade de ‘contatos’ desta solução.

O fluxo existente quando ações são executadas em interfaces de serviços DOUI (submissão de formulário, por exemplo) difere um pouco do apresentado anteriormente:

  • Sources são instanciados.
  • Controles são instanciados e restauram seu estado (populando seus valores de um source ou do ‘Request’).
  • Controles injetam seus valores como parâmetros dos process actions (para que, por exemplo, no caso de submissão de um formulário, os valores digitados nos campos sejam conhecidos e enviados ao servidor para processamento).
  • Process actions e process action handlers são executados, podendo retornar parâmetros de renderização.

Didaticamente pode ser utilizado o exemplo ilustrado a seguir.

image008.gif

As informações digitadas são passadas pelos controles à ação de processamento responsável pela submissão de formulário. Esta invoca uma ou mais funções responsáveis por este processamento e, neste caso, atualizam uma informação na fonte de dados.

As ações comuns são pré-definidas pelo DOUI, podendo ser utilizadas facilmente, sendo necessária apenas alguma parametrização simples. Serviços que tratam da manipulação de dados, na maior parte das vezes, podem ser construídos sem necessidade de qualquer programação JAVA.

Este resumo demonstra a flexibilidade de um serviço baseado em DOUI; a livre definição de fontes de dados, filtros, controles, lógica de apresentação e processamento (no caso de submissão de informações) permite que os mais diferentes tipos de serviço sejam criados e evoluídos.

Informações disponíveis a interfaces de serviços DOUI

Uma vantagem da utilização do DOUI framework é que todas as suas interfaces recebem diversas informações do Lumis Portal, e estas podem ser utilizadas para geração do HTML final da interface. A lista a seguir identifica as informações repassadas às interfaces no XML do serviço.

  • isPrinting - indica se a página está em modo de impressão (utilizando o serviço de impressão nativo do Lumis Portal).
  • Locale - idioma utilizado naquele momento, por aquela requisição.
  • serviceInstanceId - identificador da instância do serviço;
  • serviceInstanceDebugLevel - nível de depuração da instância de serviço, que pode ser alterado por configuração da interface.
  • pageId - identificador da página atual.
  • channelId - identificador do canal onde está a instância do serviço relativa à interface.
  • userId - identificador do usuário.
  • userName - nome do usuário.
  • userEmail - e-mail do usuário.
  • userLogin - login do usuário.

Estas informações fazem parte de uma seção do XML chamada ‘douicontext’, exemplificada a seguir.


<douiContext>
   <isPrinting>false</isPrinting>
   <locale>pt_BR</locale>
   <serviceInstanceId>8A488ACB12E82FAF0112E8B61A9223C3
   </serviceInstanceId>
   <serviceInterfaceInstanceId>8A488ACB12E82FAF0112E8B69C9E2476
   </serviceInterfaceInstanceId>
   <serviceInstanceDebugLevel>0</serviceInstanceDebugLevel>
   <pageId>8A488ACB12E82FAF0112E8B683D1243E</pageId>
   <channelId>00000000F00000000000000000000001</channelId>
   <userId>00000000D00000000000000000000001</userId>
   <userName>Administrator</userName>
   <userEmail />
   <userLogin>admin</userLogin>
</douiContext>

Neste exemplo, a instância de interface saberia que a página não se encontra em modo de impressão, o idioma utilizado é português (Brasil), e o usuário atual é o administrador do portal, sem e-mail cadastrado, bem como os demais identificadores mencionados.

Declaração de um serviço DOUI

O registro de serviços no Lumis Portal exige a criação de um arquivo de definição, chamado ‘servicedefinition.xml’.

Para utilizar o DOUI Framework na construção de um serviço, basta informar na definição do serviço que o mesmo é do tipo ‘lum_doui’. O exemplo a seguir mostra o trecho utilizado para definir um serviço que se utiliza do DOUI.


<service id="example.service.test"
name="Serviço Teste" type="lum_doui">
       <description>Serviço exemplo</description>
       <image>example/service/test/image/Teste.gif</image>
       <smallImage>example/service/test/image/Teste02.gif
 </smallImage>
</service>

Maiores detalhes sobre a definição de um serviço, seu registro e uso podem ser consultados na documentação do Lumis Portal. O processo de registro deste arquivo, se bem sucedido, faz com que um novo serviço chamado ‘Serviço Teste’ esteja disponível para uso no Lumis Portal.

Internamente, a informação ‘type=”lum_doui”’ faz com que o serviço criado estenda a classe DouiService e disponibilize diversas funcionalidades, mencionadas no decorrer deste documento.

Além disso, os serviços do tipo DOUI exigem que, no mesmo diretório, seja criado outro arquivo, chamado douidefinition.xml, contendo as definições utilizadas pelo framework para compor o serviço adequadamente. Este arquivo, que define os sources, controles e process action handlers utilizados no serviço, divide-se em duas partes: definição do serviço e definição de interfaces.

A figura a seguir mostra um exemplo esquemático dos nós existentes em um arquivo douidefinition.xml, claramente delimitando as seções de sources (fontes) e interfaces. 

image009.jpg

A definição do serviço encontra-se abaixo da seção (nó XML) ‘service’, incluindo todos os sources globais utilizados pelo serviço e os relacionamentos entre eles.

A definição de interfaces (seção ‘interface’) possui quatro sub-seções, que definem sources (utilizados por cada interface) hyperlinks (links entre serviços e interfaces, calculados pelo Lumis Portal automaticamente), controls (que definem uma interface) e processactions (executados por cada interface em momentos específicos).

Destes, ainda não foram mencionados os ‘hyperlinks’ – na verdade, são definições que permitem ao serviço calcular automaticamente ‘links’ entre as interfaces.  No exemplo a seguir, o serviço de ‘contatos’ (anteriormente mencionado) mostra esquematicamente três definições de ‘hyperlinks’ entre interfaces.

image011.gif

A partir da lista rápida é possível navegar para a interface de lista (usando um link de ‘ver lista completa’, por exemplo) ou para os detalhes de um contato clicando sobre ele. A interface de lista pode ter o mesmo comportamento: ao clicar sobre um nome, a interface de detalhes do contato é mostrada; situação análoga pode ser definida na interface administrativa.

Esta definição prévia dos links entre interfaces auxilia bastante na montagem de uma solução, pois permite que o Lumis Portal calcule estas URLs sem que haja necessidade do desenvolvedor ou montador saber, de antemão, em que página cada interface estará instanciada.

Nas próximas seções serão apresentados, em maiores detalhes, os conceitos vistos até o momento.

Sources (fontes de dados)

Sources são fontes de dados globalmente declaradas em um serviço e utilizadas por suas interfaces. São formados por um conjunto de campos (fields) que possuem identificadores, tipos de dados e características definidas na declaração do ‘source’.

No arquivo douidefinition.xml, cada source é definido em um elemento <source> próprio, sendo sua classe é definida através dos atributos ‘type’ (no caso de sources padrão) ou ‘className’ (no caso de sources não padrão).

Em resumo, há um elemento <sources> que lista cada fonte de dados disponíveis para o serviço, identificando-as por um atributo id. Utilizando por base o exemplo anterior, do serviço de ‘contatos’, a definição de ‘sources’ seguiria este padrão:


    <sources>
 
      <source id="setores">
             ...
      </source>
 
      <source id="cargos">
             ...
      </source>
 
      <source id="funcionarios">
             ...
      </source>
 
    <sources>

Na parte não especificada entrariam as configurações específicas de cada fonte de dados, tratadas mais à frente.

No arquivo de definição do DOUI frawework (douidefinition.xml), sources devem ser declarados na ordem inversa em que deveriam ter seus dados apagados no momento de uma exclusão de conteúdo. Em outras palavras, se uma tabela do banco de dados possui uma chave estrangeira para outra, deve ficar abaixo desta.

No exemplo hipotético mostrado a seguir, os sources devem ser declarados na ordem PROFESSOR, TURMA, ALUNO.

image013.gif

No caso do serviço de ‘contatos’ apresentado anteriormente, a ordem de ‘sources’ dependeria das relações entre as entidades; se cada setor define seus cargos, provavelmente a ordem apresentada está correta.

Cada fonte de dados é composta de campos (fields) e relacionamentos, de forma análoga a uma entidade de base de dados relacional.

O trecho de código a seguir definiria as fontes do serviço de ‘turmas escolares’.


<sources>
 
      <source id="professor" type="table">
        <table>professor</table>
               <fields>
            <field id="id" display="false" name="id"
             dataType="string" isPrimaryKey="true"/>
             <field id="nome" name="Nome" dataType="string"/>
           </fields>
       </source>
 
       <source id="turma" type="table">
        <table>turma</table>
               <fields>
            <field id="id" display="false" name="id"
             dataType="string" isPrimaryKey="true"/>
            <field id="horario" name="Horário"
             dataType="string"/>
     <field id="professorid" name="Professor"
       dataType="guid"
       lookupRelationId="professor" lookupFieldId="Nome" />
           </fields>
      </source>
 
      <source id="aluno" type="table">
        <table>turma</table>
          <fields>
            <field id="id" display="false" name="id"
             dataType="string" isPrimaryKey="true"/>
      <field id="nome" name="Nome"
              dataType="string"/>
      <field id="turmaid" name="Turma" dataType="guid"
             lookupRelationId="turma" lookupFieldId="horario" />
           </fields>
       </source>
 
    </sources>

Para compreender melhor a declaração de fontes e todas as suas possibilidades, consulte o manual do Lumis Portal e os artigos específicos sobre o assunto. Para a boa arquitetura de um serviço Lumis, é importante compreender os tipos de fonte (source) e de seus campos (fields), bem como os demais atributos constantes desta seção do douidefinition.xml.

No exemplo anterior, é possível perceber, mesmo aos que ainda não possuem conhecimentos profundos sobre o assunto:

  • Cada fonte é uma entidade mapeada em uma tabela do banco de dados, com os nomes ‘professor’, ‘turma’ e ‘aluno’, respectivamente. Isto não é necessariamente verdade, sendo possível, por exemplo, o mapeamento de uma fonte de dados para uma ‘view’, ou a utilização de rotinas que provêm dados para o serviço.
  • Cada campo da tabela é declarado com seu tipo de dados, um identificador (‘nome’ do campo na base de dados) e um nome, para exibição no portal.
  • Relacionamentos são explicitamente declarados entre campos de diferentes entidades, permitindo que na listagem de uma turma, o Lumis Portal automaticamente mostre o nome de um professor, ao invés de seu ID.

A forma de declaração de relacionamentos mostrada no exemplo é bastante simples, informando apenas a correspondência entre colunas. Uma forma mais completa, declarando também os relacionamentos entre as entidades, é mostrado a seguir. Os trechos inseridos estão em negrito para facilitar sua identificação.


<sources>
 
      <source id="professor" type="table">
        <table>professor</table>
               <fields>
            <field id="id" display="false" name="id"
             dataType="string" isPrimaryKey="true"/>
             <field id="nome" name="Nome" dataType="string"/>
           </fields>
       </source>
 
       <source id="turma" type="table">
        <table>turma</table>
               <fields>
            <field id="id" display="false" name="id"
             dataType="string" isPrimaryKey="true"/>
            <field id="horario" name="Horário"
             dataType="string"/>
     <field id="professorid" name="Professor"
       dataType="guid"
       lookupRelationId="professor" lookupFieldId="Nome" />
           </fields>
 
                        <relations>
             <relation id="default" foreignSourceId="professor"
 cascadeOnDelete="true">
                    <relationField fieldId="quot;professorid"
                     foreignFieldId="id"/>
             </relation>
           </relations>
 
      </source>
 
      <source id="aluno" type="table">
        <table>turma</table>
          <fields>
            <field id="id" display="false" name="id"
             dataType="string" isPrimaryKey="true"/>
      <field id="nome" name="Nome"
              dataType="string"/>
      <field id="turmaid" name="Turma" dataType="guid"
             lookupRelationId="turma" lookupFieldId="horario" />
           </fields>
 
                        <relations>
             <relation id="default" foreignSourceId="turma"
 cascadeOnDelete="true">
                    <relationField fieldId="turmaid"
                     foreignFieldId="id"/>
             </relation>
           </relations>
 
       </source>
 
    </sources>

Neste exemplo modificado, as chaves estrangeiras são explicitamente declaradas. Para maiores detalhes sobre estas possibilidades, consulte o manual do Lumis Portal.

A definição de relacionamentos é apenas declarativa, cabendo ao desenvolvedor explicitá-las durante a criação dos objetos na base de dados. No mesmo exemplo, chaves primárias e estrangeiras devem ser geradas em cada tabela, espelhando os relacionamentos existentes nas definições dos sources.

O elemento <fields> de um ‘source’ possui a lista dos campos que fazem parte da tabela e serão utilizados pelo serviço, cada um deles especificado em um atributo <field>. A documentação do Lumis Portal trata com detalhes a sintaxe deste atributo; cada campo pode ser de um dos seguintes tipos:

  • boolean
  • date
  • dateTime
  • document
  • double
  • file
  • files
  • guid
  • html
  • image
  • integer
  • string
  • text
  • time

Outras parametrizações de cada campo indicam se ele deve ser exibido nos formulários gerados automaticamente pelo portal, se seu preenchimento é obrigatório e se ele deve ser objeto de algum tratamento especial (deve ser indexado na busca, é uma chave primária, etc.).

Após a declaração global de sources de um serviço, cada interface deve mencionar quais sources utiliza. Uma interface pode utilizar um ou mais sources e pode, inclusive, mencionar que campos (fields) precisa manipular.  Pode também mudar a configuração dos campos utilizados, sobrescrevendo as configurações dos ‘sources’ do serviço. Algumas outras funcionalidades, como filtros e ordenações (tratadas mais à frente neste documento) também podem ser revisitadas. Em linhas gerais, cada ‘source’ de interface utiliza por base as definições do serviço, podendo ajustá-las conforme sua necessidade.

O exemplo a seguir mostra a definição de quatro sources em nível de serviço.


<sources>
 
       <source id="destaqueprogramacao" type="contentTable">
             <table>exemplo_destaqueprogramacao</table>
             <fields>
                    <field id="id" dataType="guid" isPrimaryKey="true"
                     display="false" />
                    <field id="programaid" name="Programa"
                     dataType="string"
                     lookupRelationId="programa"
                     lookupFieldId="no_programa" />
                    <field id="generoid" name="Gênero" dataType="guid"
                     lookupRelationId="genero" lookupFieldId="descricao"
                     />
                    <field id="imagemgrande" name="Imagem Grande"
                     dataType="image" />
                    <field id="imagempequena" name="Imagem Pequena"
                     dataType="image" />
                    <field id="titulo" name="Titulo" dataType="string"
                     isPrimaryName="true" />
                    <field id="introducao" name="Introdução"
                     dataType="string"
                     isIntroduction="true" />
                    <field id="descricao" name="Descrição"
                     dataType="html" />
                    <field id="datahora" name="Data e hora"
                     dataType="dateTime"
                     lookupRelationId="horario"
                     lookupFieldId="dt_inicio"
                     externalData="true" />
             </fields>
             <relations>
                    <relation id="programa" foreignSourceId="programa">
                           <relationField fieldId="programaid"
                           foreignFieldId="id_programa" />
                    </relation>
                    <relation id="genero"
                     foreignSourceId="generoprograma">
                    <relationField fieldId="generoid"
                     foreignFieldId="id" />
                    </relation>
                    <relation id="horario" foreignSourceId="horario">
                           <relationField fieldId="programaid"
                            foreignFieldId="id_programa" />
                    </relation>
             </relations>
       </source>
                   
       <source id="generoprograma" type="table">
             <table>exemplo_generoprograma</table>
             <fields>
                    <field id="id" dataType="guid" isPrimaryKey="true"
                     display="false" />
                    <field id="descricao" name="Descrição"
                     dataType="string" isPrimaryName="true" />
             </fields>
       </source>
                   
       <source id="programa" type="view">
             <table>exemplo_vw_programa</table>
             <fields>
                    <field id="id_programa" dataType="string"
                     isPrimaryKey="true" display="false" />
                    <field id="no_programa" name="Programa"
                     dataType="string"
                     isPrimaryName="true" />
                    <field id="tt_sinopse" name="Sinopse"
                     dataType="string" />
                    <field id="no_categoria" name="Gênero"
                      dataType="string" />
                    <field id="nr_ano" name="Ano" dataType="integer" />
                    <field id="no_original" name="Nome original"
                     dataType="string" />
                    <field id="nr_faixa_etaria" name="Classificação"
                     dataType="integer" />
             </fields>
       </source>
 
       <source id="horario" type="view">
             <table>exemplo_vw_horario</table>
             <fields>
                    <field id="id_programa" dataType="string"
                     display="false" />
                    <field id="dt_inicio" name="Data"
                     dataType="dateTime" />
                    <field id="mn_duracao" name="Original"
                     dataType="string" />
                    <field id="nr_canal" name="Número do canal"
                     dataType="integer" />
                    <field id="no_canal" name="Nome do canal" />
             </fields>
       </source>
 
</sources>

Neste exemplo é possível perceber que a declaração dos diversos sources utilizados por um serviço pode ser bastante extensa, e que há muito mais nesta estrutura que o demonstrado no exemplo anterior. Um ‘source’ define todos os campos que poderão ser utilizados nas interfaces do serviço, cabendo à definição de cada interface indicar quais destes campos irá utilizar. Caso não haja restrição de campos a serem utilizados em uma interface, todos serão repassados a ela. Muito cuidado deve ser tomado para que os sources sejam definidos da forma mais completa possível, e de forma coerente com sua estrutura real na base de dados.  Um ‘source’ não precisa conter toda a estrutura de uma tabela da base de dados, mas a definição dos campos utilizados precisa ser coerente entre o portal e o banco de dados para evitar situações indesejadas.

O trecho a seguir, extraído do mesmo serviço, mostra a definição de uma interface de ‘destaques’. Nele são definidos, apenas, que fontes de dados são utilizadas e hyperlinks para outras interfaces. Relembrando o que foi dito anteriormente, a interface menciona que campos utiliza, redefinindo, para seu uso, o ‘source’ chamado ‘destaqueprogramacao’.

Perceba que este código encerra toda a definição da interface, não sendo necessária (neste caso) nenhuma programação para a criação de uma lista de ‘destaques’. Esta é a grande vantagem da utilização do DOUI framework – a simplicidade com que é possível gerar serviços com comportamentos comuns.


<interface id="destaques">
       <sources>
             <source id="destaqueprogramacao">
                    <fields>
                           <field id="programaid" display="false" />
                           <field id="generoid" display="false" />
                           <field id="imagempequena" display="true" />
                           <field id="titulo" display="true" />
                           <field id="datahora" display="true"
                            doLookup="true" />
                    </fields>
             </source>
             <source id="horario">
                    <fields inherit="all" />
             </source>
       </sources>
       <hyperLinks>
             <hyperlink interfaceId=”detalhes”>
                    <target type="page" />
                    <parameters>
                           <parameter name="destaqueid"
                            dataId="contentId" />
                           <parameter name="programaid"
                            dataId="programaid" />
                           <parameter name="generoid" dataId="generoid"
                            />
                    </parameters>
             </hyperLink>
       </hyperLinks>
</interface>

Nas seções seguintes trataremos da declaração de interfaces e hyperlinks em maiores detalhes. O importante, neste momento, é que seja percebido que houve a declaração de uma interface chamada ‘destaques’ que se utiliza de dois dos ‘sources’ anteriormente declarados: ‘destaquesprogramacao’ (apenas alguns de seus campos) e ‘horario’ (todos os campos).

Naturalmente, regras de negócio mais complexas poderiam ser necessárias ao serviço, obrigando a uma maior complexidade na declaração da interface e, possivelmente, desenvolvimento de rotinas específicas para atendê-las.

Embora usualmente um ‘source’ seja mapeado em uma tabela (ou view) da base de dados, é possível construí-lo por meio de uma classe que gere dados tabulares; a esta classe damos o nome genérico de ‘data provider&rsquo (provedor de dados).

Uma fonte não precisa adquirir dados apenas de tabelas de bancos dados. Uma classe Java pode ser desenvolvida com objetivo de trazer e manipular informações, e estas podem também ser utilizadas como um ‘source’. O exemplo a seguir mostra uma fonte de dados que se utiliza de um provedor de dados (‘data provider’) customizado, de nome "lumis.sample.testDataProvider".


<source id=“default” type=“table”>
 
          <dataProviderClassName>lumis.sample.testDataProvider
    </dataProviderClassName>
 
          <fields>
              <field id=“id” dataType=“guid” name=“identificador”
               isPrimaryKey=“true”/>
              <field id=“name” dataType=“string” name=“nome”
               isPrimaryName=“true”/>
              <field id=“description” dataType=“html” name=“descrição”
               />
          </fields>
 
</source>

Além de todas as possibilidades mencionadas até o momento, uma fonte de dados pode utilizar bancos de dados externos. Para que outras configurações com bases de dados sejam utilizados no Lumis Portal, estas devem ser configuradas em arquivos XML em sua estrutura de configurações. O nome do arquivo criado (em lumisdata/config) servirá como identificador de conexão para a solução desenvolvida. No caso de fontes de dados, o atributo connectionId utiliza este valor para indicar a conexão a ser utilizada.

Esta discussão encerra apenas uma introdução geral ao conceito de fontes de dados, e de como estas são integradas aos serviços e interfaces definidas utilizando o DOUI Framework. Para maiores informações a respeito, consulte a documentação do Lumis Portal e o artigo que trata, em detalhes, de ‘sources’.

Postload processors

Em alguns casos, pode ser necessário processar as informações de uma fonte de dados, após sua carga, antes do uso em uma interface. Para fazer este processamento pós-carga são utilizados ‘postload processors’.

Com o uso desta funcionalidade é possível, entre outras possibilidades, criar, remover ou modificar o valor de um campo, e permitir que um registro seja ou não exibido. Embora a remoção de registros, em geral, possa ser executada a partir de filtros, e algumas das demais operações a partir do uso deviews ou processamentos do banco de dados, muitas vezes é interessante que este processamento seja executado por classes Java desenvolvidas no escopo da solução.

Uma alternativa ao uso de ‘postload processors’ é a implementação de um ‘data provider’ capaz de processar a lógica de manipulação de dados e entregá-los já da forma esperada pelo serviço. Entretanto, muitas vezes é mais vantajoso utilizar os ‘data providers’ nativos do Lumis Portal, com todos os seus recursos, e manipular os dados adquiridos a posteriori. A manipulação de informações a partir da base de dados (views, stored procedures),  ‘dataproviders’ ou ‘postload processors’ deve ser cuidadosamente arquitetada, levando em vista performance, reuso e simplicidade de implementação e manutenção.

PostLoad Processors podem ser aplicados em quaisquer interfaces que contenham dados tabulares, e são declarados dentro da TAG ‘source’, conforme o exemplo a seguir.


<source>
    <postLoadProcessors>
        <postLoadProcessor className="complete.class.path " />
    </postLoadProcessors>
</source>

O conjunto de registros adquiridos da fonte de dados será processado pela classe declarada, e seu resultado será utilizado em lugar do original. Para maiores detalhes sobre a implementação deste tipo de processamento, consulte os manuais do Lumis Portal.

Interfaces

Simplificadamente, podemos definir um serviço como um conjunto de interfaces que podem ser utilizadas em páginas de uma solução. Em um wireframe de portal são apresentadas as páginas, constituídas por estas interfaces, cabendo a um desenvolvedor ou arquiteto agrupá-las em serviços a serem desenvolvidos.

Um serviço construído utilizando o DOUI framework assume a existência de uma série de interfaces padrão, com comportamentos pré-definidos. Seu uso, embora não obrigatório, pode fazer com que serviços simples tenham arquivos de declaração bastante sucintos e de fácil manutenção.

A saber, as seguintes interfaces administrativas são disponibilizadas:

  • Administração (ou lista administrativa) - interface que contém a lista de conteúdos cadastrada para a instância do serviço, com botões que permitem a adição, edição e remoção de itens.
  • Adição - são disponibilizadas duas interfaces que permitem a inserção de itens: uma para uso em ‘pop-up’, chamada a partir do botão de ‘adicionar’ da lista administrativa, e outra para posicionamento em uma página do portal, sem necessidade de ser utilizada a partir da administração de conteúdos.
  • Edição - interface que permite editar o conteúdo de um item selecionado; precisa ser invocada a partir da lista administrativa, na qual é selecionado o item que se deseja alterar.
image014.png

Esta ilustração mostra a interface de lista administrativa e as três pop-ups que podem ser utilizadas a partir de seus botões: a janela de remoção de conteúdo não é uma interface do serviço, apenas uma ‘messagebox’ do navegador utilizada para confirmação da ação.

Além destas, são também disponibilizadas as seguintes interfaces:

  • Lista lista de conteúdos cadastrados na instância de serviço (ou que tenham sido publicados para aquela instância), que podem ser paginados. Cada conteúdo listado possui um ‘link’ que leva à interface de detalhes do conteúdo.
  • Lista rápida - lista de conteúdos cadastrados na instância de serviço, não paginada, e contendo ‘links’ para detalhes de cada conteúdo e um para a interface de ‘lista’.
  • Detalhes interface que apresenta as informações cadastradas para um determinado conteúdo.

A ilustração simplificada a seguir mostra as interfaces de lista rápida, lista e detalhes de conteúdos. Cada link sob um conteúdo leva à sua página de detalhes, automaticamente.

image017.gif

Com estas informações fica fácil perceber que um serviço convencional de manipulação de dados pode ser facilmente criado com o DOUI Framework. As interfaces comumente necessárias à sua administração e visualização já são automaticamente criadas pelo Lumis Portal, cabendo ao desenvolvedor apenas definir fontes de dados e outras questões que complementam suas regras de exibição: que dados devem ser exibidos e com que ordenação, por exemplo.

Para cada uma destas interfaces automaticamente providas deve ser criado um bloco de definição no arquivo douidefinition.xml. No caso deste bloco não ser declarado, serão usadas as configurações default das interfaces.

A tabela a seguir mostra como declarar cada uma das interfaces mencionadas.

Interface Tipo (type=”...”) Classe utilizada por default
Genérica lum_doui DouiServiceInterface
Lista de dados lum_douiList DouiListServiceInterface
Lista rápida lum_douiQuickList DouiQuickListServiceInterface
Detalhes lum_douiDetails DouiDetailsServiceInterface
Administração lum_douiAdministrationList DouiAdministrationListServiceInterface
Adição lum_douiAdministrationAddPropertyPage DouiAdministrationAddPropertyPageServiceInterface
Edição lum_douiAdministrationEditPropertyPage DouiAdministrationEditPropertyPageServiceInterface
Adição (embedded) lum_douiAdministrationEmbeddedAddPropertyPage DouiAdministrationEmbeddedAddPropertyPageServiceInterface

A interface ‘genérica’, mencionada, é a que dá origem a todas as demais, e que não possui comportamento próprio. É a interface mais apropriada a se utilizar quando a interface a ser construída não se encaixa nos padrões definidos pelas demais.

Para configurar (customizar) interfaces pré-definidas ou criar outras, é importante compreender as configurações que podem ser utilizadas, em ‘sub-seções’ do nó ‘interface’.

A compreensão destas possibilidades de definição/configuração de uma interface é importante tanto para a criação de interfaces não-padrão quanto para a configuração (customização) das interfaces pré-definidas apresentadas na tabela anterior.

Destas, já foram explicados os contextos de utilização de sources (utilizados globalmente e em cada interface). As demais possibilidades de configuração serão tratadas nas próximas seções deste documento:

  • Hyperlinks.
  • Controles.
  • Process actions.

Hyperlinks.

Uma das partes mais críticas na definição de portal é sua navegação.

Páginas precisam ter ‘links’ para outras, e estes ‘links’ não podem, em linhas gerais, possuir valores fixos. Não é criada uma página de detalhes para cada notícia armazenada em um portal, mas uma página de detalhes que, a partir do identificador do conteúdo, é capaz de apresentar seus detalhes. Este ‘link’ não é, em verdade, um atributo da página, mas de uma interface que a compõe.

A figura a seguir serve para ilustrar melhor este conceito; uma interface de lista rápida pode conter um ‘hyperlink’ para uma interface de detalhes, que por sua vez deve apresentar o conteúdo referente ao item que foi ‘clicado’.

image018.png

Um link deste tipo é entre interfaces; o Lumis Portal deve gerar automaticamente, para a interface de lista rápida à esquerda, um ‘hyperlink’ para o detalhe de cada conteúdo, visualizado na interface de ‘detalhes’ do serviço. Esta declaração pode ser bastante simples, como ilustrado a seguir.


<hyperLinks>
   <hyperLink id="details" interfaceId="details"/>
</hyperLinks>

Entretanto, esta simplicidade assume definições prévias (defaults) que podem ser alterados em casos mais complexos. O trecho a seguir traz uma implementação similar apontando para uma interface de detalhes de outro serviço e repassando um parâmetro específico (conteudoId), diferente do usual.


<hyperLink id="details" interfaceId="example.service.news.details">
       <parameters>
             <parameter name="lumItemId" dataId="conteudoId" />
       </parameters>
</hyperLink>

Em ambos os casos, cada item possuirá um link para a página com a interface de detalhes que tenha sido montada na solução. Não é necessário, de antemão, saber a URL da página ou sua localização no Portal. Cabe ao Lumis Portal inferir a navegação e tratar adequadamente a questão.  As regras utilizadas levam em conta a estrutura da solução, tentando encontrar a interface do tipo adequada mais ‘próxima’ à atual; desta forma uma página que contenha uma  lista de conteúdo leva à página de detalhes mais próxima.

No exemplo, há também um link para uma página que contenha a interface de lista completa do serviço deve ser criado. Este hyperlink é tratado de forma diferenciada, o que ficará mais claro na próxima seção deste documento, que trata dos ‘controles’ que compõem uma interface.

Maiores definições sobre como declarar um hyperlink devem ser verificadas na documentação do produto; mais de um parâmetro pode ser passado em um mesmo hyperlink, e há um grande número de atributos que podem ser configurados, possibiltando grande versatilidade à implementação. Por exemplo, âncoras HTML (tag <A NAME>) podem ser especificadas, permitindo que o hyperlink leve a um trecho específico da página onde o conteúdo destino se encontre.

Controles

Cada interface é composta por controles. Em uma interface típica podem ser identificados botões, links, caixas de texto e outros componentes, cada um deles um controle.

Controles podem ser visuais ou não; inputs do tipo ‘hidden’, por exemplo, fazem parte da codificação de um formulário, mas não são apresentados visualmente. Controles podem permitir interações ou não; caixas de texto desabilitadas e ‘labels’ são exemplos comuns de controles apenas visuais.

Desta forma, podemos conceituar controles como componentes existentes em uma interface com objetivo de compô-la visualmente, interagir com o usuário ou implementar alguns tipos simples de lógica, como validações de dados.

Há diversos controles básicos que podem ser utilizados em serviços DOUI; há também a possibilidade de estender classes que definem padrões de controles existentes ou mesmo a criação de novos controles.

Uma discussão mais aprofundada sobre os controles existentes e como implementar novos controles pode ser obtida em [link]. Como informação introdutória é importante apenas saber que existem quatro tipos de controle:

  • control - controles que cuidam basicamente de ‘layout’.
  • data control - controles que possuem um valor, com a intenção de manter um estado mesmo durante navegação ou recarga da página. Este controle não é associado a um campo da fonte de dados
  • databound control - associado a um campo de uma fonte de dados.
  • validator control - trata da validação de dados.

Controles que apresentam ou manipulam dados são associados a um campo (field) de uma fonte de dados (source).

Há duas formas de declarar um controle: a forma contracta (exemplo: <control:lum_form>) ou a forma usual (exemplo: <control type="lum_form">), sendo as duas igualmente válidas e equivalentes. Atualmente a primeira é mais utilizada por ter legibilidade mais direta.

Cada controle possui atributos específicos para sua parametrização. Para detalhes de que controles encontram-se disponíveis no Lumis Portal e comoparametrizá-los, refira-se à documentação do produto.

O exemplo a seguir mostra os controles que comporiam uma interface de detalhes com apenas dois campos, ‘name’ e ‘description’. A interface é inserida em um formulário (controle ‘lum_form’), dentro do qual é disposta uma tabela com estes campos.


<controls>
 <control:lum_form >
   <control:lum_propertyPage>
     <control:lum_controlGroup title= "STR_BASIC_INFORMATION">
       <control:lum_table>
          <tr>
            <td>
             <control:lum_label id= "name"/>
            </td>
            <td >
             <control:lum_inputText id= "name"/>
            </td>
          </tr>
          <tr>
            <td>
             <control:lum_label id= "description"/>
            </td>
            <td>
             <control:lum_inputText id= "description"/>
            </td>
          </tr>
        </control:lum_table>
      </control:lum_controlGroup>
    </control:lum_propertyPage>
  </control:lum_form>
</controls>

Permissões podem ser definidas para serviços, e estas podem ser verificadas a cada controle.

Se um botão de adição precisar, por exemplo, que o usuário tenha uma permissão definida com o nome ‘adicionarUsuario’, um código como o apresentado a seguir poderia ser utilizado para garantir o acesso à funcionalidade.


<control:lum_addButton>
     <permissions>
          <permission id="adicionarUsuario"/>
     </permissions>
</control:lum_addButton>

Neste exemplo, outros botões que não tivessem a mesma restrição seriam visualizados normalmente por todos os usuários.

Process Actions

Algumas interfaces são apenas de exibição, com eventual navegação para outras páginas ou interfaces. Em outros casos, entretanto, é necessário executar uma ação como resultado da interação do usuário com a interface. O exemplo mais comum deste caso é a submissão de formulários.

Cada interface deve definir que tipos de ação pode executar e as rotinas responsáveis por este processamento. Às ações de processamento dá-se o nome de ‘process actions’, e às rotinas responsáveis por este processamento, ‘process action handlers’.

Um ‘process action’ é disparado a partir do controle que recebeu a interação; desta forma, clicar em botão de ‘Adicionar’ pode disparar uma ação diferente de clicar no botão de ‘editar’ na mesma interface. O ‘process action ‘recebe como parâmetros os valores de todos os controles da interface; isso permite que uma ação de submissão de formulário saiba, por exemplo, os valores de todos os campos de preenchimento do formulário sem a necessidade de processamento específico na interface.

É possível a um controle a execução de mais de um process action, quando necessário; da mesma forma, um ou mais process action handlers podem ser invocados, sendo executados na ordem em que foram declarados. Cada process action handler é implementado como uma classe Java que implementa a interface IProcessActionHandler, normalmente estendendo-se a classe genérica GenericProcessActionHandler. Há diversos handlers nativos no Lumis Portal, sendo muitas vezes desnecessário o desenvolvimento de um novo, exceto quando há uma lógica específica do serviço que precisa ser implementada neste momento.

A figura a seguir, apresentada anteriormente neste mesmo documento, ajuda a compreender a comunicação entre estes elementos.

image008.gif

Um handler pode ou não gerar uma resposta (response), com parâmetros que podem ser utilizados na renderização da interface (render parameters). Um exemplo de resposta seria o “doui_closeWindow”, utilizado por interfaces que executam numa janela pop-up (por exemplo, ao adicionar um conteúdo). Após salvar os dados, esta resposta contém um parâmetro de renderização que instrui a interface a renderizar apenas o código Javascript para fechar a janela, no lugar do HTML normal da interface. 

A lista a seguir mostra as possibilidades para o elemento ‘response’. A documentação do Lumis Portal traz informações detalhadas sobre como utilizá-los.

  • doui_message
  • doui_refreshParent
  • doui_closeWindow
  • doui_runJavascript
  • doui_setRequestParameters
  • doui_standardPopup
  • doui_standardEmbedded
  • doui_standardCommit
  • doui_setResponseParameters
  • doui_setRequestAttributes
  • doui_hyperLink
  • doui_popupInterface
  • doui_preview

Para maiores detalhes sobre o ciclo de um process action, verifique a documentação do Lumis Portal. Embora a forma mais utilizada de implementar umprocess action handler consista simplesmente em estender a classe GenericProcessActionHandler e implementar apenas o método processAction, é importante ter um amplo conhecimento das possibilidades providas por este mecanismo de modo a arquitetar adequadamente um serviço.

Cada elemento <processAction> define um process action handler, o que pode ser feito através do atributo type (para handlers padrão do produto) ouclassName (handlers customizados, indicando como valor a classe que implementa o handler).

Se houver necessidade da execução de mais de um process action handler, haverá mais de um elemento <processAction> com o mesmo valor para o atributo “id”.  Neste caso, quando um process action com esse identificador é desencadeado, todos os process actions com esse identificador são executados, na ordem em que estão definidos dentro do elemento <processActions> .

O exemplo a seguir mostra um formulário com um único campo para digitação de dados (‘name’) e um botão de Ok. Este botão, ao ser clicado, executa uma ação padrão de ‘commit’, que se encarrega de submeter o formulário e gravar os dados em uma tabela no banco de dados utilizando-se do handler‘tableAddData’. Ao seu término duas respostas são enviadas: a primeira (doui_refreshParent) atualiza a janela anterior, e a segunda (doui_closeWindow) fecha a janela atual. Trata-se, com certeza, de um formulário apresentado em uma pop-up, chamada a partir de uma interface de lista.


<controls>
     <control type=“lum_label” sourceId=“default” dataId=“name”/>
     <control type=“lum_text” id=“name” sourceId=“default”/>
     <control type=“lum_okButton”>
         <onClick type="processAction">
            <processActionId>commit</processActionId>
         </onClick>
     </control>
 </controls>
 <processActions>
    <processAction id=“commit” type=“tableAddData“>
       <response type="doui_refreshParent" />
       <response type="doui_closeWindow" />
    </processAction>
 </processActions>

Um segundo exemplo, a seguir, mostra um tipo de resposta (doui_hyperlink) que exige a passagem de parâmetro – neste caso, o identificador de interface para o qual será gerado o link.


<processActions>
 <processAction id="delete" sourceId="voteQuestion"
  type="contentTableDeleteData"/>
 <processAction id="results" type="generic">
  <response type="doui_hyperLink">
   <interfaceId>results</interfaceId>
    <parameters>
     <parameter name="id" />
    </parameters>
  </response>
 </processAction>
</processActions>

Estes exemplos não encerram todas as possibilidades, mas demonstram a simplicidade com que ações e respostas comuns a serviços de um portal podem ser executadas, uma vez que o desenvolvedor tenha plenos conhecimentos das possibilidades do DOUI framework.

Para a criação de seus próprios Process Action Handlers, consulte a documentação do Lumis Portal.

Filtros, número de ítens e ordenação de dados

Cada interface (e cada uma de suas fontes) pode ter filtros definidos para intervir na exibição ou não de informações. Filtros podem ser visíveis para o usuário (permitindo que ele selecione um ou mais valores para este processamento) ou invisíveis (pré-definidos).

A definição da interface ‘destaquegeral’, apresentada a seguir, permitiria que fossem filtrados conteúdos a partir de seus ‘títulos’ (com preenchimento parcial) e intervalo de datas.


<interface id="list">
       <sources>
             <source id="destaquegeral">
                    <filters>
                           <filter id="titulo" operator="like" />
                           <filter fieldId="publishStartDate"
                            id="publishedDateStart" name="Data de
                            publicação de" operator="greaterThanOrEqual"
                           />
                           <filter fieldId="publishStartDate"
                            id="publishedDateEnd" name="Data de
                            publicação até" operator="lessThanOrEqual"
                           />
                    </filters>
                    <fields>
                           <field id="id" display="false" />
                           <field id="titulo" />
                           <field id="titulobotao" />
                           <field id="funcionalidade" display="false" />
                           <field id="publishStartDate" />
                           <field id="detailLink" externalData="true"
                            display="false" />
                           <field id="ordem" />
                           <field id="descricao" />
                           <field id="imagem" />
                    </fields>
                    <orderBy>
                           <field direction="ascending" id="ordem"/>
                           <field direction="descending"
                            id="publishStartDate" applyAlways="before"
                           />
                    </orderBy>
             </source>
       </sources>
       <hyperLinks>
             <hyperLink interfaceId="details" />
       </hyperLinks>
</interface>

O uso de filtros traz muitas possibilidades, não limitadas apenas a seleção de informações por parte de usuários. Para maiores informações sobre esta funcionalidade, consulte a documentação do Lumis Portal ou artigos complementares a respeito.

Outra possibilidade na definição de uma fonte de dados é informar um ‘limite’ para o número de dados que devem ser trazidos para manipulação pelo serviço. Isso é muito importante pois, muitas vezes, uma tabela pode conter milhares de registros, nos sendo úteis apenas alguns deles para popular, por exemplo, uma lista rápida.

Esta definição pode ser feita com o uso da TAG ‘maxRows’, na definição de um ‘source’, tanto em nível de serviço quanto de interface. Quando não for declarado, o limite de dados trazidos é de 50 itens. O exemplo a seguir mostra como redefinir este valor para mil elementos.


<sources>
 <source id="category">
  <maxRows>1000</maxRows>
 </source>
</sources>

A ordem em que os registros são exibidos pode ser, também, alterada em uma interface e fonte de dados. No exemplo a seguir, é definido que a ordenação dos registros da interface envolverá os campos "position" e "name" em ordem crescente ("ascending"), referentes ao source "category".


<sources>
 <source id="category">
  <orderBy>
   <field direction="ascending" id="position" />
   <field direction="ascending" id="name" />
  </orderBy>
 </source>
</sources>

Mais de uma ordenação pode ser utilizada. No trecho a seguir, retirado do exemplo anterior, percebe-se a existência de dois critérios de ordenação: inicialmente, por um campo ‘ordem’, e de forma secundária, pela data de publicação do item (mais recentes antes dos mais antigos).


<orderBy>
       <field direction="ascending" id="ordem"/>
       <field direction="descending" id="publishStartDate" />
</orderBy>

Expression language

A implementação de serviços utilizando o DOUI framework é tambem facilitada pela possibilidade de uso de Expression Language. Atributos e valores nos arquivos de definição podem possuir ELs, definidos pelo padrão usual "${}".

Exemplos de como utilizar expressões, variáveis, objetos implícitos, operadores e palavras reservadas de EL podem ser obtidos em http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html.

O manual do Lumis Portal lista os objetos implícitos que estão disponíveis para uso em ELs.

Logic TAGs

Outro facilitador para a construção de serviços é a implementação de elementos lógicos nos arquivos de definição DOUI. Com esta funcionalidade é possível ao Lumis Portal a montagem de uma interface dinamicamente, durante sua execução, mantendo sua definição puramente declarativa.

Condicionais utilizadas no douidefinition.xml devem estar declaradas com namespace http://www.lumis.com.br/lumisportal/xsd/doui/logic. Condicionais só podem ser utilizados na definição de interfaces.

A estrutura segue um padrão simples de choose... then... otherwise, que deve ser natural a desenvolvedores. Maiores detalhes são apresentados na documentação do Lumis Portal que trata de ‘Logic Tags’. O exemplo a seguir, extraído desta documentação, ajuda a compreender a funcionalidade.


<doui:douiDefinition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:doui="http://www.lumis.com.br/lumisportal/xsd/doui"        xmlns:control="http://www.lumis.com.br/douicontrols" xmlns:logic="http://www.lumis.com.br/lumisportal/xsd/doui/logic">
 
       <service id="myServiceId">
               ...
       </service>
       <interfaces>
               ...
         <interface id="myInterfaceId">                      
             <logic:choose>
             <logic:when test="empty param.lumItemId">
                       ...
                    </logic:when>
                    <logic:when test="param.myParameter eq 'value1'">
                       ...
                    </logic:when>
                    <logic:when test="param.myParameter eq 'value2'">
                       ...
                    </logic:when>
                    <logic:otherwise>
                       ...
                    </logic:otherwise>
              </logic:choose>
         </interface>
               ...
       </interfaces>
 
</doui:douiDefinition>

Menu de configuração de interfaces

Um serviço pode precisar mostrar, em uma lista rápida na home, os três últimos conteúdos; uma lista rápida do mesmo serviço, em outra página, pode precisar apresentar cinco elementos. Se não fosse possível configurar cada instância de interface individualmente, seria necessário criar duas interfaces distintas (lista rápida com três elementos, e lista rápida com cinco elementos), ou criar arquivos XSL específicos para cada uma. A manutenção de um portal, mesmo em situações simples como esta, exigiria mudanças no serviço ou redundância de arquivos para atender a cenários básicos.

Nesta e em diversas outras situações é interessante que uma instância de interface possa ser configurada. Usos típicos envolvem o número de itens de uma lista, ordenação de elementos e os campos apresentados. Outra possibilidade é a definição de filtros, indicando que conteúdos serão efetivamente mostrados em cada interface.

Serviços criados usando o DOUI Framework permitem diversas configurações nativamente, e outras podem ser acrescentadas com facilidade. Nos demais serviços do portal o menu de configuração de interface possui apenas duas opções: Propriedades e Atualizar.

Detalhes sobre como acrescentar outros itens (não previstos) no menu de configuração de interfaces em um serviço DOUI, consulte o manual do Lumis Portal.

Autor: Josemar Ferreira (Lumis)