Le blog de la CT2C

Créer un petit éditeur de texte pour forums - Etape 2

Par Kulgar , publié le 21 Novembre 2012

Aussi appelé "Parser", le traducteur permet de traduire le BBCode/Markdown en code HTML lors de l'affichage du BBCode/Markdown enregistré dans la base de donnée. Il existe déjà plusieurs gem pour traduire du BBCode en Rails, dont bb-ruby, ou bbcodeizer et sûrement quelques autres. Remarquez que certains ne sont plus mis à jour, pour la simple et bonne raison qu'une fois que vous avez un bon parser, il n'est plus utile de le modifier. ;) Mais, faites attention à la compatibilité avec Rails 3.1+.
Bref, moi j'ai choisi bb-ruby pour le BBCode, et Redcarpet pour markdown. Voyons voir comment ça marche.


BB-Ruby pour le BBCode


Pour installer bb-ruby, tapez donc la commande: gem install bb-ruby. Ne l'ajoutez pas à votre gemfile et ne le mettez pas non plus en tant que "requis" dans le fichier "application.rb". Pourquoi ? En fait juste pour plus de praticité, nous allons récupérer le code qui nous intéresse de cette gem.

Allez dans le répertoire où est installé la gem :

Sous windows: "c:/ruby19x/gems/1.9.x/gems/bb-ruby" (de mémoire).
Sous Linux: "/home/user/.rvm/gems/ruby-1.9.x/gems/bb-ruby".



Dans le dossier "bb-ruby", allez dans "lib" et ouvrez le fichier "bb-ruby.rb".
En gardant le fichier ouvert, retournez dans votre application et créez un nouveau fichier dans le dossier "helpers" que j'ai appelé "bbcode_helper.rb". Copiez collez le code du fichier bb-ruby.rb dans ce nouveau helper puis remplacez toutes les occurrences de BBRuby par BBCodeHelper (enfin par le nom que vous avez donné au helper, faites attention il y en a aussi tout en bas du fichier).

Pourquoi faire ça ? Simple, même si le développeur a prévu de quoi créer ses propres traduction de balise BBCode, vous remarquerez très vite (enfin, je vous le dis) qu'il est plus rapide de les ajouter directement dans le code de la gem. De plus, en faire un "helper", le rend bien plus portable sur tous vos projets. ;) Si vous vous demandez comment tout ce code fonctionne, patience, nous le décortiquerons dans une des étapes suivantes afin que vous puissiez tout personnaliser.

Maintenant que nous avons créé ce Helper, nous sommes presque au bout de nos peines. Nous allons désormais pouvoir utiliser les fonctions de ce helper qui permettent de traduire le BBCode en code HTML, ou plus exactement la méthode : "bbcode_to_html". Cette méthode a été conçue pour s'appliquer directement sur un String (une chaîne de caractères).

Si nous reprenons notre exemple du commentaire. Admettons que vous enregistrez le commentaire de l'utilisateur dans l'attribut "body" du Model "Comment". Vous accédez donc à un commentaire à travers le code suivant : "comment.body" et, cette commande vous renvoie un String. Donc, si votre commentaire contient du BBCode, tout ce que vous aurez à faire ce sera d'écrire la ligne de code suivante (soit dans une vue soit dans un controller) :

comment.body.bbcode_to_html

Bon ! Admettons que votre commentaire contient le texte suivant:

Ceci est un commentaire <strong> où je n'ai rien à dire</strong>.

Si vous testez la méthode bbcode_to_html sur ce commentaire (donc sur ce texte) vous verrez les balises HTML en lieu et place des balises BBCode lorsque vous l'affichez dans votre navigateur. Vous obtiendrez l'affichage suivant:

<strong>Ceci est un commentaire</strong> <strong> où je n'ai rien à dire </strong>.

Et là, vous vous dites: mais il n'y a pas de différences entre les balises BBCode et les balises HTML enregistrées dans le commentaire! En fait si! Rappelez-vous, Rails échappe automatiquement les caractères spécifiques à HTML lorsqu'il enregistre des données dans la base de données. En fait dans la base de données vous avez ceci d'enregistré :

Ceci est un commentaire &l t;strong&g t; où je n'ai rien à dire &l t;/strong&g t;.

(En principe, il n'y a pas d'espaces entre lt et gt, mais si je n'en mets pas il m'affiche les caractères < et >).
Donc, pour vos yeux, il n'y a pas de différence, mais pour Rails et pour le navigateur il y en a bien une. Et là, une autre question: Pourquoi du coup, le navigateur n'interprète-t-il pas les balises <strong></strong> issues de la traduction du BBCode ?

Parce que Rails est très bien sécurisé et malin. Par défaut, il considère que tout ce qui vient de la base de données n'est pas sûr. Il faut donc lui dire : ces données que tu reçois sont sûres et tu peux interpréter l'HTML sans échapper ses caractères. Non seulement il faut lui dire que le code est sûr, mais en plus on doit lui demander expressément de mettre en forme le code HTML reçu.

En Rails, il existe la fonction "simple_format" pour effectuer cette mise en forme. Fort heureusement, la gem que nous avons téléchargé a prévu le coup, et la fonction "montexte.bbcode_to_html_with_formatting" fait le même boulot que "simple_format(montexte.bbcode_to_html)".

Nous devons également signaler à Rails que l'HTML est sûr, pour ça, la méthode "html_safe" existe. Ce qui nous donne donc pour avoir un joli commentaire mis en forme comme il faut:

comment.body.bbcode_to_html_with_formatting.html_safe

Et là, magie, votre commentaire sera mis en forme uniquement pour les balises HTML issues de la traduction du code BBCode.
Certains vous parleront aussi de la méthode "raw" pour faire le boulot de html_safe. Ca marche aussi, les deux méthodes sont presque quasi-équivalentes. La différence réside juste dans le fait que raw est une méthode de helper et pas html_safe, vous ne pourrez donc utiliser raw qu'uniquement dans des controllers ou des views. Ma préférence réside juste dans le nom de la méthode, je préfère la connotation de "html_safe". Si vous voulez en savoir plus sur comment tout ça fonctionne, voici un superbe article (en anglais)
Si vous voulez une traduction de cet article, postez un commentaire. Après une dizaine de demandes, j'effectuerai la traduction.

Redcarpet pour Markdown


Tout d'abord, ajoutez la gem Redcarpet dans votre Gemfile et lancez un bundle install.

Vous devez mettre dans vos initializers quelques configurations, qui vous permettront d'interpréter correctement vos balises. Par exemple, dans "config/initializers/gems_config.rb ":
### Redcarpet & Markdown
renderer = Redcarpet::Render::HTML.new(
:with_toc_data => true,
:filter_html => true)
MARKDOWN = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
:autolink => true,
:space_after_headers => true,
:hard_wrap => true)

Il y a de nombreuses options disponibles, à vous de voir lesquelles vous souhaiter activer ou non. (N'oubliez pas de redémarrer votre serveur après cette manipulation.)

Pour interpréter votre code, vous devrez mettre ceci dans votre view :
<p><%= MARKDOWN.render(comment.body.).html_safe %></p>

Vos balises markitup devrait à présent être correctement interprété et rendre des mots en gras, en italique, etc. Magique !

La Preview


Pour faire fonctionner la preview au clic du bouton de l'interface de MarkItUp, ce n'est pas compliqué en fait. Il faut juste savoir que MarkItUp envoie le contenu du champ d'édition dans une variable POST "data" qui est récupérable, en Rails, via la variable "params" : "params[:data]".
Comment ai-je su pour cette variable "data" ? Grâce à Firebug (quel excellent outil quand même!). Si vous l'avez, ouvrez donc le panneau de Firebug et allez dans l'onglet "console". Cliquez sur le bouton de "preview" de MarkItUp. Une ligne devrait apparaître avec "POST http://adresse_web". Evidemment ça fait erreur puisque vous n'avez sans doute pas configuré la route ni même l'adresse web adéquate, mais on y reviendra. Cliquez plutôt sur le + à côté de cette ligne. Là, cliquez sur l'onglet "Post", et vous devriez y voir la variable "data" dont je parlais contenant le texte que vous avez écrit dans le champ de text_area.

En fait, MarkItUp envoie une requête Ajax vers l'url que vous spécifiez dans le fichier "bbcode_set.js" devant "previewParserPath:". Admettons que vous avez donc ceci dans votre fichier "bbcode_set.js" :

mySettings = { 
previewParserPath: '/preview', //Vous pouvez mettre l'url que vous souhaitez, moi j'ai mis preview
...

Lorsque vous cliquez sur le bouton "preview" de l'interface d'édition de MarkItUp vous avez maintenant une requête Ajax "POST" vers l'URL /preview. Première chose à faire : créer la route. Pour ça, ouvrez votre fichier /config/routes.rb et ajoutez-y la ligne suivante:

post "/preview" => "comments#preview"

Comme nous sommes dans l'exemple où vous voulez personnaliser l'interface d'édition du textarea pour poster un commentaire, je choisis volontairement de dire à Rails d'aller chercher l'action "preview" dans le controller "Comments". :via => "post" n'est pas obligatoire, mais conseillé. Si vous ne le spécifiez pas /preview sera aussi accessible par une requête "get/put/delete", et, ces requêtes, on n'en a pas l'utilité. Il est toujours de bon ton de restreindre une route aux requêtes HTTP désirées.

Reste à créer l'action dans le controller Comments. Ouvrez-le donc et ajoutez le code suivant :

def preview 
text = params[:data]
parsed_text = text.bbcode_to_html_with_formatting.html_safe
render :inline => parsed_text, :layout => 'preview_layout'
end

Prenons les éléments dans l'ordre. Première ligne, on récupère le contenu du textarea, nous venons de le voir, on récupère ce contenu grâce à params[:data].

Deuxième ligne on traduit le BBCode en code HTML en utilisant les mêmes méthodes que nous avons utilisées pour afficher et formater correctement le commentaire.

Troisième ligne... Le mot clé "render" renvoie un rendu de ce que vous lui spécifiez. Si ce mot clé n'est pas présent, l'action ira chercher la vue "preview.html.erb". Notez que vous pouvez toujours utiliser cette méthode, mais là ce n'est pas nécessaire en fait... grâce à...
render :inline : cela va renvoyer une vue contenant uniquement ce que vous spécifiez dans "inline", ici nous lui mettons le contenu du commentaire avec le BBCode traduit et formaté.

Enfin, ":layout" est une option de render et permet d'utiliser un autre layout que celui par défaut (à savoir "application.html.erb"). Mon "preview_layout.html.erb" est très minimaliste. Créez le dans le dossier app/views/layouts et ajoutez-y :

<!DOCTYPE html> 
<html lang="fr">
<head>

<%= csrf_meta_tags %>
<%= stylesheet_link_tag "application"%>

</head>
<body id="markitup_preview_body">

<%= yield %>

</body>
</html>


Vous pouvez y mettre ce que vous voulez. Comprenez bien que la preview affichée est une "iframe", une page dans une page donc et que cela va s'afficher dans un cadre en dessous de votre textarea. Vous ne voulez certainement pas que cette preview ait le même affichage que vos autres pages du site, cela risque de mettre un gros bazar. C'est pour cette raison que nous avons recours à cette option ":layout" de render.

Voilà, avec tout ça, votre preview devrait fonctionner. Je vous laisse essayer ;). Le prochain billet va vous apprendre à personnaliser et créer vos propres balises BBCode. (En cours de rédaction).


Index -- --

  • Aucun commentaire - Soyez le premier !

Insérez votre commentaire
  1. Min: 50 caractères, Max: 800. Actuellement: 0 caractères

  2. ne pas remplir