sábado, 4 de dezembro de 2010

Hacks para o Blogger

Se você também utiliza o Blogger para os seus posts, aqui vão algumas dicas que eu descobri recentemente googlando por aí.

Remover barra de navegação


Neste tópico da ajuda, você vai encontrar informações a respeito de como ativar a barra de navegação, e vai notar que a desativação é feita publicando seu blog por FTP. Eu nem sabia que poderia publicar via FTP, e não quero isso. Uma forma mais simples de escondê-la é alterar a folha de estilos padrão para deixá-la invisível. Acesse o painel de configurações, e em seguida:
  • Clique na aba Design, e depois em "Designer do Modelo"
  • Em seguida, vá em "Avançado", e clique na seção "Adicionar CSS"
  • Insira a seguinte regra CSS ao modelo escolhido:
body .navbar {
    display: none !important;
}

Pronto, agora a barra não estará visível. Note que ela continua lá, na verdade, mas ninguém deve vê-la.

Adicionar coloração de sintaxe


Se você vai postar trechos de código, provavelmente vai preferir que eles estejam com a sintaxe em destaque, pois isso ajuda (e muito) na leitura. Existem algumas formas de se fazer isso, e a que eu achei mais simples é a de utilizar o prettyprint. Ele é bem mais simples, e detecta automaticamente a linguagem utilizada para inserir a coloração.

Você precisa baixá-lo e hospedá-lo em algum lugar. Se você tem uma conta do Google Storage for Developers ou em algum dos serviços como o Dropbox ou simliares, pode publicar o arquivo na Web.

Uma forma de inserí-lo em seu Blog é editar o HTML do seu modelo. Os novos modelos são bem interessantes, e construídos em formato XML. Entretanto, ao trocar de modelo ou realizar alguma configuração mais avançada, você pode acabar perdendo este código e ter de refazê-lo.

Outra alternativa é inserir um gadget no Blog que permita inserir código JavaScript e que não é perdido ao trocar/alterar seu modelo. Para isso, em seu painel de controle:

  • Clique na aba "Design", e em seguida na seção "Elementos da página".
  • Na área do rodapé (para evitar que o carregamento dos posts fique muito lento), adicione um gadget do tipo HTML/JavaScript com o seguinte conteúdo:
<script type="text/javascript" 
    src="http://<local onde hospedou o script>/prettify/prettify.js"></script>
<script type="text/javascript" language="javascript">
(function() {
var css = document.createElement('link');
css.rel = 'stylesheet'; css.type = 'text/css';
css.href = 'http://<local onde hospedou o script>/prettify/prettify.css';
var s = document.getElementsByTagName('script')[0]; 
s.parentNode.insertBefore(css, s);
})();
prettyPrint();
</script>

Substitua os campos marcados com <local onde hospedou o script> apropriadamente para a sua situação.

Pronto, agora basta inserir em seus posts os trechos de código envolvidos por uma tag pre ou code, com o elemento class="prettyprint". Por exemplo:

<pre class="prettyprint">
<b>Sintaxe HTML</b>
</pre>

Alterar o favicon


Semelhante ao processo acima, você pode alterar o favicon do seu Blog utilizando um gadget de Script, agora com o seguinte conteúdo:

<script>
var icon = document.createElement('link');
icon.rel = 'shortcut icon'; icon.type = 'image/vnd.microsoft.icon';
icon.href = 'http://<local onde voce hospedou>/favicon.ico';
var s = document.getElementsByTagName('script')[0]; 
s.parentNode.insertBefore(icon, s);
</script>

Ao acessar o seu blog, ele terá um ícone customizado. Utilizei esta abordagem porque estava com uma certa preguiça em procurar as opções de configuração, mas eu acredito que isso seja possível de ser realizado de forma diferente.

quinta-feira, 2 de dezembro de 2010

Implementando o pattern Composite View com Tags JSP

Update 1: Atualizei o nome deste post, uma vez que "Templates estilo Django para páginas JSP" não refletia muito o conteúdo. O código também está disponível no repositório de códigos-fonte http://code.ronoaldo.net/composite-view/

Uma das ferramentas do Django que eu admiro é a sua engine de templates, pois com ele produzimos layouts uniformes para toda a sua aplicação web de maneira estruturada.

O problema


Em Django, você define um template base, que geralmente contém o cabeçalho de suas páginas, e define blocos onde o conteúdo será inserido. Nas páginas de conteúdo, você utiliza uma tag para indicar que você está estendendo o template base, e preenche apenas os blocos de conteúdo a serem alterados.

Isso funciona como uma herança de templates, onde o template filho apenas sobrescreve os blocos de conteúdo do template pai, ao mesmo tempo que permite ter um blocos de conteúdo padrão em um só arquivo. E mais, você pode criar uma hierarquia de quantos níveis forem necessários (e razoáveis!), proporcionando reuso inclusive de sua marcação HTML.

O problema aparece quando você tenta realizar algo semelhante em outras tecnologias com as quais acaba trabalhando. Estamos portando uma aplicação Django para o AppEngine, e por restrições diversas, a tecnologia escolhida foi Java.


Optamos por causar o mínimo de overhead, e escolhemos usar apenas as tecnologias estritamente necessárias. Além do mais, temos poucas interfaces distintas, uma meia dúzia de jsps já resolveria o problema, e criamos uma interface administrativa utilizando o Google Web Toolkit, que nos permitiu criar uma interface rica e reutilizar  diversos códigos de validação server-side em uma versão client-side.

Por questões de simplicidade, geralmente criamos blocos reutilizáveis em JSP da seguinte maneira. No arquivo cabecalho.jsp:
<h1>Cabecalho</h1>

No arquivo rodape.jsp:
<i>Todos os direitos reservados</i>

No arquivo index.jsp:
<%@ include file="cabecalho.jsp" %>
Conteúdo
<%@ include file="rodape.jsp" %>

Obviamente, isso chega a ofender. Pesquisando um pouco, encontrei algumas soluções, quase todas baseadas no pattern Composite View. Basta fazer uma busca e você vai encontrar implementações, mas a maioria delas dá muito trabalho e não é tão intuitiva quanto a solução Django.

Resolvi desenvolver algo um pouco mais simples, a partir da seguinte idéia: criar uma custom tag que define um bloco, e criar uma outra tag que permite extender um template (outra página JSP). A tag que define um bloco, o registra no contexto da requisição e salva um buffer com o conteúdo do mesmo, e em seguida, passa o controle da página para o template pai.

A idéia seria utilizar nossas tags de template da seguinte forma:

No arquivo base.jsp, temos a definição dos blocos:
<%@ page language="java" pageEncoding="utf-8" %> 
<%@ taglib prefix="t" uri="/templates.tld" %>
<html>
<head>
<title>
<t:block name="title">Título padrão</t:block>
</title>
</head>
</body>
<h1> 
<t:block name="header">Cabeçalho Padrão</t:block>
</h1>
<div id="left-box">  
<t:block name="left">Navegação</t:block>
</div>
<div id="main-box">
<t:block name="main">Bloco de conteúdo</t:block> 
</div>
</body> 
</html>

E na página index.jsp, teríamos apenas:
<%@page language="java" pageEncoding="utf-8" %>
<%@taglib prefix="t" uri="/templates.tld" %>
<t:extends template="base.jsp">
<t:block name="main">
 Conteúdo da Index 
</t:block>
</t:extends>  

Implementação


Primeiro, vamos à definição da tag block:

package net.ronoaldo.tools.templateutils.tags;

import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
 * Tag simples que define um bloco de template.
 * 
 * @author Ronoaldo Pereira &lt;ronoaldo@ronoaldo.net&gt;
 */
public class Block extends SimpleTagSupport {

 /**
  * Chave para recuperar o buffer, no escopo da requisição.
  */
 private static final String BLOCK_REGISTRY_KEY = Block.class.getName()
   + "-BLOCK_REGISTRY_KEY";

 /**
  * {@link Logger} para depuração.
  */
 protected Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

 /**
  * Nome do bloco.
  */
 private String blockName;

 /**
  * Setter para o atributo name, que identifica unicamente o
  * bloco.
  * 
  * @param name
  *            o nome do bloco.
  */
 public void setName(String name) {
  this.blockName = name;
 }

 /**
  * Realiza a implementação da Tag propriamente dita.
  */
 @Override
 public void doTag() throws JspException, IOException {
  if (withinExtendsBlock()) {
   updateBlockContent();
  } else {
   renderBlock();
  }
 }

 /**
  * Atualiza o conteúdo em cache do bloco, caso ele ainda não tenha sido
  * definido.
  * 
  * @throws JspException
  * @throws IOException
  */
 private void updateBlockContent() throws JspException, IOException {
  // Renderiza o bloco caso ele ainda não exista
  if (getRegistry().get(blockName) != null)
   return;

  StringWriter sw = new StringWriter();
  JspFragment body = getJspBody();
  if (body != null) {
   body.invoke(sw);
  }
  getRegistry().put(blockName, sw.toString());
  logger.info(String.format("Content for block %s updated to %s",
    blockName, sw.toString()));
 }

 /**
  * Renderiza o bloco na página JSP.
  * 
  * @throws JspException
  * @throws IOException
  */
 private void renderBlock() throws JspException, IOException {
  // Insere o bloco na página
  updateBlockContent();
  JspWriter out = getJspContext().getOut();
  out.print(getRegistry().get(blockName));
 }

 /**
  * Identifica se esta tag {@link Block} está dentro de uma tag
  * {@link Extends}.
  * 
  * @return
  */
 private boolean withinExtendsBlock() {
  return (getParent() instanceof Extends);
 }

 /**
  * Recupera ou cria um registro no escopo da requisição, para armazenar os
  * buffers dos blocos da página a ser exibida.
  * 
  * @return um {@link Map} contendo os valores associados ao nome do bloco.
  */
 private Map<String, String> getRegistry() {
  JspContext ctx = getJspContext();

  @SuppressWarnings("unchecked")
  Map<String, String> registry = (Map<String, String>) ctx.getAttribute(
    Block.BLOCK_REGISTRY_KEY, PageContext.REQUEST_SCOPE);

  if (registry == null) {
   registry = new HashMap<String, String>();
   ctx.setAttribute(Block.BLOCK_REGISTRY_KEY, registry,
     PageContext.REQUEST_SCOPE);
  }

  return registry;
 }
}

Esta tag é bem simples. Neste caso, estamos utilizando uma implementação baseada em um Map<String, String>, para armazenar apenas um valor para o bloco durante o processamento de todas as tags das páginas envolvidas.

Se utilizarmos apenas esta Tag, já conseguimos criar um efeito bem interessante. Basta definir os blocos antes de qualquer outra coisa, e finalizar a página com a diretiva <%@include %>. Isso já nos dá o resultado esperado, exceto para aninhar páginas.

Para uma implementação mais completa, vamos definir a tag Extends:

package net.ronoaldo.tools.templateutils.tags;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
 * Tag simples que realiza um include após realizar o processamento de
 * seu conteúdo.
 * 
 * @author Ronoaldo Pereira &lt;ronoaldo@ronoaldo.net&gt;
 */
public class Extends extends SimpleTagSupport {

 /**
  * Nome do template a ser utilizado para inclusão.
  */
 private String template;

 /**
  * Setter para que o atributo template funcione.
  * 
  * @param template
  *            o nome do template a ser extendido.
  */
 public void setTemplate(String template) {
  this.template = template;
 }

 /**
  * Implementação da Tag.
  */
 @Override
 public void doTag() throws JspException, IOException {
  // Processa o body (definição de blocos)
  getJspBody().invoke(null);

  // Realiza o include do template
  try {
   PageContext pageContext = (PageContext) getJspContext();
   pageContext.include(template);
  } catch (ServletException e) {
   throw new JspException(e);
  }
 }

}

Para finalizar com chave de ouro, basta agora realizar a implementação de um arquivo tld:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE taglib PUBLIC
 "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
 "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
 <tlib-version>1.2</tlib-version>
 <jsp-version>2.0</jsp-version>
 <short-name>template-utils</short-name>
 <description>Several utilities for templating with JSP and JSTL</description>

 <tag>
  <name>extends</name>
  <tag-class>net.ronoaldo.tools.templateutils.tags.Extends</tag-class>
  <body-content>scriptless</body-content>
  <attribute>
   <name>template</name>
   <required>true</required>
   <type>java.lang.String</type>
   <description>Indicates the parent template to extends from.</description>
  </attribute>
 </tag>

 <tag>
  <name>block</name>
  <tag-class>net.ronoaldo.tools.templateutils.tags.Block</tag-class>
  <body-content>scriptless</body-content>
  <attribute>
   <name>name</name>
   <required>true</required>
   <type>java.lang.String</type>
   <description>The block name, unique across all template and its extensions</description>
  </attribute>
 </tag>

</taglib>

Você pode até criar um pequeno jar com estas classes e este arquivo taglib.tld dentro de META-INF, e ele pode ser incluído no seu diretório WEB-INF/lib como biblioteca reutilizável.