Formatando textos com o plugin has_markup no ActiveRecord
Leia em 3 minutos
Às vezes é preciso deixar que um usuário entre com textos formatados —seja HTML puro, Markdown ou Textile. O grande problema está na hora de filtrar o que está sendo enviado; não queremos que um markup inválido ou com tags que você não quer sejam salvas no banco de dados.
Infelizmente, o Ruby on Rails não possui nenhuma forma nativa de fazer isso (na verdade, acho meio burra a decisão de manter os métodos de sanitização apenas no escopo de views).
Pensando nesse problema, criei um plugin chamado has_markup, que permite especificar quais tags e atributos são permitidos, eliminando todo o resto. Ele também normaliza o HTML enviado, convertendo-o para XHTML caso ele esteja mal-formatado. Você pode definir se o texto será formatado em HTML puro, Markdown ou Textile.
Bem simples de usar e faz o trabalho muito bem!
Como instalar
Primeiro, vamos instalar o plugin.
script/plugin install git://github.com/fnando/has_markup.git
O has_markup possui algumas dependências se você quiser usar Tidy, Markdown ou Textile. Veja abaixo como você pode instalar estas dependências.
Markdown
Parar ter suporte a Markdown, é necessário instalar a gem RDiscount.
sudo gem install rdiscount
Textile
Se você prefere Textile —eu prefiro!—, instale a gem RedCloth.
sudo gem install RedCloth
Tidy
Se você quer ter certeza de que não irá quebrar o seu próprio HTML, torne-o válido com o Tidy. Primeiro é preciso instalar a biblioteca. Acesse e faça o download do código-fonte. Existem alguns binários disponíveis, então dê uma olhada para descobrir qual é o mais adequado para você. Para compilar, execute os comandos abaixo.
tar xvf tidy4aug00.tgz
cd tidy4aug00
make
sudo make install
O Tidy deu alguns warnings após a compilação, mas ele foi instalado. No Mac OS X ele é instalado em /usr/lib/libtidy.dylib
.
cp -f tidy /usr/local/bin
cp -f man_page.txt /usr/local/man/man1/tidy.1
cd /usr/local/bin; \
chmod 755 tidy; \
chgrp bin tidy; \
chown bin tidy;
chown: bin: Invalid argument
make: *** [install] Error 1
Agora instale a gem com sudo gem install tidy
.
Este plugin tentará encontrar o Tidy em seu sistema, procurando alguns diretórios onde ele normalmente estaria. Se ele não puder ser encontrado, especifique o caminho para o diretório da biblioteca em seu arquivo de ambiente na constante TIDY_PATH
.
# config/environments/development.rb
TIDY_PATH = '/custom/path/to/tidy'
Usando o has_markup
O has_markup é um plugin para ActiveRecord, que disponibiliza algumas ferramentas que podem ser usadas em qualquer lugar. No seu modelo, basta adicionar algo como isto:
class Post < ActiveRecord::Base
has_markup :content,
:format => :markdown,
:tidy => true,
:tags => %w(p a em strong ul li),
:attributes => %w(href)
end
No exemplo acima, o atributo content
será formatado com Markdown. Apenas algumas poucas tags estão disponíveis e apenas o atributo href
pode ser utilizado. O markup também irá passar pelo Tidy, garantindo que é um XHTML válido.
NOTA: o has_markup mantém uma versão final do markup em um atributo formatted_<attribute>
. Dessa forma, você não terá problemas de performance por ter que converter o texto toda vez que quiser exibí-lo. O modelo acima poderia ter uma estrutura como esta:
create_table :posts do |t|
t.string :title
t.text :content, :formatted_content
end
Se você não quer limitar o conteúdo salvo, basta que você não especifique as tags e atributos aceitos.
class Post < ActiveRecord::Base
has_markup :content,
:format => :textile,
:tidy => true
end
É possível usar o has_markup apenas para validar um HTML, mesmo sem estar formatado como Markdown ou Textile; basta especificar o formato :html
.
class Post < ActiveRecord::Base
has_markup :content,
:format => :html,
:tidy => true
end
Para pegar o HTML equivalente a um texto Markdown ou Textile diretamente, você pode instanciar um objeto da classe Markup
markup = Markup.new(:markdown, 'some text')
markup = Markup.new(:textile, 'some text')
puts markup.to_html
Para sanitizar códigos HTML, utilize a classe Sanitize
.
# will sanitize and normalize HTML
Sanitize.html('<script>alert(document.cookie)</script>')
# will sanitize and allow only the specified tags
Sanitize.html('<script>alert(document.cookie)</script>',
:tags => %w(p a em strong img ul li ol)
)
# will sanitize and allow only the specified attributes
Sanitize.html('<script>alert(document.cookie)</script>',
:attributes => %w(href title alt)
)
# will sanitize and normalize HTML using Tidy
Sanitize.html('<script>alert(document.cookie)</script>',
:tidy => true
)
Se quiser normalizar um HTML, utilize o método tidy
.
Sanitize.tidy('some text', options)
Feedback
É isso! Se você tem alguma sugestão, dúvida ou crítica, envie um comentário. O projeto está hospedado no Github, então sinta-se livre para fazer um fork a qualquer momento.