eZ Community » Blogs » Philippe VINCENT-ROYOL » Syntax Color with eZ Publish 5...

By

Syntax Color with eZ Publish 5 XmlText (twig)

Tuesday 20 August 2013 5:00:53 pm

  • Currently 5 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Previous eZ Publish release, it was really easy to add syntax color, thanks to ezgeshi extension. But now it's different with eZ 5 and this blog post will explain how to deal with it and Pygment.

I took a long time before having a working and nicely code like this, because eZ Publish 5 is used a XML parser do display content of your Xml Block. Before starting, just a note: this example explain you how to implement your own syntax color mechanism; of course this code could be optimise :)

Be Careful: this is only for 2013.6 and 5.1 ! If you would like to use 2013.7 and 5.2+ you have an other good way to proceed (https://confluence.ez.no/display/EZP/How+to+implement+a+Custom+Tag+for+XMLText+FieldType)

Requirements

Before playing with eZ Publish, you have to install Python first, and Pygments. In order to do that just copy and paste:

sudo apt-get install python
sudo easy_install Pygments

Of course, you could install Pygments via source or Mercurial. For this step please have a look to the online documentation.

eZ Publish 5

It's time to play with the "beast" ;) First step is to create a bundle !

Create your Pygment class

We are going to create our Pygment class which will  "communicate" with Pygments library in order to generate your colored code:

<?php
 
/**
 * Lexer documentation: http://pygments.org/docs/lexers/
 */
 
namespace pvr\PheelitBundle\DependencyInjection;
 
class Pygment
{
    public function __construct() {
 
    }
 
    static public function highlight_test($string, $lexer = 'bash', $format = 'html')
    {
        // use proc open to start pygmentize
        $descriptorspec = array(
            array( "pipe", "r" ), // stdin
            array( "pipe", "w" ), // stdout
            array( "pipe", "w" ), // stderr
        );
 
        $cwd = dirname( __FILE__ );
        $env = array();
 
        //if ( $extra_opts == "" )
        {
            $extra_opts = "-O style=default"; //,linenos=inline";
            if ( $lexer == "php" ) $extra_opts .= ",startinline=True";
        // } else {
        // Just append these to the passed-in args:
        // $extra_opts .= ",full,style=".$style.",cssclass=".$highlight_class;
        // }
 
        $proc = proc_open( '/usr/local/bin/pygmentize -l ' . $lexer . ' -f ' . $format . " " . $extra_opts, $descriptorspec, $pipes, $cwd, $env );
 
        if( !is_resource( $proc ) )
        {
            return false;
        }
 
        // now write $string to pygmentize's input
        fwrite( $pipes[0], $string );
        fclose( $pipes[0] );
 
        // the result should be available on stdout
        $result = stream_get_contents( $pipes[1] );
        fclose( $pipes[1] );
 
        // we don't care about stderr in this example
        // just checking the return val of the cmd
        $return_val = proc_close( $proc );
 
        if( $return_val !== 0 )
        {
            return false;
        }
 
        return $result;
    }
}

eZ Publish configuration

We have to "override" two things: service which used converter and xsl templates.

First, put those lines on your service.yml:

parameters:
    ezpublish.fieldType.ezxmltext.converter.html5.resources: %kernel.root_dir%/../src/Acme/DemoBundle/FieldType/XmlText/Input/Resources/stylesheets/eZXml2Html5.xsl
    ezpublish.fieldType.ezxmltext.converter.literalToHtml5.class: Acme\DemoBundle\FieldType\XmlText\Converter\LiteralToHtml5
 
services:
    ezpublish.fieldType.ezxmltext.converter.html5:
        class: %ezpublish.fieldType.ezxmltext.converter.html5.class%
        arguments:
            - %ezpublish.fieldType.ezxmltext.converter.html5.resources%
            - [@ezpublish.fieldType.ezxmltext.converter.embedToHtml5, @ezpublish.fieldType.ezxmltext.converter.ezLinkToHtml5, @ezpublish.fieldType.ezxmltext.converter.literalToHtml5]
 
    ezpublish.fieldType.ezxmltext.converter.literalToHtml5:
        class: %ezpublish.fieldType.ezxmltext.converter.literalToHtml5.class%

We pass a new arguments to tell to eZ Publish that it have to load our new converter on ezpublish.fieldType.ezxmltext.converter.html5 service.

Don't forget to copy files from /vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/FieldType/XmlText/Input/Resources/stylesheets/ to your bundle.

Modification of eZXml2Html5_core.xsl

Just change your literal part by

<xsl:template match="literal">
<xsl:value-of select="text()" disable-output-escaping="yes"/>
</xsl:template>

Create a converter

Not really hard to create a converter:  we define it like this:

<?php
 
namespace Acme\DemoBundle\FieldType\XmlText\Converter;
 
use eZ\Publish\Core\FieldType\XmlText\Converter;
use eZ\Publish\API\Repository\Repository;
use Acme\DemoBundle\DependencyInjection\Pygment;
 
use DOMDocument;
 
class LiteralToHtml5 implements Converter
{
    public function __construct( )
    {
 
    }
 
    public function convert( DOMDocument $xmlDoc )
    {
        foreach ( $xmlDoc->getElementsByTagName( "literal" ) as $literal )
        {
            $code = $literal->getAttribute( 'class' );
            $literalString = $literal->nodeValue;
            $pygments = new Pygment();
            $highlight = $pygments->highlight_test( $literalString, $code );
 
            $literal->nodeValue = htmlentities($highlight);
        }
    }
}

That's all :)

Just test it via a simple

{{ ez_render_field( content, "my_xml_block" ) }}

Tip: if you would like to keep space on your code (specially after span tag), you have to delete spaceless tag for ezxmltext_field and single_block_field or create new one :)

Have fun !

Proudly Developed with from