sábado, 12 de novembro de 2016

Global Events


Os habitantes de Funafuti acordaram muito preocupados nesta última semana. E isto se deve principalmente aos últimos acontecimentos, também pudera, não é sempre que o famoso Tuvalu-News noticiou algo tão estranho como a eleição americana. Simplesmente a bolsa de valores de Tuvalu teve uma queda expressiva!

Realmente estamos vivendo uma era de eventos globais, e a eleição nos Estados Unidos é um bom exemplo, pois como pode algo específico do povo americano afetar o funcionamento do mundo inteiro? Será que os americanos se deram conta da responsabilidade que possuem neste momento? Será que ao saírem de casa, no dia da eleição eles pensaram, muito bem vamos derrubar a bolsa de Tuvalu?

Por mais que gostem ou não do resultado, alguns países ficam mais 'afetados' com o problema dos outros. A própria imprensa brasileira está 'decepcionada' com o resultado, e são intermináveis programas, entrevistas, debates sobre este 'tão relevante' assunto para o Brasil, e claro que a conclusão de todos é sempre a tal: vamos ver como é que fica.  Também como explicar uma imprensa que torce ao invés de reportar.

De fato vivemos momentos globais.

Mas deixemos o Trump trabalhar e o pessoal de Tuvalu jogar o seu kilikiti em paz, e vamos ao que, de fato, interessa.


Global Events

Assim como o acontecimento americano, Genexus 15 também afetou a economia. Porém, não estamos falando de dinheiro, e sim da economia de se criar uma única WebPanel para todas as interfaces do sistema! A chamada Single Page Application.

Este tema ficou meio esquecido principalmente porque faltava exatamente o Global Events, e os resultados produzidos não eram, assim, tão legais.

Acontece que GeneXus 15 nos entregou um novo objeto em nossa Kb, o Global Events, que ao ser configurado nos permite que os nossos WebComponents possam 'conversar' com o WebPanel.


Lembrando que isso não era então possível, simplesmente porque o WebPanel tinha a mania de não escutar o que o WebComponent tentava lhe falar, era quase um monólogo onde o painel passava seus parâmetros para o componente, e esperava que este funcionasse, porém não se preocupava nem um pouco se este produzia alguma coisa que lhe pudesse interessar.

Esta situação de falta de comunicação ficou muito evidente com a GeneXus Ev3 porque a mudança do cenário dos eventos Smooth, já não aceitava mais o conceito de reconstruir a interface inteira com um Refresh total, coisa das 'antiquadas interfaces' da Ev2, Ev1, e dai pra baixo. Isto passou a ser quase um pecado, pois como você pode fazer algo com o avançado recurso de atualizar apenas um pedaço da interface (Smooth), se de vez em quando tem que ser forçado a atualizar tudo, parece que a coisa perde um pouco sua importância.

Uma Single Page Application de uma única via também parecia ter um limite de funcionamento, então, o pessoal, 'ouvindo a voz da comunidade' meteu a mão na massa e nos entregou o Global Events.

Nada como termos uma conversa franca, não é mesmo?

Para que serve isso?

Simples, serve para que você possa enviar para um WebPanel alguma ocorrência de um de seus componentes, e com isso permitir que o painel principal possa fazer alguma coisa.

Por exemplo, se você possui um WebPanel e precisa carregar um component, o comando Create é suficiente para enviar para este os parâmetros necessários para sua construção.


Desta forma um comando Component1.Object = Comp1.Create(&p1, &p2), vai passar para o objeto Comp1, que é um WebComponent, os parâmetros &p1 e &p2. E assim o objeto Comp1 será incluído na interface e apresentar seu conteúdo.

O problema é que o objeto Comp1 não possui uma maneira eficaz de enviar para o WebPanel o resultado de algum evento que possa ocorrer. Por exemplo, se este tiver um Grid, ao se selecionar uma linha, não teremos como enviá-la para a página principal. Pode-se dizer que utilizávamos algumas técnicas aleatórias profissionais para fazer isso.


Bem, não tínhamos, agora temos, pois com o Global Events, registramos um evento que ao acontecer disparará uma ação global no sistema, e poderemos interceptar esta ação em nosso WebPanel. Não fique nervoso, é simples.

É mais ou menos assim, registre um novo evento no Global Event, definindo os parâmetros que utilizará para realizar a comunicação.
Em seguida, para gerar o evento será necessário 'chamar' este evento, num formato muito parecido com a chamada a um objeto qualquer. Entre parenteses se deve informar o valor a ser transmitido,a como, por exemplo, GlobalEvents.MeuEvent(2). Esta ação é realizada no WebComponent.

Esta operação disparará um evento global, que poderá ser interceptado por um WebPanel. Para que isto ocorra será necessário criar um Event GlobalEvents.MeuEvento, neste WebPanel.



Em seguida,  uma vez que o evento tenha sido interceptado com Event MeuEvento, será possível tomar uma decisão, como por exemplo, se trocar o Comp1 para outra interface qualquer, criando o efeito da Single Page Application. Teríamos vários componentes a serem apresentados em apenas um WebPanel.

Um Exemplo

No Wiki HowTo: Using Global Events in Web Objects temos um excelente exemplo apresentado pela Sabrina Juarez, inclusive com o XPZ que pode ser baixado e colocado em sua Kb. Neste a Sabrina apresenta uma lista de autores e seus livros, e quando se escolhe algum no Grid à esquerda, os componentes que apresentam os detalhes são disparados.


É um recurso muito interessante e simples de entender.  No exemplo, apenas um pequeno detalhe, caso ocorra um erro na carga das imagens, no DataProvider LiteraryWork_DataProvider, mais especificamente nas linhas que carregam as capas dos livros, devem ser comentadas. // LiteraryWorkImage = Link("bodas_de_sangre")

Conclusão

O Global Event do GeneXus se mostra um recurso excepcionalmente interessante e com certeza vai se adequar muito bem aos projetos mais ambiciosos de interface.

Infelizmente, quanto à eleição americana, parece que o mundo também tem sido influenciado globalmente pela falta de candidatos adequados para que a população possa escolher alguém de fato representativo. Os políticos de aqui e de acolá estão adotando aquela velha estratégia suja de fechar uma lista de pessoas que infelizmente não poderiam ser escolhidas, na qual o melhor seria não escolher ninguém, digamos, que tal o menos pior?

Se a tendência perdurar até 2018, pode ser que tenhamos um Trump aqui também.







terça-feira, 11 de outubro de 2016

Web Live Editing

Normalmente levamos um certo tempo para assimilar as coisas novas, inclusive é esperado que percamos também algum tempo entendendo tudo. Minha experiência com live editing foi assim, porém, após três horas tentando fazer a coisa funcionar, sem sucesso, uma inocente tentativa de abrir o programa em um outro navegador fez com que o endpoint fosse imediatamente construído, :(

Portanto, antes de mais nada, feche seu Firefox e abra o Chrome, e em seguida se quiser ver o recurso funcionando bonito, bastará respeitar algumas regrinhas:

1) Execute a aplicação Genexus em modo Live Editing


2) Abra a aplicação no Chrome, Opera, Microsoft Edge, Vivaldi, ...,  (somente no Firefox não funciona, vai entender)

3) Na interface do Genexus verá que um endpoint é definido para o navegador. Uma bolinha verde indicará que o Genexus está conectado ao navegador. E assim, qualquer ajuste refletirá imediatamente no navegador aberto. Se abrir uma segunda janela do navegador, um novo endpoint será criado para esta.


4) Na Web somente ajustes realizados no tema Carmine atualizarão imediatamente o conteúdo do navegador. Isto significa que é possível trocar cores, fontes, dimensões de texto, porém, não será possível se incluir um novo recurso na tela, como por exemplo um novo botão.  A estrutura da página deverá estar montada, o live editing ajudará no ajuste visual final.

Portanto, acredito que uma boa prática para usar o recurso seria construir a estrutura da interface, e em seguida quando entrar no modo ajuste fino, se liga o recurso para alinhar as fontes e aspectos visuais.

Troca de imagens também não funciona, portanto, até o momento o recurso está um pouquinho limitado, mas a idéia com certeza é fantástica!

Maiores informações




quarta-feira, 5 de outubro de 2016

User Control, um pequeno resumo

Durante o evento Genexus Gx26 apresentamos uma palestra resumindo a construção de User Controls.

Para os interessados, o link é o seguinte:

GX26 Meeting
Desenvolvendo Web User Controls para GeneXus 15

O processo apresentado é aplicável às versões anteriores do Genexus.

quinta-feira, 22 de setembro de 2016

User Controls

Existem alguns temas que parecem tão complexos e obscuros que afugentam a grande maioria das pessoas, como se fosse uma selva sombria e úmida. Os poucos que desbravam esta densa vegetação, creio que em busca de uma aventura, acabam como heróis por sua coragem, desprendimento, e principalmente por encontrar as riquezas ocultas.

Esta era minha a sensação quando se falava User Control, um tema oculto e destinado apenas aos mais destemidos, que, sendo sincero, nunca tive muita intenção de abraçar, mas que ao mesmo tempo aguçava demasiadamente a minha curiosidade, e claro admiração.

Acontece que novos caminhos levaram a uma trilha na qual, o que se via a frente era apenas esta densa floresta, e nenhum desvio, a não ser a única opção de ingressar literalmente no meio do mato.

Trilhando um novo caminho

Com o advento do Abstract Editor o que pudemos observar foi que o pouco de HTML que poderíamos recorrer quando o calo apertava, simplesmente foi ocultado em um novo nível de abstração, que simplesmente deixou de aceitar baixarias, ou melhor, 'baixo nível'.

O mesmo editor também causou um bom incômodo, por nos permitir criar as tais maravilhosas interfaces responsivas, porém, com parcos recursos. Até o momento, sem o Work With for Web não saímos do lugar.

Foram muitos artigos aqui no Genexando nos quais apresentávamos algumas formas de produzir conteúdo HTML + CSS na interface, como, por exemplo, textblock.caption, mas mesmo isto, apesar de ser possível, não parece mais ser uma solução elegante e consistente no modelo atual. Nada contra este recurso, ainda o utilizo, mas que tal buscarmos algo novo?

O pior é que a solução para estas questões já estava disponível desde 2007, sob o nome de User Control, a única coisa que faltava era uma boa documentação.

User Control

No meu humilde ponto de vista, User Control acaba de ganhar muita importância neste novo cenário, pois, de repente, nossos recursos foram limitados basicamente aos Common Controls do Toolbox.

A razão é simples, nenhum (ou quase nenhum) User Control atualmente publicado no Marketplace está preparado para o cenário da responsividade. Encontram-se ainda sob a ótica das interfaces Web tradicionais, e vão exigir, no mínimo uma boa revisão por parte dos nossos heróis desbravadores.

Os User Controls também oferecem uma forma muito mais elegante para resolvermos a questão do uso de recursos para construirmos interfaces convincentes, que são as excelentes bibliotecas externas em Javascript que diariamente nos entregam novas funcionalidades, e que caberiam muito bem aos nossos projetos.

Desta forma, com a motivação adequada e certo de que poderíamos sair vivos desta selva, buscamos a ferramenta adequada para enfrentarmos o desconhecido. E o resultado desta aventura, também se mostrou um enorme desafio.

O que é um User Control?

Dificilmente você não utilizou um User Control, é quase impossível uma interface GeneXus sem pelo menos um deles. Porém, em termos estruturais, acredito que pouca gente conheça que é um pequeno projeto de um componente de interface, no qual definimos suas configurações de instalação,  aparência no GeneXus e funcionalidade no navegador do usuário. No mínimo teremos sempre quatro arquivos para registrar todas estas características.

Uma vez construído e instalado, teremos o recurso à nossa disposição no Toolbox do objeto GeneXus. Ou ainda, na propriedade Control Type de atributos e variáveis.

O objetivo fundamental será sempre o de oferecer algum recurso visual ou funcional diferenciado na interface do usuário, e que pode ser um novo controle para ingresso de dados, novos componentes de navegação como menus, navbar, dropdowns, ou mesmo recursos para controlar as ações dos usuários.

Para chegarmos a isto necessitaremos conhecer novos recursos como a linguagem Javascript, CSS, HTML, XML e XSLT. Mas também bibliotecas externas que eventualmente nos forneçam o recurso que desejamos implementar. Além dos próprios arquivos de configuração.

Não é necessário ser um programador profissional destes recursos para se alcançar resultados excelentes, veja meu caso, por exemplo, rs.

User Control: Construindo controles com o GeneXus


Como estratégia de aprendizagem deste tema peculiar, praticamente tive que ler e interpretar o funcionamento da grande maioria dos User Controls do Genexus Marketplace, entender suas semelhanças e diferenças, e em seguida buscar uma explicação plausível para seu comportamento.

A intensa programação Javascript por muitas vezes ocultavam importantes segredos, assim como as classes CSS que quando em funcionamento também não respondiam a contento, enfim, um cenário muito desafiador.

O Genexus Wiki, uma fonte importantíssima de informação, desta vez não contribuiu muito.

Ao final foi simples chegar a uma conclusão: o tema é muito importante para ficar escondido no mato.  E como resultado desta aventura, resolvi escrever um livro sobre este tema desafiador, principalmente porque o pouco material disponível praticamente pouco me ajudou.

A idéia por traz do livro foi simples: dividir os temas mais complexos em tópicos e apresentar muitos exemplos práticos para reforçar todo conteúdo apresentado. Com isso chegamos a onze capítulos que vão apresentar e desenvolver todos os recursos necessários para se construir seu próprio User Control. E ao término de cada um, uma atividade prática com o objetivo de desenvolver um controle útil com o recurso Bootstrap, ou seja, o mesmo framework utilizado pelo GeneXus para construir as interfaces responsivas.

Um detalhe, não abordo no livro User Controls para Android e iOs, nesta edição limitei a controles para Web.

Então, se você também sofrer da mesma doença: usercontrolitis curiositis, terá a sua disposição um material que eu acredito, irá te ajudar nesta aventura.

Disponível aqui:

https://www.amazon.com.br/User-Control-Desenvolvendo-controles-GeneXus-ebook/dp/B01LZT1NX7/ref=sr_1_5?ie=UTF8&qid=1474121129&sr=8-5&keywords=user+control

https://www.amazon.com/User-Control-Desenvolvendo-controles-Portuguese-ebook/dp/B01LZT1NX7/ref=sr_1_7?s=books&ie=UTF8&qid=1474120886&sr=1-7&keywords=user+control#nav-subnav



quinta-feira, 7 de julho de 2016

Stop o Post

Desculpem o trocadilho, mas a piada já estava pronta, não resisti, e no final das contas STOP e POST combinam perfeitamente com o assunto que eu quero abordar, e, incrível, possuem exatamente as mesmas letras.  Enfim é necessário PARAR um pouco para pensar no POST.

Postando coisas

Existe uma característica nos sistemas Web que permite que tenhamos um excelente mecanismo de persistência nas chamadas ao servidor que consiste no simples ato de colocar uma variável na interface. Parece pouco, mas ao fazermos isso estamos fazendo com que diversas chamadas para a mesma interface mantenha uma informação persistente. Por exemplo, se colocarmos o código de um produto em uma variável na interface, se o usuário pressionar um botão Confirmar, por exemplo, o conteúdo desta estará disponível ao evento de usuário, Refresh e Load do nosso programa Genexus, sem que tenhamos que nos preocupar em buscar alguma coisa no banco de dados, por exemplo.

Esta é uma característica de um formulário Web, e toda interface Genexus é um FORM por natureza, se você olhar no código fonte suas páginas encontrará uma linha que define o mesmo.

<form id="MAINFORM" name="MAINFORM" method="post" ... >

Aliás POST é uma palavra muito bem escolhida pelo Tim Berners-Lee  para representar o que acontece quando o botão Confirmar é pressionado, alias vamos abrir um parenteses aqui, porque graças a ele temos emprego hoje, rs. Mas enfim, o Tim pensou no seguinte, vou montar uma agência de correio no navegador do usuário e simplesmente enviar um pacote do tipo SEDEX para o servidor toda vez que ocorrer um Confirmar, e o que tem dentro deste pacote? todas as variáveis da sua interface e seus conteúdos, e o tal Confirmar (para nós) é o tal Submit para o Tim.  Isso é o tal POST.

Neste ponto ele foi categórico ao afirmar que tudo que estiver na interface em forma de variável deverá ser incluído, humm, parece meio radical, mas no final das contas não temos como dizer ao navegador para não incluir alguma coisa que eventualmente não desejamos neste pacote.

Enfim, se tudo que é definido na interface em formato variável é devolvido ao servidor no POST então deve haver alguma diferença no tamanho do tal pacote caso tenhamos uma única e singela variável ou uma duzia delas na interface não? E este é o ponto que precisamos parar pra pensar, porque ao colocarmos uma duzia de variáveis na interface estaremos aumentando o preço do SEDEX, pois como sabemos uma carta simples de até 60g custa apenas um selinho de R$ 1,70, mas um pacotão de 500g vai custar R$9,10, e claro, neste mundo capitalista, tudo custa alguma coisa para alguém, inclusive o passe gratuito de ônibus para os estudantes do Passe Livre.

Onde está o custo das coisas?

Nosso programa Genexus roda no servidor correto? na interface do usuário roda o resultado do processamento deste programa, correto? Então temos duas fases na qual ocorrem processamentos, no servidor em primeiro lugar e em segundo lugar no navegador do usuário. Vou recorrer a um desenhinho (meio tosco) para mostrar isso.



  1. Neste esquema o usuário solicita alguma coisa ao servidor (1), neste caso temos a latência da internet que atrapalha um pouco e vai consumir alguns segundos para que seu pedido chegue. 
  2. O servidor, quando recebe alguma tarefa, dispara o processamento da mesma (2), e eventualmente ocorrerá chamadas ao banco de dados (3) para que alguma informação adicional seja adicionada. Teremos aqui diversos elementos que contribuirão no custo, desde a capacidade do servidor (memória, processador), como também a quantidade de pedidos que o mesmo responde por segundo, pois muita gente poderá estar solicitando coisas simultaneamente, e todas devem ser processadas para serem devolvidas. Mais pedidos simultâneos, mais consumo de recursos do servidor e eventualmente mais problemas de desempenho desta infra-estrutura.
  3. Ao término do processamento começa o processo da agência de correios, pois o servidor montará um pacote para enviar ao navegador, e claro, poderá ser um pacote pequeno ou grande de acordo do que foi programado, ao término da montagem o mesmo é enviado ao navegador (4). Se tínhamos uma latência no item (1) teremos a mesma amplificada pelo tamanho do pacote na fase (4), portanto, se diminuirmos ao máximo esta resposta, será melhor, pois vamos chegar próximo ao tempo minimo que é exatamente o tempo da solicitação (1).
  4. No navegador teremos que pegar o pacote recebido pelo servidor e processá-lo para transformar numa imagem a ser apresentada para o usuário (5)  e se tudo estiver certinho, a mesma será apresentada na interface (6). Aqui os desenvolvedores normalmente não se preocupam muito, mas se o usuário estiver rodando um Windows XP com 2Mb de memória RAM, o desempenho da interface no navegador Nestcape vai ficar bem ruinzinho, se a coisa for muito complexa para a maquininha resolver.


O segredo de um bom desempenho até aqui é diminuir ao máximo o tamanho do pacote entre o navegador e o servidor, pois com isso poderemos melhorar todos os pontos, pois menor resposta = menor necessidade de processamento, menor tamanho do pacote, enfim, o Steve Jobs já falou isso e alguns ouviram, a simplicidade é tudo.

Bom, vamos ao problema. Acontece que se pegarmos o POST do Tim, e colocarmos na equação teremos algo 'a mais', pois, quando retornarmos ao servidor, passo (1), quando pressionarmos Confirmar vai acontecer que ao tempo de latência anteriormente avaliado agora teremos que colocar também a montagem do pacote no navegador e a transmissão do mesmo ao servidor (1), além da necessidade de processar o pacote recebido na execução do script (2).

Pacotes do Genexus

Considerando que você tem usado o Genexus de forma regular e normal, com seus controles Attributes, Grids e FreeStyleGrids, ..., e de vez em quando reclama do desempenho da coisa, preciso te informar uma coisa, o problema talvez não esteja na sua lógica, na lógica do Genexus de criar queries para o BD, ou mesmo programar C# e Java, mas exatamente na sua interface.

E sabe porque? imaginemos um Grid na interface contendo um conjunto de 10 variáveis, a respeito de seu cadastro de clientes, e ainda, a pedido do usuário, apresentemos 200 registros de clientes ao mesmo tempo. Teríamos um pacotinho formado por 10 x 200 x o tamanho em bytes de cada variável, a ser processada no servidor e enviada ao navegador. E.... do navegador ao servidor quando você pressionar algum evento do tipo POST. Ou seja, a informação em formato variável na interface será devolvida ao servidor no POST, e nosso grid pesadão vai também ser enviado ao servidor de volta, e sabe porque? somente para voce ter um for each line in GRID.

Desta forma,, temos a beleza do comando for each line denegrida pela queda significativa de desempenho necessário para que a informação da interface seja devolvida ao servidor. Fazer o que, como falei antes não existe passe de ônibus livre.

Testando

Se você tiver um programa que te apresente um timeline com o tempo de resposta do servidor, normalmente os navegadores atuais dispõem de um recurso assim, poderá, construindo um webpanel com um Grid cheio de coisas, medir o tempo gasto na chamada GET e posteriormente na chamada POST.

Construindo uma interface de testes é simples, escolha uma tabela que contenha muitos registros, e monte um GRID recuperando um conjunto significativo deles. Em seguida faça variações neste GRID para diminuir o que será devolvido ao servidor.

Caso 1) Montei um webpanel simples com um GRID utilizando atributos com 10 colunas e 500 registros e obtive os seguintes tempos de resposta.

tempoenviadosrecebidos
1o GET01.46766729.882
2o GET01.01563129.827
3o GET
01.019
63129.827
1o POST
01.086
196.09828.605
2o POST01.038196.85828.605
3o POST01.049196.85828.605

Caso 2) O mesmo conjunto de colunas, mas desta vez montando o mesmo GRID com variáveis.

tempoenviadosrecebidos
1o GET03.499631    46.311
2o GET03.02263146.311
3o GET03.06463146.311
1o POST03.025136.44744.244
2o POST02.942137.56444.244
3o POST02.906137.56444.244


Já é possível perceber o problema quando se olha as linhas GET e POST em ambos os testes. A coluna enviados mostra bem que no POST as informações que estão no navegador são enviadas ao servidor.

Um detalhe interessante é que com variáveis o Genexus produz mais código, aproximadamente 54% maior.

O tempo também é influenciado, ou seja com atributos é menor, minha teoria para isso é a necessidade das alocações no Genexus, para se atribuir cada variável e se executar o LOAD acaba gastando mais recursos de processamento.

Normalmente nos preocupamos com a primeira carga da interface, mas se você observar, o envio da informação ao servidor a partir desta primeira carga é que pesa.

O que fazer?

Diminua sua interface, use menos variáveis, não utilize o GRID.
Não deixe o Automatic Refresh ligado, ao invés disto prefira um botão para realizar a filtragem dos registros.
Não carregue Grids com informações 'default', ou seja, aguarde a pessoa definir o que deseja ver.



Os dados que alcançamos são meio estranhos, e cabe um estudo mais aprofundado para identificar porque os POSTs são exageradamente grandes. Os tempos também sofreram influência, apesar dos pacotes terem dimensões parecidas, enfim, precisaremos abordar a questão em um outro artigo.


Por enquanto, recomendamos juízo no POST.






quarta-feira, 6 de julho de 2016

MathML

Precisa expressar alguma função matemática? Conhece o MathXML?

Pois é, acabei esbarrando por acaso nesta notação e gostei bastante do resultado. Como temos muitos professores que utilizam Genexus, vamos dar uma palhinha neste assunto.

O que é?

O MathML é uma linguagem de notação que resulta em uma imagem que normalmente é utilizada para produzir uma expressão matemática. De maneira geral,

A organização que está apoiando a iniciativa é o W3C, ISO, ou seja, é um assunto sério. Ver:


Esta linguagem é dependente dos navegadores que devem implementar a interpretação das definições da especificação.

Como usar em Genexus

Será necessário produzir um conteúdo HTML para apresentar a fórmula matemática, uma forma de fazer isso em Genexus é incluir no Caption de um Textblock.  Utilizamos uma variável &expressao do tipo Character(100) para criar a expressão.

Event Start
 &expressao   = '  <math xmlns="http://www.w3.org/1998/Math/MathML">'
 &expressao  += '    <mrow>'
 &expressao  += '      <mi>a</mi>'
 &expressao  += '     <mo>⁢</mo>'
 &expressao  += '      <msup>'
 &expressao  += '        <mi>x</mi>'
 &expressao  += '        <mn>2</mn>'
 &expressao  += '      </msup>'
 &expressao  += '      <mo>+</mo>'
 &expressao  += '      <mi>b</mi>'
 &expressao  += '      <mo>⁢ </mo>'
 &expressao  += '      <mi>x</mi>'
 &expressao  += '      <mo>+</mo>'
 &expressao  += '      <mi>c</mi>'
 &expressao  += '    </mrow>'
 &expressao  += '  </math>'
 textblock1.Caption = &expressao
Endevent


O resultado é interessante.



Atenção! nem todos os navegadores dão suporte ao recurso, o recém lançado Vivaldi, por exemplo, não consegue interpretar a coisa ainda.


Formatando com casas decimais

Resultado de imagem para Cultureinfo image
Um problema que enfrentamos no Genexus, na construção de relatórios impressos (PDF, por exemplo), é a impossibilidade de aplicarmos uma mascara que formate milhares e casas decimais a um certo número, isto para que saia dentro dos padrões dos diversos países.

Quando programamos WebPanels e outras interfaces temos alguns recursos que auxiliam na apresentação, como User Controls, Thousand Separator e Pictures, porém quando falamos em impressão PDF os recursos ficam escassos.

Um mesmo valor de 1000 apresentado no sistema Português-Brasil e Americano, deveria resultar em um número apresentado no seguinte formato:

    R$ 1.000,00
    U$ 1,000.00

Infelizmente não conseguimos fazer isso com muita facilidade.

Como Resolver?

Quando esgotamos as possibilidades poderemos então recorrer aos universitários, e existem algumas formas de fazer isso, optei por programar diretamente na linguagem C# que possui uma classe chamada CultureInfo que resolve bem esta parada. A solução final pode ser feita de duas maneiras, programando diretamente no código de nosso procedimento ou mesmo criando uma classe externa na linguagem nativa que importaremos com o External Object.  Ambos os recursos já exploramos aqui no Genexando.

Nosso exemplo seguirá a programação direta no código, porém já salientamos que esta prática não é boa, pois o objeto ficará refém da linguagem programada, e no caso de um novo environment que venha a ser criado posteriormente com outra linguagem de programação estaremos inviabilizando a operação. Cuidado com este tipo de solução porque uma das melhores características do Genexus é a independência tecnológica e este tipo de alternativa afeta de forma significativa a troca de gerador. Se ao invés de programar direto voce optar por uma classe externa com uma DLL que faça a operação, melhor.

Para utilizar a classe CultureInfo será necessário conhecer o código que representa o país e código da região, normalmente formado por duas letras seguida de um hífen e posteriormente o código da região, como por exemplo: pt-BR.

Apresentamos a seguir alguns desses códigos.

en-USEnglish (USA)
pt-BR Portuguese (Brasil)
es-ESSpanish (Spain)
ja-JPJapanese (Japan)
zh-TWChinese (Taiwan)
zh-CNChinese (PRC)
zh-HKChinese (Hong Kong SAR)
zh-SGChinese (Singapore)
zh-MOChinese (Macao SAR)

Programando a coisa

Para programar o formatador precisaremos de uma procedure, que chamaremos de FormatNumber, que definimos com o seguinte:

Rules:
parm(in:&valor, out:&final);

Source:
csharp decimal number = [!&valor!];
csharp [!&final!] = number.ToString("N",new System.Globalization.CultureInfo("en-US"));

Variables:
&valor   Numeric(12.2) // por exemplo!
&final   Character(20)

Utilizando

Para utilizar o conversor será simples, bastando chamar a procedure passando o numero a ser formatado, e recebendo uma string com o valor

&Resultado= FormatNumber(&ValorAFormatar)

O resultado será uma string que poderá ser incorporada ao PDF com o formato da moeda que se deseja apresentar.


segunda-feira, 9 de maio de 2016

Revoluções silenciosas...

Certas funcionalidades do Genexus mudaram silenciosamente na Ev3, nenhum alarde foi feito, talvez devido a operação lava jato, no Brasil, estejamos nos acostumando com noticias explosivas e ruidosas, e quando a coisa é mais discreta não damos a devida atenção. Desta vez acredito que Genexus nos entregou um modelo um pouco mais flexível, portanto, melhor.

Estou falando sobre a determinação da tabela base e estendida, tema fundamental para quem desenvolve sistemas no Genexus, e que já tivemos a oportunidade de apresentar aqui no Genexando em dois artigos.


O fato é que Genexus define a tabela resultante (tabela base) que será utilizada para recuperar registros quando programamos um for each, e também as tabelas que irão complementar a informação que desejamos, definindo um conjunto chamado tabela estendida, que é a soma da tabela base e das demais complementares.


O que devemos fazer em um comando for each é dizer quais atributos desejamos, a ferramenta faz o resto.
 
Tomemos como exemplo o modelo apresentado acima. Se programarmos um for each, como o apresentado a seguir, com os atributos ClienteNome, CidadeNome e PaisNome, teremos como tabela base a Cliente e a estendida Cliente-Cidade-Pais.




Não será necessário fazer nada, para que tenhamos uma pesquisa completa na tabela Cliente com o nome da cidade e país incluída na apresentação, este modelo é natural no Genexus, e muita gente utiliza isto sem perceber o recurso.


A revolução ocorreu no seguinte sentido, uma nova cláusula no for each foi entregue, a base transaction, ou seja, podemos informar a transação que originará a tabela base do modelo. Desta forma poderíamos programar de forma explicita a transação Cliente no for each. Para transações com dois níveis teríamos que colocar o nome da transação e o nome do segundo nível, por exemplo, NotaFiscal.Produto. Algo como:



O fato é que podemos alterar a base transaction do for each para apontar para outra tabela qualquer, e aqui apresentamos o efeito da revolução. Se por exemplo, dissermos que a base transaction do exemplo seja a Cidade, nos Genexus anteriores teríamos um erro no for each, pois o Genexus não alcançaria o ClienteNome, e como resultado, um ruidoso erro indicando um não relacionamento entre os atributos.

Na versão EV3 do Genexus, o erro de não relacionamento não ocorrerá e em seu lugar teremos um aviso que o ClienteNome não poderá ser alcançado. Observe que a tabela base do exemplo passa a ser Cidade, como determinamos no for each Cidade, e Cliente passa a não fazer parte da solução.


Podemos levar este cenário ao extremo, ao apontarmos como tabela base uma que não poderá ser resolvida por meio do conceito da tabela estendida, façamos uma pequena modificação no modelo, acrescentando um conceito de Empresa que possui Clientes.



Se pegarmos o mesmo for each anterior e apontarmos para Empresa, nenhum atributo poderá ser alcançado pelo conceito da tabela estendida, e o Genexus será compreensivo e não apresentará nenhuma mensagem de erro, apenas avisos.



Portanto, muita atenção na leitura do Navigation View para não deixar as mensagens de warning passarem desapercebidas. Acredito que este novo modelo ficou melhor, porém exigirá mais atenção do desenvolvedor.

Apoiemos as revoluções silenciosas, e também as ruidosas, desde que sejam importantes.


domingo, 17 de abril de 2016

OWASP em Brasilia!


A nova liderança do capítulo da OWASP em Brasília convida para o primeiro evento de 2016 GRATUITO, de interesse de desenvolvedores, profissionais de segurança da informação, estudantes das áreas de computação e Tecnologia da Informação e demais interessados no assunto.

O 1° Encontro OWASP 2016 será um evento de Segurança da Informação, mais especificamente Segurança em Aplicações, que acontecerá no dia 04 de Junho de 2016
 em Brasília, DF. O evento contará com profissionais da área de Segurança da Informação de diversos Estados, como também diversos membros da OWASP. 

sexta-feira, 15 de abril de 2016

Login

Pergunta recorrente pelos desenvolvedores iniciantes, como desenvolver um login?
Esta é o primeiro muro que um desenvolvedor sofre ao se deparar com o Genexus, e muitas vezes a dificuldade não está na compreensão do processo em si, mas na escolha dos elementos necessários para realizar a operação.

Me desculpem os veteranos, mas no intuito de contribuir com os recém-nascidos, vamos responder isso de uma vez por todas, rs.

Use o GAM

A melhor opção que você poderá desenvolver é utilizando o GAM, pois é um recurso que já está disponível, foi desenvolvido mediante o protocolo OAUTH e protegerá todos os seus objetos expostos incluindo webservices.

Simples de utilizar, bastando nas Preferences ligar o Enable Integrated Security=Yes, em seguida trabalhar na configuração dos perfis (Roles) e usuários (Users).

O GAM poderá se comunicar com sistemas de login existentes por meio dos métodos de autenticação externa, que utilizará um webservice para conectar ao seu sistema remoto.

Maiores informações

Porque precisamos de Login?

Para os teimosos que pretendem escrever seus próprios mecanismos, vamos a uma estrutura básica.

Fazer login significa fornecer informações suficientes para que o script em execução no servidor possa identificar a pessoa que está utilizando o sistema. Desta forma será necessário ter algumas informações para que o controle possa ser efetuado. Previamente será necessário cadastrar as pessoas que serão identificadas, sendo que normalmente necessitamos de duas informações para proceder a identificação: usuário ou email e a senha. Uma transação serve a esta finalidade.
 

Algumas chaves possíveis para representar usuários:
  • Email: alguns sistemas utilizam email para identificar uma pessoa, o que não deixa de ser uma boa estratégia porém, muitas pessoas preferem ficar trocando de caixa de email de tempos em tempos, temos também pessoas que utilizam email de outra pessoa, como por exemplo, a namorada apaixonada que utiliza o email do namorado (acuado), claro que com primeiras intenções.
  • CPF: para os brasileiros temos o numero do CPF que também pode ser uma boa opção de chave, porém temos também algumas dificuldades, principalmente para pessoas que utilizam o CPF de terceiros (o filho que utiliza o CPF do pai ou do avô), e estrangeiros também não o possuem.
  • Palavras simples: apesar das críticas ainda acho que uma palavra definida pela própria pessoa seja a melhor estratégia para usuários. Colocando a palavra como chave primária teremos a garantia de que não será repetida, portanto, temos uma boa e livre opção.]
Teremos também a necessidade de identificar objetos (programas), perfis e associar tudo isso ao usuário.

 
 Veja que a complexidade vai aumentando, por isso a recomendação pelo GAM.

Processo de Login

Quanto uma pessoa utiliza um navegador de internet para se comunicar com um servidor Web se estabelece um registro da conexão que é a denominada chave de sessão. Trata-se de um valor randômico que inclui letras e números, único e que vai permitir entregar a comunicação de forma adequada para o requisitante.


A chave de sessão é criada  com ou sem operações de login, ou seja, é uma característica da Web, bastando chamar uma página ou script, que a mesma será criada. Localmente se criará um cookie e no servidor uma websession. Na imagem acima a chave é apresentada na variável Valor.

O servidor Web armazena esta chave em uma porção de memória chamada de websession, juntamente com outras informações que permitem estabelecer o contato com o cliente remoto. O endereço IP, e principalmente a data e hora da última comunicação com o servidor, de forma que depois de um certo tempo limite de inatividade (Time Session) o servidor pode destruir as sessões que expiraram (normalmente a sessão dura 20 minutos).

O processo de login normalmente utiliza o conceito do websession para armazenar alguma informação que determine que o cliente remoto passou pelo processo de identificação, para fins de controle de autorização para acesso aos objetos, normalmente o Perfil de acesso. A este perfil associa-se os objetos que possui privilégio de acesso, e por ai vai.

Operação de Login


A operação de login é simples, bastando um webpanel com duas variáveis e um botão.


E ao pressionamento do Confirm, a avaliação para saber se a pessoa informou corretamente os dois valores.

Event Enter
    &erro = ''
    for each
        where UsuarioId = &UsuarioId
        if UsuarioSenha <> &UsuarioSenha
            &erro = 'Senha incorreta'
        endif
    when none
        &erro = 'Usuário não encontrado'       
    endfor

    if &erro.IsEmpty()
        &websession.set('Usuario', &UsuarioId)
        Inicial()

    else
        msg(&erro)
    endif
Endevent


Observe que se o usuário e a senha estiverem corretos a página de ingresso no sistema Inicial(), será chamada, caso contrário a mensagem indicativa será exibida.

Na página Inicial() e em todos os objetos será necessário programar uma avaliação para se saber se existe alguma coisa registrada na variável Usuario na websession.

Event Start
    if &websession.get('Usuario').IsEmpty()
        Login()
    endif

Endevent






Esta é a estrutura mais básica que podemos construir. Falta ainda identificar o perfil, mas creio que daqui pra frente fica simples.

Considerações

Apesar da simplicidade temos muitas implicações relacionadas neste processo, recomendo inclusive que você estude um pouco a respeito da OWASP TOP 10, que trata dos problemas de vulnerabilidade que nossas aplicações poderão enfrentar.

  1. Recomenda-se que dados sensíveis como a própria senha do usuário seja gravada no banco em forma encriptada, praticamente dados pessoais devem sempre ser protegidos. Top A6: Exposição de dados sensíveis.
  2. A comunicação entre o navegador de internet e o servidor tem que ocorrer de forma encriptada por meio de um certificado SSL, e com protocolo HTTPS. Se não for desse jeito, qualquer pessoa poderá interceptar a comunicação e ler o usuário e senha transmitidas. Top A5 – Configuração Incorreta de Segurança, A2 – Quebra de autenticação e Gerenciamento de Sessão.
  3. Se não for avaliado o acesso em cada programa, e muitas vezes a verificação também tem que envolver os dados apresentados, estaremos quebrando mais um protocolo de segurança. Top A7 – Falta de Função para Controle do Nível de Acesso
  4. Normalmente nossas interfaces de login são submetidas a um mecanismo de invasão por força bruta onde um script busca criar as senhas para tentar ingressar. Deve haver um mecanismo de proteção para se evitar que sejam realizadas chamadas indiscriminadas em uma única chave de sessão. Uma estratégia é incluir um controle do tipo Captcha, para identificar um usuário humano. http://marketplace.genexus.com/product.aspx?captcha,en
  5. Os navegadores de internet tem a péssima funcionalidade de lembrar os valores de login. Se o procedimento não for corretamente implementado, poderá haver a possibilidade de um usuário ingressar no login de outro.
  6. Controles de segurança implementados em Javascript poderão ser facilmente quebrados, bastando o usuário desligar o privilégio de execução no seu navegador. Toda operação de segurança deverá ocorrer no servidor e não no cliente.

Poderíamos ficar discorrendo sobre esse assunto por várias páginas, mas acho que já deu pra dar uma noção. Eu particularmente acho que o GAM é a melhor solução, pois não precisaremos programar nada, e teremos mais tempo para desenvolver outras questões importantes relacionadas com segurança, que podem muitas vezes inviabilizar o sistema na web.

Este tema é profundo e requer não apenas atenção para se criar uma bela interface para realizar o login.

Bom trabalho!

sexta-feira, 8 de abril de 2016

Sobre Blob e Images

Carregar imagens diretamente no banco de dados é uma operação bastante interessante e prática utilizando os tipos Image e Blob no Genexus. Porém, quais as diferenças e até onde chegamos com esses dois recursos.

Parece até aquela briguinha manjada dos super heróis, aquela coisa meio armada das lutas de vale tudo dos anos 80. Falo isso porque os dois recursos são ótimos e não competem entre si, podem inclusive encontrar várias áreas em que se conectam, e cada um vai auxiliá-lo a desenvolver excelentes recursos.

Porém, antes de avançarmos, vamos revisar os artigos já apresentados sobre esses dois assuntos, aqui do Genexando.

Manipulando Imagens

  • http://www.genexando.com/2009/12/tratando-imagens.html
    Neste artigo chegamos a conclusão que o tipo Image nos ofereciam recursos que permitiam a carga da imagem, e principalmente sua posterior recuperação por meio do image.Link(), desta forma podemos ligar a imagem a um controle, ou programar o acesso por meio de <img src='...' >
  • http://www.genexando.com/2015_10_01_archive.html
    Neste artigo mais recente, falamos a respeito de resoluções, dimensões de imagens e também passamos rapidamente sobre o armazenamento com Blob e Image.

Manipulando Blob


O que devo escolher: Blob ou Image?

Apesar de diversos artigos ainda não tivemos um que respondesse definitivamente a respeito deste assunto. Ambos os tipos permitem armazenar dados binários no banco de dados. Então porque precisamos de dois tipos distintos? Qual é mais indicado em cada situação?

Image 

É destinado a armazernar e recuperar imagens, aceitando as extensões:  jpe, jpg, jpeg, gif, png, bmp, ico, svg, tif, tiff, ai, drw, pct, psp, xcf, psd, raw. Desta forma arquivos do tipo pdf, doc, xls, não serão aceitos.

É disponibilizado uma série de propriedades que facilitam a operação da imagem.


Image.FromURL()
Permite carregar uma imagem a partir de uma URL, bastando informar o local onde a mesma se encontra publicada, como por exemplo o logotipo do Genexando, que pode ser carregado em uma variável &image.

Event 'fromurl'
    &image.FromURL('http://1.bp.blogspot.com/-m1NiBb4VqsA/UXvcMgc9YpI/AAAAAAAAAsc/kUip66gYUGk/s1600/genexandologo.png')
Endevent


Image.FromImage()
Carrega uma imagem a partir de outra que já se encontra carregada na Kb, na área Customization - Images, como por exemplo a ActionExport, que representa um XLS.

Event 'fromimage'
    &image.FromImage(ActionExport)
Endevent


*** Inverti a ordem e coloquei uma URL em um FromImage e funcionou também, portanto, tanto FromImage como FromUrl aceitam imagens de URLs externas.


Image.ImageURI, Image.ImageName, Image.ImageType
Para quem precisa recuperar a imagem a partir de uma URL ou fisicamente no disco, é necessário compreender como a imagem carregada no BD se transforma novamente em um arquivo no servidor. Em nossa Kb temos uma pasta chamada PUBLICTEMPSTORAGE\MULTIMEDIA que é utilizada para construir os objetos temporários, como as imagens, por exemplo.

Image.ImageURI: devolve o caminho via servidor web até a imagem.
http://localhost/nomekb/PublicTempStorage/multimedia/nomekb_229a89cef51d48579586cdcf30d6ad85.png

Image.ImageName: devolve o nome da imagem, sem a extensão.
nomekb_229a89cef51d48579586cdcf30d6ad85

Image.ImageType: devolve o tipo ou extensão da imagem
png 
   
*** Se for necessário obter o caminho físico até a imagem, poderemos utilizar o caminho conhecido até a pasta PublicTempStorage\multimedia e acrescentar o ImageName e ImageType para completar.

Z:\Models\TESTE\EV3\Extensao\CSharpModel\web\PublicTempStorage\multimedia

*** Os valores ImageName, ImageType e ImageURI somente quando referenciarmos um atributo armazenado em uma tabela, isto devido a necessidade de se reconstruir a imagem em formato de arquivo físico novamente para ser devolvida para o navegador. Se a operação envolver apenas a carga de uma imagem em uma variável, a mesma ficará no somente no navegador, desta forma não teremos as informações até que ocorra a carga da imagem a partir do servidor, ou seja, a mesma for armazenada e posteriormente recuperada.

Blob

Blob é mais flexível em termos de carga pois aceitam qualquer tipo de arquivo, desde que se estabeleça um atributo para armazenar a extensão na propriedade File Type Attribute. Isto quer dizer se definirmos esta propriedade para um atributo do tipo Character(4), teremos a possibilidade de armazenar qualquer coisa, uma vez que para reconstruir o objeto a extensão será variável e recuperada deste atributo.

Assim como ocorre com as imagens os atributos do tipo Blob precisam se transformar novamente em arquivos físicos, para que possam ser enviados aos navegadores, e o mesmo é construido na pasta PUBLICTEMPSTORAGE. Com atributos do tipo Blob as informações não são tão explicitas quanto as definidas no tipo Image.

Blob.ToString() ou link(Blob)
A operação retorna o caminho físico até o arquivo reconstruído para ser devolvido ao navegador. Neste caso o nome do arquivo original não será mais considerado, sendo gerado novos arquivos aleatórios, abaixo um exemplo.

Z:\Models\TESTE\EV3\Extensao\CSharpModel\web\PublicTempStorage\usercontrol5863015.pdf

&URL = pathtourl(Blob)
Esta operação é meio obscura na documentação, mas muito importante pois devolve o link utilizado pelo navegador de internet para enviar o arquivo gerado pelo Genexus. Está documentada aqui http://wiki.genexus.com/commwiki/servlet/wiki?9563,PathToURL+function,. O resultado devolvido é algo semelhante ao apresentado a seguir.

http://localhost/nomekb/PublicTempStorage/usercontrol5863015.pdf



Tamanho do Blob: &File.GetLength()
Outra operação interessante que pode realizada no Blob e igualmente obscura na documentação é a obtenção da dimensão do Blob por meio de um tipo File, a operação GetLength() devolve a dimensão em bytes do arquivo. A documentação encontra-se aqui http://wiki.genexus.com/commwiki/servlet/wiki?6915,File+data+type,

&File.Source  = Blob.ToString()
&size = &File.GetLength()


Resultado:
12311

Tamanho do Blob: &File.GetName()
Utilizando o File também será possível obter o nome do arquivo gerado pelo Genexus. Com este nome também será possível chegar a URL da imagem.

&File.Source  = Blob.ToString()
&filename = &File.GetName()


Resultado:

usercontrol1740234.pdf

Base64 String

Os navegadores de internet conseguem entender um formato chamado Base64 String que representa uma imagem, porém codificada num chamado encode. Neste formato a imagem é gerada como uma string, no seguinte formato, apresentado de forma reduzida.

data:image/png;base64,iVBORw0KAACrQAAAmICA...WgA5ErkJggg==

O modelo foi implementado como uma maneira de reduzir a comunicação entre o servidor e o cliente, e não nos cabe aqui discutir o que é melhor ou pior, porque a imagem pode ser alcançada pela sua URI ou por uma string codificada no formato Base64.

A seguir alguma documentação sobre este tema:

Blob.FromBase64String()
Blob.ToBase64String()
Os Blobs incluem métodos que transformam uma string em Base64 em um Blob (Blob.FromBase64String()) ou um Blob em Base64 (Blob.ToBase64String()). Isso é interessante porque Image não possui este recurso, somente Blob.

Se for necessário uma Image neste formato é possível converter em Blob e utilizar o recurso da conversão Base64String().  Para isso será necessário que a Image e Blob sejam do mesmo tipo, ou seja, compartilhem do mesmo MIME type.

&Blob  = &Image
&string = &Blob.ToBase64String()

Ver documentação em http://wiki.genexus.com/commwiki/servlet/wiki?15204,Image+data+type

Redimensionamento?

Esta é uma boa pergunta, pois não temos como redimensionar a imagem quando realizamos o upload, e este é um fator muito importante visto que os aparelhos atuais geram imagens quase sempre desnecessariamente muito grandes. Imagens grandes muitas vezes não vão atender nossa necessidade na grande maioria dos casos, normalmente o que ser faz é reduzir a imagem a uma dimensão predeterminada.

Para smartdevices temos uma propriedade para escalar a imagem, Scale Type Property


Voltemos outra hora para tratarmos especificamente deste assunto.