ZMarkdown regroupe plusieurs modules autour de remark pour transformer le MarkDown en différents formats. Je n’utilise ici que la sortie HTML, mais vous pouvez très bien l’utiliser pour générer des PDF, des epubs ou du LaTeX.

Installation

On commence donc par un peu de configuration pour Ansible :

group_vars/all :

zmarkdown_root_dir: /var/www/zmarkdown/
env: production

# Web user
web_user: www
web_user_group: www

On peut ainsi commencer à installer les dépendances :

install.yml :

---
- name: Install server requirements
  hosts: all
  become: yes

  roles:
    # (les autres rôles)
	- zmarkdown

roles/zmarkdown/handlers/main.yml :

---
- name: restart zmd
  service:
    name: zmd
	enabled: true
    state: restarted

roles/zmarkdown/tasks/main.yml :

---
- name: Make sure Node.js is installed
  apt:
    update_cache: yes
    name:
      - nodejs

- name: Install "zMarkdown" package
  npm:
    name: zmarkdown
    production: true
    path: "{{ zmarkdown_root_dir }}"

- name: Change ownership of zMarkdown installation
  file:
    path: "{{ zmarkdown_root_dir }}"
    owner: "{{ web_user }}"
    group: "{{ web_user_group }}"
    state: directory
    recurse: yes

- name: Create service file
  template:
    src: zmd.service.j2
    dest: "/etc/systemd/system/zmd.service"
    mode: u=rw,g=r,o=r
  notify: restart zmd

- name: Ensure that zMarkdown is running
  uri:
    url: http://localhost:27272/
    return_content: true
  register: this
  delay: 1
  retries: 10
  until: "'zmd is running' in this.content"

roles/zmarkdown/templates/zmd.service.j2 :

# [PM2] Spawning PM2 daemon with pm2_home=/home/zds/.pm2
[Unit]
Description=zmd server
Documentation=https://pm2.keymetrics.io/
After=network.target

[Service]
Type=forking
User={{ web_user }}
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=/usr/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=PM2_HOME={{ zmarkdown_root_dir }}/.pm2
Environment=ZDS_ENVIRONMENT={{ env }}
PIDFile={{ zmarkdown_root_dir }}/.pm2/pm2.pid

ExecStart={{ zmarkdown_root_dir }}/node_modules/pm2/bin/pm2 start -f {{ zmarkdown_root_dir }}/node_modules/zmarkdown/server/index.js -i 2 --max-memory-restart 100M
ExecReload={{ zmarkdown_root_dir }}/node_modules/pm2/bin/pm2 reload all
ExecStop={{ zmarkdown_root_dir }}/node_modules/pm2/bin/pm2 kill

[Install]
WantedBy=multi-user.target

Un petit coup de make server-install pour tout installer via Ansible et le service tourne !

J’utilise ici le package npm zmarkdown qui inclut tout ce qu’il faut pour faire tourner un serveur HTTP local facile à interfacer avec n’importe quel autre langage.

Utilisation

Maintenant il faut utiliser tout ça dans Laravel.

On commence avec un peu de config (config/zmd.php) :

<?php
return [
    'url' => env('ZMARKDOWN_URL', 'http://localhost:27272'),
    'html' => env('ZMARKDOWN_URL_HTML', 'http://localhost:27272/html'),
];

Et dans le modèle on enregistre automatiquement le HTML généré dès que le MarkDown change (app/Models/BlogPost.php) :

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Http;

class BlogPost extends Model
{
	// …

	public function setContentAttribute($value)
	{
		$this->attributes['content'] = $value;
		$this->attributes['content_html'] = Http::post(config('zmarkdown.html'), [
			'md' => $value,
			'opts' => [
				'heading_shift' => 0,
				'disable_ping' => true,
				'disable_jsfiddle' => false,
				'stats' => false,
			]
		])->json()[0];
	}
	
	// …
}

Affichage

À l’affichage rien de plus simple :

<div class="post-content">
	<div>
		{!! $post->content_html !!}
	</div>
</div>
Attention

Cette méthode n’échappe pas le HTML généré avant affichage, c’est à vous de vous assurer que le parseur ne laisse pas passer de code malicieux !
C’est d’ailleurs en partie pour cette raison que je n’utilise ZMarkdown que pour des contenus que je maîtrise sur mon site.