01. Como forçar o uso do HTTPS no Web Framework?
02. Como adicionar colunas ao processo de permissões?
03. Como alterar o texto da barra de título do navegador?
04. Como habilitar usuários para criar chaves custom na base de Desenvolvimento?
05. Como configurar o sistema para uso de nome e logomarca?
06. Como criar um relatório em árvore?
07. Como estender classes explorer?
09. Como executar códigos na inicialização do Web Framework?
10. Como ligar e desligar o speed fill?
11. Como criar grades detalhe?
12. Como estender a interação showVariables de um layout?
13. Como exportar os dados de um relatório que não usa SimpleLayout?
14. Como funcionam os agrupamentos de campos da grade?
15. Como utilizar a propriedade aggregate do Field?
16. Como utilizar a propriedade canNavigate?
17. Por que os detalhes técnicos da ajuda e a pilha do erro não são exibidos pelo sistema?
18. É possível declarar um processo em um módulo da Union File System?
Como forçar o uso do HTTPS no Web Framework?
Para forçar o uso do HTTPS, deve-se criar um arquivo x-class ou x-view no diretório “/Configuração/Web Framework/Segurança” da Virtual File System.
Nesta classe, pode-se indicar manualmente os servidores que têm certificados SSL instalados e são capazes de atender o protocolo HTTPS, conforme exemplo abaixo:
this.httpsCapable = true;
this.serverAddress.push('<IP_DO_SERVIDOR>');
Caso o protocolo HTTPS seja implementado por um outro serviço, como um balanceador de carga ou
um proxy reverso, pode-se utilizar o evento o onGetUseHTTPS
para detectar que o tráfego foi
originado a partir desse outro serviço e realizar o redirecionamento automático. Abaixo segue
um exemplo que detecta o uso do balanceador de carga do Google Cloud Platform:
this.onGetUseHTTPS.set(function() {
return request.getHeader('via') === '1.1 google' &&
request.getHeader('x-forwarded-proto') === 'http';
});
Como adicionar colunas ao processo de permissões?
Durante o processo de desenvolvimento, quando trabalhando em novas funcionalidades para o sistema às vezes surge a necessidade de criar uma permissão específica para uma ação definida nesta funcionalidade. O processo de definição de permissões pertence à licença do time Framework, então para resolver o nosso problema isoladamente, existe uma maneira isolada e simples de criar essa coluna sem alterar esse processo.
O primeiro passo a se dar é criar a nova coluna para a nossa nova permissão na tabela de permissões, por isso vamos à classe Permissões em “Raiz > Dados > Sistema > Permissões”. Com essa classe em foco vamos criar um novo registro contendo os dados do nosso campo.
var fld = this.field("EDITANOMECLIENTE", "boolean");
fld.label = "Permite edição de nomes de clientes em um processo imaginário";
fld.help = "Informe se o usuário ou grupo tem permissão para alterar os nomes dos clientes imaginários no processo imaginário.";
fld.order = 999;
fld.visible = true;
fld.scope.addClass(-123456678);
Você deve ter notado que chamamos o método addClass do propriedade scope que, como o nome já diz, adiciona as classes passadas por parâmetro dentro do escopo deste campo de permissão. A consequência disso é que este campo estará visível no processo de permissões para estas classes e suas descendentes. Essa é a única maneira de alterar a visibilidade dos campos de permissão. Apesar de não recomendarmos, é possível um campo de permissão ter várias classes raízes, basta chamar o método novamente passando uma nova chave. Nos casos em que é necessário a criação de uma nova raiz para o campo de permissão é preferível que se crie um novo campo.
IMPORTANTE: Quando dizemos que a visibilidade dos campos de permissão não deve ser alterada em
nenhum lugar, nem mesmo no evento onDefinePermissionsGrid
, estamos falando sério. Caso isso
aconteça um erro será disparado.
Feito isso, certifique-se de que a coluna está criada no banco de dados e no cache local, e prossiga com o nosso manual.
Agora que temos o campo criado e visível, pode ser necessário ainda alguma customização.
Como temos acesso aos campos? Simples! É só definir uma implementação do evento
onDefinePermissionsGrid
na classe que se deseja essas alterações. Segue um exemplo:
this.onDefinePermissionsGrid.set(function (grid) {
inherited(grid);
var fld = grid.field('editanomecliente');
fld.label('lambda');
fld.help('Permissão para escrever "lambda"!');
});
IMPORTANTE: Depois de ler sobre o evento onDefinePermissionsGrid
você pode querer alterar a
visibilidade dos campos de permissão utilizando-o. Isto não é possível. A visibilidade dos campos
só pode ser alterada utilizando o método o método addClass
do propriedade scope
do próprio
campo.
Como alterar o texto da barra de título do navegador?
Na plataforma nginstack há duas configurações referente ao texto da barra de título do navegador, uma para as telas de login customizadas e outra para o ambiente do sistema que será utilizada após o usuário ser autenticado.
Variáveis disponíveis para a personalização da barra de título do navegador:
- $DataBase: nome da base de dados.
- $Port: porta de acesso em que o sistema está sendo executado.
- $EngineVersion: versão atual do Engine.
- $ProductVersion: versão atual do sistema.
- $LoggedUser: usuário logado no sistema.
- $SystemName: nome do sistema.
Para que possamos alterar o título das telas de login customizadas devemos abrir o processo “Admin > Aparência e Personalização > Tela de login” e clicar no botão “Configurar telas customizadas”. Em seguida, será apresentada uma grade com o título “Telas de login”, nela deve ser selecionada a linha cuja a tela de login desejamos alterar e clicar em “Mudar visão”. Já com a grade no modo tabela será possível ver o campo “Título usuário não autenticado”, este campo é responsável pelo texto da barra de título na tela de login.
Variáveis permitidas: $DataBase, $Port, $EngineVersion, $ProductVersion e $SystemName.
Exemplo:
Título: Sistema - $ProductVersion $DataBase - Engine: $EngineVersion - Porta: $Port.
Resultado: Sistema - 2011.4 BaseDeDados - Engine: 11.1.3.10 - Porta: 80.
A configuração do título do navegador no ambiente do sistema é basicamente a mesma que a da tela de login, existindo apenas algumas variações como: processo utilizado e a adição da variável $LoggedUser para compor o título.
Para que possamos alterar o título do navegador no ambiente do sistema devemos abrir o processo Ambientes que se encontra em “Admin > Aparência e Personalização > Ambientes”.
Ao abri-lo você verá uma grade com o título Ambientes, nela deve ser selecionada a linha cujo o ambiente desejamos alterar e clicar em “Mudar visão”. Já com a grade no modo tabela será possível ver o campo “Título usuário autenticado”, este campo é responsável pelo título do navegador após o login.
Variáveis permitidas: $DataBase, $Port, $EngineVersion, $ProductVersion, $LoggedUser e $SystemName.
Exemplo:
Título: Sistema - $ProductVersion $DataBase$LoggedUser - Engine: $EngineVersion - Porta: $Port. Resultado: Sistema - 2011.4 BaseDeDados\Usuário - Engine: 11.1.3.10 - Porta: 80.
Como habilitar usuários para criar chaves custom na base de Desenvolvimento?
Para habilitar a permissão de um usuário criar chaves custom, acesse “Admin > Customização > Configuração.” Clique em Inserir, digite o login do usuário e salve as alterações.
Para criar uma chave custom pela IDE clique em “Tools > License to create keys > Custom”. Caso não seja possível alterar o código, clique em “Tools > Enable product changes”.
Como configurar o sistema para uso de nome e logomarca?
Para realizar essa configuração é necessário criar um script x-class em “Raiz > Configuração > Inicialização da Sessão.””
O objeto uwi.config
contém diversas propriedades relativas a configurações do sistema.
Quatro delas dizem respeito a nomes e sobremarcas:
- uwi.config.systemName: é o nome de exibição do sistema. Por exemplo, quando o sistema dispara um email de relatório agendado, este é o nome que o sistema usa como remetente do email.
- uwi.config.vendorFooterLogo: é a chave do arquivo que contém a logomarca, para exibição no rodapé dos relatórios.
- uwi.config.vendorFooterLogoTitle: texto descritivo do logotipo no rodapé.
- uwi.config.vendorName: o nome do fornecedor.
- uwi.config.vendorURI: é o endereço do site do fornecedor. Quando tem um valor, a logomarca também se comporta como link, apontando para esse endereço.
É importante notar que existe sempre a possibilidade de que sejam implementadas melhorias e alterações relativas a colocação e formatação dos nomes e marcas no sistema. Utilizar esta API garante a estabilidade do sistema quanto ao uso de marcas e nomes independente das melhorias que venham a ser feitas.
Como criar um relatório em árvore?
Árvore é uma estrutura capaz de representar uma hierarquia em modo gráfico, sendo a sua base, a raiz, o primeiro elemento e também a origem de todos os outros elementos. As pontas da árvore são as folhas.
E como eu indico ao sistema que o meu relatório se trata de um relatório em árvore? O Simple
Layout vai entender isso através do método sobrecarregado newRecord
. A assinatura do método
é essa:
newRecord(checkGroup, groupTotalLabel, showLineTop, showLineBottom, treeNodeId, parentTreeNodeId)
Note que os dois últimos parâmetros dizem respeito à estrutura de árvore, sendo o treeNodeId
o ID do nó que se quer acrescentar ao Simple Layout e o parentTreeNodeId
o pai dele.
O Simple Layout ainda tem atributos especiais para a nossa estrutura em árvore:
- treeExpansionLevel: Esse atributo é um inteiro que representa até que nível da árvore ela vai estar expandida quando o relatório for aberto.
- showTreeRoot: Atributo booleano que indica se a raiz da árvore deve ser exibida.
Como estender classes explorer?
O processo Classes Explorer é utilizado para configurar as classes presentes no sistema.
O Classes Explorer é um processo implementado para ser possível configurar as classes existentes no sistema. A execução do processo abrirá uma tela onde mostrará toda a hierarquia de classes do sistema.
É possível estendê-lo para que a hierarquia de classes exibidas, seja mostrado a partir de uma determinada classe base.
Para configuração é necessário criar um processo e alterar a propriedade baseClass para ter a chave da classe que se deseja utilizar como raiz (base). O exemplo do processo segue abaixo:
this.baseClass = -1898187809; /* Usuarios */
__includeOnce(-1898146496 /* /products/Admin/modules/Admin/Classes Explorer.ip */);
Como estender o explorer?
Esta resposta visa mostrar como estender o Explorer.ip para uso em outros processos. O Explorer é um processo utilizado para criação, exclusão e alteração dos registros de quaisquer tabelas de cadastro (filhas da classe Cadastrais) do sistema e também pode ser estendido e customizado.
Os atributos e eventos que podem ser customizados são:
- viewMode: Define o modo de vizualização da grade na primeira escrita. (Grid.FORMVIEW ou Grid.TABLEVIEW)
- baseKey: Chave do registro em que a grade deve estar posicionada. (Number)
- help: ajuda do processo. (String)
- onDefineExplorerGrid: evento que possibilita a customização da grade de dados.
Um exemplo de um processo simples usando o Explorer:
this.baseClass = -1898187808; /* Tipos de Arquivos */
include -1898145512 /* Data Explorer.ip */
Podemos também usar propriedades para alterar a grade, no exemplo abaixo estamos mudando o modo de exibição:
this.baseClass = -1898187808; /* Tipos de Arquivos */
this.viewMode = Grid.FORMVIEW;
include -1898145512 /* Data Explorer.ip */
Pode-se fazer customizações mais específicas utilizando o evento onDefineExplorerGrid
.
No exemplo abaixo iremos criar um novo filtro (extensions) para a grande principal:
this.baseClass = -1898187808; /* Tipos de Arquivos */
include -1898145512 /* Data Explorer.ip */
this.onDefineExplorerGrid.set(function(grid) {
var filter = grid.process.grid("filter");
filter.onDefineFields.set(function(grid) {
inherited(grid);
var fld = grid.field("extension", "string", 4);
fld.defaultValue = null;
fld.saveInputAsDefault = false;
fld.help = "Extensão do arquivo.";
fld.onAfterChange.set(function(field) {
field.parent.process.atualizaFiltroDataSet();
});
var fld = grid.field("CLASS");
});
grid.process.atualizaFiltroDataSet();
})
this.atualizaFiltroDataSet = function() {
this.dataSet.filter = this.pegaFiltroDaClasse();
}
this.pegaFiltroDaClasse = function() {
var filter = this.filter;
var extension = filter.field("extension").value;
if (extension) {
return "javascript:ds.ifileextensions == \"" + extension + "\"";
} else {
return "javascript:true";
}
}
Como executar códigos na inicialização do Web Framework?
Durante o desenvolvimento de alguns produtos, surgiu a necessidade de se executar eventos de interação com o usuário logo após o login no sistema. Para isso desenvolvemos usando um sistema de inicialização para o Framework.
Temos um novo sistema de inicialização que executa os scripts presentes na classe localizada em “/Configuracao/Inicializacao do Web Framework”. Nessa classe está presente a variável global de nome “environment” que é do tipo Environment.
Para sua utilização, deve ser criado um registro novo com uma chave custom na classe -1892603642 (/Configuração/Inicialização do Web Framework). Com a chave custom criada podemos inserir o nosso código diretamente.
var answer = env.confirm('Deseja usar o sistema?', false);
if (!answer) {
env.exit();
} else {
env.alert('Bem vindo ao sistema!');
}
Como ligar e desligar o speed fill?
O speed fill é um tipo de preenchimento automático que permite que o sistema sugira um valor para um campo lookup ou combo, completando a digitação do usuário.
À medida que o usuário digita os dados, aparecem palavras ou frases que podem preencher aquele campo (esta ação é chamada speed fill, auto-preenchimento ou preenchimento rápido).
Caso necessário, é possível habilitar ou desabilitar esse comportamento. O uso do speed fill é configurado separadamente para cada agente de usuário.
Para configurar o uso do speed fill no sistema, navegue até “Admin > Explorer” e utilize a classe “Agentes de Usuário”. A classe possui um campo chamado “Tipo de Auto Complete”.
Caso a opção speed-fill esteja selecionada, o recurso será usado para o agente do registro. Para desativá-lo para o agente de usuário sendo editado, selecione outra opção (ou nehuma opção) e salve o registro.
Como criar grades detalhe?
Exemplo 1: Grade simples
this.interaction("main", function () {
this.ds = connection.cloneLocalCacheByClass(-1898187808/* Tipos de Arquivos */);
this.ds.indexFieldNames = 'iKey';
this.detail = dbCache.getTable('iVfs');
this.detail.indexFieldNames = 'imimeType';
var grid = this.grid("master", this.ds, -1898187808/* Tipos de Arquivos */);
grid.onDefineFields.set(function(grid){
var fld = grid.field("detail", "grid");
fld.readOnly = true;
fld.masterFieldNames = 'iKey';
fld.detailFieldNames = "imimeType";
fld.detailIndexFieldNames = "imimeType";
fld.detailFilter = null;
fld.masterDetailMaxRecordCount = 10;
fld.masterDeleteAction = "throw";
fld.onDefineGrid.set(function(field) {
var grid = field.grid
grid.onCreateDataSet.set(function (grid) {
return grid.process.detail
})
});
});
grid.onBeforePost.set(function(grid) {
if (grid.field("detail").ds.isEmpty) {
grid.process.alert("O DataSet da grade detalhe está vazio");
}
});
grid.write();
});
Exemplo 2: Utilizando eventos específicos da grade Detalhe.
Neste exemplo, ao invés de utilizarmos as propriedades, iremos sobrescrever os eventos da grade, de forma que ela produzirá o mesmo efeito das propriedades que foram definidas no exemplo 1.
this.interaction("main", function () {
this.ds = connection.cloneLocalCacheByClass(-1898187808/* Tipos de Arquivos */);
this.ds.indexFieldNames = 'iKey';
this.detail = dbCache.getTable('iVfs');
this.detail.indexFieldNames = 'imimeType';
var grid = this.grid("master", this.ds, -1898187808/* Tipos de Arquivos */);
grid.onDefineFields.set(function(grid){
var fld = grid.field("detail", "grid");
fld.order = 999;
fld.readOnly = true;
fld.masterDetailMaxRecordCount = 10;
fld.onDefineGrid.set(function(field) {
var grid = field.grid
grid.onCreateDataSet.set(function (grid) {
return grid.process.detail
});
grid.onMasterScroll.set(function (grid) {
var master = grid.master;
grid.ds.setRange(master.iKey,master.iKey);
});
grid.onMasterDelete.set(function (grid) {
if( !grid.ds.isEmpty ) {
throw new Error('É necessário apagar os registros da grade detalhe antes de efetuarmos a deleção.');
}
});
grid.onMasterInsert.set(function (grid) {
grid.process.alert('Essa extensão não tem nenhum arquivo associado ainda!');
});
grid.onMasterPost.set(function(grid) {
grid.process.alert('Detalhe: A master acabou de ser postada!');
});
});
});
grid.write();
});
Como estender a interação showVariables de um layout?
Todo relatório no sistema é, na verdade, um processo com interactions e activities pré-definidas, de acordo com a seguinte estrutura básica:
- interaction showVariables
- interaction run
- interaction writeLayout
Existem ainda outras interações auxiliares responsáveis pelo envio de e-mail, exportação do layout para texto, dentre outras.
Iremos compreender como estender a interação showVariables, que é primeira interação chamada ao ser aberto um relatório, e que é responsável pela montagem da grade de variáveis visando a execução do layout.
Um caso prático:
this.help = 'Esse layout mostra como é possível estender a interação "showVariables"
para, por exemplo, alterar os botões ou escrever um texto antes da
grade principal.';
var fld = vars.field('variavelA', 'int64');
fld.help = '.';
fld = vars.field('variavelB', 'int64');
fld.help = '.';
fld = vars.field('variavelC', 'int64');
fld.help = '.';
this.interaction( 'showVariables', function () {
var label = this.label(this.help + '<br><br>');
label.write();
inherited();
this.visibleActions = ['run', 'NovaAção', 'printTxt', 'sendReport', 'export'];
});
this.activity('run', function () {});
this.interaction('writeLayout', function () {
this.write('resultado');
});
this.activity('NovaAção', function () {
throw new Error('Ação nova executada com sucesso!');
});
O trecho de código destacado em vermelho, mostra a definição (opcional) da interaction
showVariables
.
-
Observe que primeiro estamos escrevendo um label com um texto obtido da propriedade help do layout.
var label = this.label(this.help + '<br><br>');
label.write();
-
Em seguida, a instrução
inherited();
se encarrega de executar o que por padrão a interação em questão faz: que é escrever a grade de variáveis e definir as ações padrão. Se essa instrução não for executada, a grade de variáveis não será escrita.inherited();
-
Por fim, estamos redefinindo as ações que essa interação irá apresentar, acrescentando às ações já existentes, uma nova ação chamada ‘NovaAção’:
this.visibleActions = ['run', 'NovaAção', 'printTxt', 'sendReport', 'export'];
É importante atentar para os nomes das ações pré-definidas pelo SimpleLayout. São elas:
- run
- printTxt
- sendReport
- export
Como exportar os dados de um relatório que não usa SimpleLayout?
iremos mostrar o uso do evento onGetDataExporter
, que serve para exportarmos dados em relatórios
que não usam o SimpleLayout.
Exemplo:
includeOnce -1898143872; /* .../library/export/DataSetExporter.ijs */
var fld = vars.field('quantidadeDeRegistros', 'int32');
fld.help = 'Quantidade de registros que serão exportados.';
this.activity('run', function () {
this.ds = new DataSet();
this.ds.createField('Nome', 'string', 200);
this.ds.createField('Valor', 'int32');
this.ds.create();
var qtdRegistros = this.quantidadeDeRegistros;
for (var i = 0; i < qtdRegistros; ++i) {
this.ds.append(['Registro' + i, i]);
this.ds.post();
}
});
this.interaction( 'writeLayout', function () {
var result = '';
result += '<br><table>';
result += '<tr>';
for (var i = 0; i < this.ds.fieldCount; ++i) {
result += '<td>';
result += this.ds.getFieldName(i);
result += '</td>';
};
result += '</tr>';
for (this.ds.first(); !this.ds.eof; this.ds.next()) {
result += '<tr>';
for (var i = 0; i < this.ds.fieldCount; ++i) {
result += '<td>';
result += this.ds.getField(i);
result += '</td>';
};
result += '</tr>';
}
result += '</table>';
this.write(result);
});
this.onGetDataToExport.set(function(process) {
var dataExporter = new DataSetExporter(process.ds);
dataExporter.title = process.title;
return dataExporter;
});
- Incluímos o objeto de exportação de dados do
DataSet
. - Declaramos as variáveis do relatório.
- Criamos um
dataSet
com uma massa de dados de teste. - Criamos um relatório HTML sem o uso do SimpleLayout.
- Alteramos o evento
onGetDataToExport
para retornar umDataSetExporter
dodataSet
utilizado para gerar o relatório.
Como funcionam os agrupamentos de campos da grade?
No modo formulário, a grade do sistema agrupa automaticamente os campos com mesmo “group” (Field.group). Vejamos o exemplo de definição de campos abaixo, e logo em seguida o resultado da ordenação dos campos.
grid.onDefineFields.set(function(grid) {
var ordem = 0;
var fld = grid.field("campoA", "int64");
fld.group = "Grupo 1";
var fld = grid.field("campoB", "int64");
fld.group = "Grupo 2";
var fld = grid.field("campoC", "int64");
fld.group = "Grupo 1";
});
Os campos serão exibidos na seguinte ordem:
-
Grupo 1
CampoA
CampoC
-
Grupo 2
CampoB
Observe que o campoB, apesar de ter sido definido entre os campos CampoA e CampoC, ficou isolado pois pertence a um grupo diferente.
Existem duas maneiras de definir a propriedade group de um field:
- Ela pode ser definida com uma String. Ex.: “Primeiro Agrupamento”
- Ou pode receber uma instância do objeto FieldGroup, como no exemplo abaixo:
var fieldGroup = new FieldGroup("Grupo Contraido");
// Todos os campos desse agrupamento estarão contraídos no momento da escrita da grade,
// podendo ser visualizados quando o usuário clicar sobre o agrupamento para expandí-lo.
fieldGroup.colapsed = true;
var fld = grid.field("campoF", "string", 30);
fld.group = fieldGroup;
var fld = grid.field("campoG", "string", 30);
fld.group = fieldGroup;
Os campos CampoF e CampoG serão visualizados dentro do “Grupo Contraido” somente após esse ser expandido manualmente
Como utilizar a propriedade aggregate do Field?
Essa propriedade serve para agregar campos que tenham um campo correspondente no DataSet.
Ela permite o uso de funções de:
- Soma
- Média
- Máximo
- Mínimo
- Quantidade
O exemplo abaixo irá mostrar como realizamos cada totalização nos campos, é simples:
this.interaction("main", function () {
this.ds = new DataSet();
this.ds.createField('soma','number');
this.ds.createField('media','number');
this.ds.createField('maximo','number');
this.ds.createField('minimo','number');
this.ds.createField('quantidade','number');
this.ds.create();
this.ds.append([1,1,1,1,1]);
this.ds.append([2,2,2,2,2]);
this.ds.append([3,3,3,3,3]);
this.ds.append([0,0,0,0,0]);
this.ds.append([0,0,0,0,0]);
this.ds.append([0,0,0,0,0]);
// 6,1,3,0,6
this.ds.post();
var gr = this.grid("Agregacao", this.ds);
gr.onDefineFields.set(function (grid) {
var fld = grid.field("soma", "number");
fld.label = "Soma";
fld.aggregate = new SumDataSetAggregate();
var fld = grid.field("media", "number");
fld.aggregate = new AvgDataSetAggregate();
fld.label = "Média";
var fld = grid.field("maximo", "number");
fld.label = "Máximo";
fld.aggregate = new MaxDataSetAggregate();
var fld = grid.field("minimo", "number");
fld.label = "Mínimo";
fld.aggregate = new MinDataSetAggregate();
var fld = grid.field("quantidade", "number");
fld.label = "Quantidade";
fld.aggregate = new CountDataSetAggregate();
});
gr.write();
});
Como utilizar a propriedade canNavigate?
Ao entrar no Web Framework, o sistema precisa descobrir quais diretórios devem ser exibidos no
menu Ir Para. O comportamento atual é que sejam exibidos os diretórios que possuem a propriedade
de x-class canNavigate
com valor true.
Para descobrir quais diretórios possuem esta propriedade habilitada, o sistema inicia uma
exploração da árvore de classes a partir da classe Raiz. Se a classe não configurar esta
propriedade, suas filhas são avaliadas. Este processo é executado recursivamente até que
se encontre uma classe que defina a propriedade canNavigate
.
Visto que habilitados a propriedade canNavigate
apenas nas classes que são módulos do
sistema, a princípio esta implementação teria um problema de desempenho, pois
teríamos que executar praticamente todos os x-class do sistema. Para evitar este comportamento,
temos a possibilidade indicar que um ramo não será navegável no menu Ir Para definindo a
propriedade canNavigate com false. Desta forma, suas filhas não precisam ser exploradas.
Se observamos a nossa árvore de classes, temos 3 grandes ramos:
- Dados
- Configuração
- products
Os dois primeiros não podem conter processos ou relatórios, portanto são configurados com
canNavigate
false. Esta configuração garante que todos os x-class dentro destes ramos não
serão avaliados desnecessariamente na inicialização do Web Framework. O diretório products
não possui esta configuração, pois é nele que estão os processos e relatórios dos módulos.
Por que os detalhes técnicos da ajuda e a pilha do erro não são exibidos pelo sistema?
Os detalhes técnicos nos diálogos de erro e de ajuda podem conter informações privilegiadas que podem ser utilizadas em um ataque, fragilizando a segurança do sistema. Por padrão, essas informações são exibidas apenas para os usuários que participam dos grupos “Administrators” e “Developers”.
Para permitir que outros grupos de usuários visualizem essas informações, basta conceder a esses grupos a permissão ao escopo “security.viewTechnicalInfo”. Essa atribuição deve ser realizada no processo “Admin > Segurança > Grupos, papéis e usuários > Grupos e papéis”. No entanto, considerando aspectos de segurança, deve-se restringir a visualização dessas informações à menor quantidade de usuários possíveis.
É possível declarar um processo em um módulo da Union File System?
Diretamente, não. Os processos precisam ser definidos em arquivos da Virtual File System, pois
a chave do arquivo é utilizada como identificador do processo no controle de permissões do
sistema e o menu de navegação é construído a partir da estrutura de diretórios da
Virtual File System. No entanto, o script da Virtual File System pode delegar
a definição do processo para um módulo JavaScript por meio da função loadModule
conforme exemplo abaixo:
// Vfs File "/products/custom/modules/Example/Process.ip"
this.loadModule('custom/modules/Example/Process.js');
// Ufs File "/package/custom/modules/Example/Process.js"
/** @param {import('@nginstack/web-framework/lib/process/Process.js')} process */
module.exports = function (process) {
process.help = 'Example process.';
process.interaction('main', function () {
const label = process.label('Hello World from module!');
label.write();
});
};