sábado, 9 de fevereiro de 2008

Escrevendo um plugin de uploads de Imagem para o TinyMCE usando Rails e attachment_fu (Tradução)

Escrevendo um plugin de uploads de Imagem para o TinyMCE usando Rails e attachment_fu - Uma tradução do post original de Scott Roth um dos mantenedores do blog Rails Loft.

-----

TinyMCE é um belo editor de texto rico (Rich Text Editor - RTE, conhecidos também como editores wysiwyg) baseado em broswer. Nós estamos usando isso com sucesso em uma das nossas aplicações para prover um caminho amigável para pessoas não técnicas terem algum controle sobre o estilo de conteúdo que eles estão criando. Todavia, uma clara deficiência está incluída nos seus plugins, ele não possibilitam o upload uma imagem (ou qualquer arquivo) para ser usado com o RTE. Isto faz sentido quando pensamos sobre o assunto pois o upload de arquivos requer um componente do lado servidor - às vezes o lado cliente do TinyMCE precisa ter certeza sobre isso. Então, sendo um cidadão Web preguiçoso, eu tentei usar o google para escrever o plugin que eu precisava. Procurando, vi várias opões para backend PHP. Vi que não temos ainda um feito para o Ruby on Rails. Então, eu mesmo fiz um e vou rabiscar o processo aqui.

Verificando como trabalha um plugin básico Eu iniciei minha questão na página wiki dedicada a desenvolvedores do TinyMCE. Eu fiz o download da versão DEV do TinyMCE como instruído, ele inclui um exemplo de plugin chamado "_template". Para sua informação, nós estamos usando a versão 2.x do TinyMCE. De acordo com o Wiki, duas API foram alteradas na arquitetura, esse processo será um pouco diferente se você está na versão 3.x. Renomeie o plugin "_template" e copie para dentro da pasta "plugins" da sua instalação do TinyMCE. Você verá 2 arquivos nesse instante: "editor_plugin_src.js" e "editor_plugin.js". Por questões de desenvolvimento copie o arquivo "_src" para dentro do outro, na verdade é este arquivo que é lido. No final nós copiaremos novamente o arquivo "_src" e faremos a compressão do arquivos (http://javascriptcompressor.com/) "editor_plugin.js" no qual ele é o único que realmente é usado em runtime. Abra o arquivo "editor_plugins.js" e substitua todas as referências que contenha "template" com o nome do seu plugin.

Agora você precisa abrir sempre que você tiver seu método TinyMCE.init() e adicionar seu plugin nele. Especificamente, adicione o nome do plugin na linha de registro de nomes de "plugins" e na linha do seu botão de tema ("Theme_advanced_buttons1" no meu caso), então esse botão será mostrado. Você também precisa de uma imagem para ser clicada por seus usuários. Crie uma (ou lá no exemplo de plugin tem um template.gif tem um que você pode usar por enquanto) e coloque na pasta de imagens (themes/advanced/images/[pluginname].gif no meu caso).

Faça um refresh forçado na instância do TinyMCE e, voilá, você poderá ver um botão do plugin totalmente sem funcionalidade. Algum javascript alertará provavelmente no popup como um plugin exemplo é um alerta apropriado indicando que está pronto para uso.

Quando você clica no botão, você será levado a uma página popup onde o usuário poderá especificar o arquivo para upload. Vamos arrumá-lo. No arquivo editor_plugin.js" tem uma função chamada "getControlHTML" - altere o "mceTemplate" para qualquer que seja o nome de sua função - Eu escolhi "doUpload". Encontre o mesmo nome na função "execComand". Na função "execComand" tenha certeza que o comando "template['file']" aponta para um arquivo html real. Ok, agora faça um refresh no TinyMCE e clique no botão - você terá um exemplo de popup.


O front-end

Ok, agora toda nossa infraestrutura está estabelecida. Nós mostraremos que somos capaz de ligar qualquer coisa agora. O plugin que eu escrevi está atualmente um pouco mais complexo que o código mostrado aqui, pois eu criei algumas funcionalidades avançadas como uma opção de selecionar um arquivo de uma lista de aruqivos que foram previamente uplodeados para dentro do sistema. Por conta do tempo e espaço eu mostrarei um código simples aqui.

Nós precisamos pegar o exemplo do popup.htm e modificar para suportar upload de arquivos. Alguma coisa como isso:



1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <head>
3 <title>Image uploader</title>
4 <script language="javascript" type="text/javascript"
src="../../tiny_mce_popup.js"></script>
5 <script language="javascript" type="text/javascript"
src="../../utils/mctabs.js"></script>
6 <script language="javascript" type="text/javascript"
src="../../utils/form_utils.js"></script>
7 <script language="javascript" type="text/javascript"
src="../../utils/validate.js"></script>
8 </head>
9 <body>
10 <form action="/binaries/upload"
enctype="multipart/form-data" id="binary-edit-form"
method="post">
11 <p><label for="binary">Upload a file:</label><br/>
12 <input id="binary_uploaded_data"
name="binary[uploaded_data]" size="30"
type="file" /></p>
13
14 <p><label for="binary_title">Give it a
title</label><br/>
15 <input id="binary_title" name="binary[title]"
size="30" type="text" value="" /></p>
16
17 <p><label for="binary_contents">Add a
description</label><br/>
18 <textarea cols="40" id="binary_contents"
name="binary[contents]" rows="5"></textarea></p>
19
20 <input type="submit" value="Upload!" />
21 </form>
22 </body>
23 </html>


Tudo que nós fizemos aqui foi criar um front-end com um form que será colocado em sua aplicação rails (/binaries/upload) e com o campo ID o seu "attachment_fu" ativado, modelos e classes serão reconhecidos. Os javascript incluidos no topo são algumas mágicas do TinyMCE.

Eles não precisarão de todos para esse simples exemplo, mas eles provavelmente serão desejados quando você precisar de uma funcionalidade extra. Por exemplo, se você precisar de tabs na sua interface de plugin ou precisar de alguma validação.


O back-end

Para meu exemplo, nós assumiremos que você tem um modelo e um controller Binary que já estão mixados com o "attachment_fu". Se você não usou o attachment_fu antes, Mike Clark postou um grande tutorial sobre como usar. Aqui está o método upload no controller (isto é de quem está postando).



1 def upload
2 binary = Binary.create(params[:binary])
3 @insertString = "<img src=\"#{binary.public_filename}\" />"
4 render :layout => false
5 end


O quê? um método magro? Graças à mágica do attachment_fu, nós podemos uplodear e salvar um arquivo binário com apenas uma linha. Na segunda linha, nós criamos uma string html que será inserida dentro da página cliente DOM do TinyMCE. Nós então podemos renderizar para ver. Eu estou mantendo isto bem simples para o exemplo, mas você pode ver algumas mudanças imediatas no controller. Por exemplo, você pode querer checar algum erro para ter certeza que o binário foi uplodeado e salvo corretamente. Por exemplo, se um PDF for uplodeado, você poderá querer ver o PDF na sua aplicação como um ícone link para o PDF, no qual vai requerer uma codificação parecida com essa:



1 @insertString = "<a href=\"#{binary.public_filename}\"><img
src=\"/images/pdfIcon.gif\" /></a>"


Agora nós precisamos retornar algum javascript para o browser para inserir nossa imagem dentro do RTE. O visualzualizador de arquivo (upload.rhtml usando meu código):



1 <script language="javascript" type="text/javascript"
src="/javascripts/tiny_mce/tiny_mce_popup.js"></script>
2 <script language="javascript" type="text/javascript">
3 tinyMCEPopup.execCommand("mceInsertContent", false, '<%=
@insertString %>');
4 tinyMCEPopup.close();
5 </script>


Isso também é muito simples. Usando a função interna do TinyMCE, nós pegamos nossa string HTML construída e colocamos em DOM e então fechamos o janela popup.


Terminando

Agora nós temos todos os pedaços para tentar nosso novo plugin. Nele você pode fazer upload de imagem e ele aparecerá imediatamente na área e texto do TinyMCE da sua aplicação. Se você está pronto para colocar em produção e quer seguir as boas práticas do TinyMCE, não esqueça de copiar seu "editor_plugins.js" para dentro do arquivo "_src" e despir o arquivo de execução para ser carregado mais rapidamente. (Moxiecode sugere o jstrim.)

Como mencionado anteriormente, existem muitas maneiras interessantes e tarefas complexas que você pode realizar com um plugin como esse. A intenção aqui foi fazer uma viagem completa ao de como funciona um plugin e como ele pode ser usado para criar trabalhos futuros.
Esperançosamente essa ajuda vai iniciá-lo.


-----


A tradução acima necessita de ajustes (principalmente o 2o parágrafo), visto que essa é uma das primeiras traduções que faço para tentar aprimorar meu Inglês. Se você quer contribuir melhorando essa tradução deixe um comentário ou envie um email para jackson.pires [at] gmail.com.

Desde já eu agradeço sua contribuição.

4 comentários:

Renato Biaz disse...

Olá Jackson,
Gostei muito do seu post. Estamos em um projeto final de faculdade e vamos utilizar o tiny com o "upload", vai nos ajudar muito. Agora, estamos tentando já alugum tempo manipular o conteúdo do tiny em tempo de execução, sabe me dizer se é possível? Abraços, Renato Biaz.

Unknown disse...

Olá Renato!

Olha, eu não trabalho com o tiny, então não estou por dentro, mas acredito que seja sim possível a manipulação. Eu estou usando o FKEditor que já está pronto com a parte de upload que foi implementada em javascript, achei muito interessante a solução. Se não me engano ela está no github.. http://github.com/bilson/fckeditor/tree/master

vlw!

Renato Biaz disse...

Olá Jackson,
Valeu pela dica, irei testar o Fkeditor.
Agora eu acho que não expliquei direito, eu gostaria de fazer um botão com um evento que quando o usuário desse um click, seria inserido dentro da área de texto do tiny um input text com uma máscara qualquer, ou até do Fkeditor. Será que é possível? Abraços.

Unknown disse...

Ôpa...

É possível sim fazer o que vc está querendo no próprio FCKEditor, pra isso é só vc procurar como criar um plugin para ele, para isso vc pode ir na parte de documentação (http://docs.fckeditor.net/), lá explica alguma coisa, de resto vc pode procurar no google tbm.

vlw!