Páginas

quarta-feira, 10 de fevereiro de 2016

SharePoint: problema em consulta de termos via JavaScript

O SharePoint tem um serviço de termos que pode ser usado tanto como navegação como classificação de itens (colocando tags em documentos, por exemplo).

Esse serviço de termos, chamado Managed Metadata Service, pode ser acessado através de uma API que pode ser consumida através de JavaScript.

Em uma funcionalidade na qual precisávamos acessar um conjunto de termos específico para popular um controle na tela, começamos a ter o erro abaixo quando a solução foi implantada no ambiente de testes.

Cannot read property 'toString' of null

O erro a princípio é simples de identificar, existe uma chamada do método toString em uma string que não recebeu nenhum valor. O problema é que esse erro dá dentro da biblioteca JavaScript do próprio SharePoint.

O código utilizado nessa chamada é o abaixo.

var termSetName = "NOME_TERMO";
var locale = 1046; //pt-BR
var clientContext = SP.ClientContext.get_current();
var taxonomySession = SP.Taxonomy.TaxonomySession.getTaxonomySession(clientContext);
var termStore = taxonomySession.getDefaultSiteCollectionTermStore();
var termSets = termStore.getTermSetsByName(termSetName, locale);
var termSet = termSets.getByName(termSetName);
var terms = termSet.getAllTerms();
clientContext.load(taxonomySession);
clientContext.load(termStore);
clientContext.load(termSet);
clientContext.load(terms);
clientContext.executeQueryAsync(function onSuccess() { 
    var enumerator = terms.getEnumerator(); 
    while (enumerator.moveNext()) { 
        var spTerm = enumerator.get_current(); 
        var name = spTerm.get_name(); 
        var id = spTerm.get_id(); 
        // ... 
    }
}, function onFailure(args) { 
    console.log(args.get_message()); 
});

Depois de analisar e comparar os ambientes, chegamos na raiz do problema: a obtenção do term store. Veja que no código estamos dizendo, na linha 5, para recuperar o term store padrão do site collection. Há uma outra forma de obter o term store, através do seu nome, mas havíamos decidido usar o padrão pois foi um código de exemplo que encontramos e funcionou!

A diferença dos ambientes foi uma configuração no serviço de termos:


Após selecionar a opção "This service is the default storage location for column specific term sets" nas propriedades do serviço, a solução começou a funcionar no ambiente de testes! Dessa maneira, há um term storage a ser usado no momento da chamada do método getDefaultSiteCollectionTermStore, cuja ausência aparentemente era a causa do problema.

Isso depois foi formalizado com o cliente, para que no ambiente de produção não haja esse tipo de erro.

[]'s

quinta-feira, 21 de janeiro de 2016

Criando modelos de HTML com Underscore.js

Em aplicações web, os dados que são retornados de serviços, como um REST retornando um objeto JSON, precisam ser exibidos aos usuários. Ou seja, pegamos essas informações e as transformamos em elementos HTML (parágrafos, tabelas, listas, etc). Uma das maneiras de se fazer isso é a manipulação desses dados através de scripts de forma a montar cada elemento. Por exemplo, o código abaixo pega um objeto JSON e o transforma em um parágrafo, através da concatenação do valor com as tags "<p>" e "</p>". Isso a princípio parece ser algo simples, mas no final acabamos com um código de difícil manutenção. Imagine um JSON que tenha um array de objetos para ser montado em uma tabela, com cada linha precisando gerar todas as respectivas tags " <tr>", "<td>" e etc. Pior, imagina esquecer de fechar uma tag ou concatenar sem fechar uma aspas. A próxima pessoa que for dar manutenção vai gastar um bom tempo entendendo o que foi feito (e qual é o resultado esperado).

var dado = { valor: 'teste' };
var valor = "<p>" + dado.valor + "<p>";
$("#lista").append(valor);

Obs. No exemplo, usamos jQuery para colocar o conteúdo HTML formatado dentro de um outro elemento.

O que precisamos é de uma forma fácil de poder visualizar a estrutura HTML desejada e inserir nos pontos específicos as informações. Para isso, uma boa maneira é trabalhar com ferramentas de templates (modelos). O Angular.js (https://angularjs.org/) e o Knockout (http://knockoutjs.com/) fazem isso muito bem, mas na falta da possibilidade do seu uso, temos que pensar em alternativas.

O Underscore (http://underscorejs.org/) é uma biblioteca JavaScript que existe há um bom tempo, e funciona como um "cinto de utilidades" para o desenvolvedor. Há várias funcionalidades nela, e uma telas é o template. Em linhas gerais, funciona assim:

  1. O desenvolvedor cria um modelo HTML (um trecho HTML que será repetido várias vezes);
  2. Dentro desse modelo, ele define os pontos que serão substituídos com valores;
  3. Via código, esse modelo HTML é obtido e "compilado" pelo Underscore;
  4. Esse objeto "compilado" recebe um objeto JSON, e internamente faz a substituição de valores, gerando como resultado um trecho HTML completo.

Vejamos o exemplo de HTML abaixo. Note que existe uma tag script cujo tipo é "text/template". Dentro desta tag está o nosso modelo, o trecho HTML que será repetido a partir de um objeto JSON (que no nosso exemplo, contém uma lista de estados e cidades brasileiras).

Figura mostrando uma página HTML com caixas com o nome dos estados e algumas cidades


Esse modelo é composto por uma div de classe item contendo um parágrafo onde será colocado o nome do estado e uma lista onde serão colocadas as cidades. Cada conjunto de objetos HTML será repetido tantas vezes quanto forem os dados dentro o objeto JSON.

<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Exemplo de uso de templates do Underscore.js</title>
        <link rel="stylesheet" type="text/css" href="estilo.css">
    </head>
    <body>
        <script type="text/javascript" src="bower_components/jquery/dist/jquery.min.js"></script>
        <script type="text/javascript" src="bower_components/underscore/underscore-min.js"></script>
        <script type="text/template" id="modelo">
            {% _.each(estados, function(estado) { %}
                <div class="item">
                    <p>{{ estado.nome }}</p>
                    <div>
                        <ul>
                            {% _.each(estado.cidades, function(cidade) { %}
                                <li>{{ cidade }}</li>
                            {% }); %}
                        </ul>
                        {% if(estado.cidades.length === 0) { %}
                            <div>N/D</div>
                        {% } %}
                    </div>
                </div>
            {% }); %}
        </script>
        <h2>Lista de Estados e suas cidades</h2>
        <div id="lista">
            <!-- conteúdo vai aqui -->
        </div>
        <script type="text/javascript" src="exemplo.js"></script>
    </body>
</html>

Veja que temos alguns caracteres especiais que definem o comportamento do modelo. Logo na primeira linha temos dentro das tags "{%" e "%}" um código JavaScript que efetua um loop dentro do array estados. Note também que no final do script há o fechamento deste bloco de código JavaScript. Quando a esse modelo for aplicado o objeto JSON, esse loop irá executar para permitir que os dados sejam criadas as divs que representam cada um dos estados brasileiros.

Dentro da div, temos na tag "<p>" os caracteres "{{" e "}}". Eles definem o conteúdo que seja colocado dentro deste elemento HTML. No nosso exemplo, o parágrafo será preenchido com o nome do estado.

Há também uma lista (UL) que irá conter as cidades de cada estado. Novamente temos um loop que irá percorrer um array, que neste caso é o array de cidades. E por fim, caso não haja nenhuma cidade na lista, temos uma instrução if em JavaScript, devidamente inserida dentro dos caracteres especiais, para exibir a mensagem N/D (não disponível) caso o tamanho do array de cidades seja zero.

Vamos ver agora o código JavaScript por trás dessa página (que está referenciado no final do HTML, no arquivo exemplo.js.

var dados = {
    estados: [
        {
            nome: 'São Paulo',
            cidades: [
                'São Paulo', 
                'Santo André',
                'Guarulhos'
            ]
        },
        {
            nome: 'Minas Gerais',
            cidades: []
        },
        {
            nome: 'Rio de Janeiro',
            cidades: [
                'Niterói', 
                'Angra dos Reis'
            ]
        }        
]    
};

(function(){
    
    _.templateSettings = {interpolate : /\{\{(.+?)\}\}/g,      // print value: {{ value_name }}
                        evaluate    : /\{%([\s\S]+?)%\}/g,   // excute code: {% code_to_execute %}
                        escape      : /\{%-([\s\S]+?)%\}/g}; // excape HTML: {%- <script> %} prints <script>  
    
    var modelo = _.template($("#modelo").html());
    var conteudo = modelo(dados);
    $("#lista").append(conteudo);
})();

A primeira coisa que foi feita e a declaração do objeto JSON de estados e cidades. Talvez agora fique mais simples de entender a estrutura do modelo, visualizando a estrutura do JSON. É um objeto que possui um array chamado "estados", e para cada elemento temos uma propriedade de nome e uma propriedade que é outro array de cidades.

Em seguida temos um trecho de código JavaScript. A primeira coisa que fazemos aqui é definir os caracteres especiais que o Underscore irá entender para fazer a aplicação de dados no modelo. Eu não disse, mas existe um padrão já do Underscore que usa "<%=" e "%>", por exemplo, como caracteres especiais de escape. Mas eu não gosto dele assim pois confunde se formos trabalhar com uma página dentro do ASP.NET (Webforms, zuado...). Além disso, usar chaves dá uma cara mais parecida com a forma de trabalhar do Angular.js :-).

Bom, depois de definido, temos o código que faz a geração do HTML. Pegamos o conteúdo do modelo através do jQuery (o nome do elemento script é "modelo"), aplicamos a função template do Underscore para obter o gerador de HTML, e por fim passamos o objeto JSON para a criação do HTML final. Esse HTML final então é colocado na página, através novamente do método append do jQuery. Como resultado final, temos o HTML montado!

Um detalhe. Veja que tanto o método each, usando nos loops do modelo, quanto o ajuste de configurações do Underscore, com a propriedade templateSettings, é de um objeto chamado "_". Exatamente o caractere de underscore (sublinhado). Nada é por acaso...

E por fim, para fechar o exemplo, aqui está o CSS que define as propriedades visuais da página de exemplo.

body{
    font-family: Verdana, Geneva, Tahoma, sans-serif;
}

.item {
    width: 10em;
    background-color: lightyellow;
    border-style: solid;
    border-width: 0.1em;
    border-color: sandybrown;
    float: left;
    margin: 0.5em;
    padding: 0.5em;
}

.item > p {
    text-align: center;
    background-color: moccasin;
}

.item > div {
    background-color: moccasin;
    margin: 0.1em;
}

.item > div > div {
    width: 100%;
    text-align: center;
    font-weight: bold;
}

O template é só uma das inúmeras funcionalidades que o Underscore provê. Ele ainda dá suporte para métodos de pesquisa em arrays (find, filter), muito úteis para a manipulação de objetos JSON (algo parecido com o LINQ do .NET).

Até a próxima.

quarta-feira, 9 de dezembro de 2015

Dez coisas que todo desenvolvedor deve saber

Olá, galera!

Hoje tive o prazer de participar de um hangout com meus antigos colegas de trabalho Leandro Magnani, Luiz Pais, Alexandre Costa (Magoo), Gabriel Vieira e Vinícius Tosta sobre coisas que todos os desenvolvedores devem saber.

Confira no vídeo abaixo.

[]'s


segunda-feira, 30 de novembro de 2015

Usando PowerShell para obter informações do AD

Hoje precisei acessar algumas informações do AD, e decidi usar o PowerShell para isso.

Eu precisava encontrar alguns usuários na rede, e vi que o comando Get-ADUser (https://technet.microsoft.com/en-us/library/ee617241.aspx) era exatamente o que eu precisava. Assim, abri um console do PowerShell, digitei o comando e erro!


O comando Get-ADUser não foi reconhecido. Bom, neste caso eu precisava fazer referência ao pacote onde ele faz parte, mas vi que antes disso eu precisava baixá-lo. Pesquisando na internet vi que se tratava do Remote Server Administration Tools for Windows 7 with Service Pack 1 (SP1). O link para download é este: https://www.microsoft.com/en-us/download/details.aspx?id=7887

Depois de baixar o pacote e instalar, é necessário ativá-lo como feature do Windows. Para isso, basta escolher a opção "Módulo do Active Directory para Windows PowerShell" (ou "Active Directory Module For Windows PowerShell").


Feito isso, basta importar o módulo activedirectory através do comand Import-Module activedirectory, e a partir daí usar os comandos. Abaixo está uma imagem de exemplo.


Obs. Sim, infelizmente o Windows está em português nesta máquina. Nem tudo é perfeito na vida.

[]'s

segunda-feira, 26 de outubro de 2015

Pérola arquitetônica

Aqui está um código que é uma verdadeira obra arquitetônica!


A montagem eu mesmo que fiz :-)
Bom, mas quais são os problemas com esse código?

Este código está aí para permitir que campos possuam máscara de dados. Até aí, é um requisito normal de qualquer sistema. O problema é a forma como está sendo feito. Eu verifiquei e existem várias máscaras que não estão sendo utilizadas. Um primeiro problema aqui é desempenho, pois esse script executa em toda página que é carregada, acessando elementos HTML pelo jQuery. É um código desnecessário e sem justificativa.

Agora conceitualmente, essas inúmeras máscaras, qual o significado delas dentro do sistema? Qual tipo de campo é formatado por 19 números sequenciais? Não há motivo para esse código existir. Seria necessário a associação com alguma regra do sistema para que elas fizessem sentido. Máscaras que têm sentido são, por exemplo, CPF (999.999.999-99) ou CEP (99999-999).

[]'s