Full highlighting functionality with symfony, Geshi and TinyMCE


Symfony | Technical | Development | Doctrine | September 7, 2010

Most of the time when displaying programming code in a web page is better to see it highlighted. It is better for the visitors who try to read the code, to see it highlighted like in an IDE. Well, in this post, a full highlighting functionality will be described step by step with symfony, GeSHI and TinyMCE, based on a previous post.

 

Schema

First we will focus on the schema for the database.

GeSHI will parse code like:

[php] echo 'symfony, GeSHI and TinyMCE'; [/php]

 

And will produce code like:

 
<p><span class="kw1">echo</span><span class="st_h"> 'symfony, GeSHI and TinyMCE'</span>;</p>
 

 

This parsing is quite intensive, then we can not do it everytime a post needs to be shown. Then we must have a cache table for our posts, where to save the parsed code. This way we will have two tables: post and post_index

 

 
Post:
  tableName:          post
  actAs:
    Sluggable:        { fields: [ title ] }
    Timestampable:
  columns:
    id:               { type: integer  , length: 20 , primary: true    , autoincrement: true                                                  }
    title:            { type: string   , length: 200                   , notnull: true                                                        }
    content:          { type: string   , length: 20000                 , notnull: true                                                        }
    excerpt:          { type: string   , length: 500                   , notnull: true                                                        }
  indexes:
    u_title:          { fields: [ title ]                              , type: unique                                                         }
    i_content:        { fields: { content: { length: 400 } }                                                                                  }
    i_excerpt:        { fields: { excerpt: { length: 100 } }                                                                                  }
  relations:
    PostIndex:        { class: PostIndex        , local: id            , foreign: post_id      , type: one , alias: PostIndex                                         }
 
PostIndex:
  tableName:          post_index
  columns:
    id:               { type: integer  , length: 20 , primary: true    , autoincrement: true                                                  }
    post_id:          { type: integer  , length: 20                    , notnull: true                                                        }
    title:            { type: string   , length: 200                   , notnull: true                                                        }
    content:          { type: string   , length: 20000                 , notnull: true                                                        }
  indexes:
    u_title:          { fields: [ title ]                              , type: unique                                                         }
    i_content:        { fields: { content: { length: 400 } }                                                                                  }
  relations:
    Post:             { class: Post             , local: post_id       , foreign: id           , type: one , alias: Post      , onDelete: CASCADE , onUpdate: CASCADE }
 

 

 

Form

To properly insert and update data to our new brand post table we need to customize a PostForm class:

 

 
class PostForm extends BasePostForm
{
  public function configure()
  {
    $this->setWidgets(array
    (
      'id'        => new sfWidgetFormInputHidden(),
      'title'     => new sfWidgetFormInputText(array(), array('size' => 60)),
      'content'   => new sfWidgetFormTextareaTinyMCE(array
                     (
                       'width'            => 550,
                       'height'           => 350,
                       'config'           => 'theme_advanced_disable: "anchor,cleanup,help"',
                     )),
  }
 
  protected function updateContentColumn($content)
  {
    $index_content = preg_replace_callback('#< pre>\[(\w+)\](.*?)\[/\\1\]< /pre>#s', array($this, 'processCode'), $content);
 
    $this->object->getPostIndex()->setContent($index_content);
 
    return $content;
  }
 
  protected function processCode($matches)
  {
    $code = str_replace('& nbsp;', ' ', $matches[2]);
    $code = html_entity_decode($code);
 
    $geshi = new GeSHi($code, $matches[1]);
    $geshi->set_header_type(GESHI_HEADER_PRE);
    $geshi->enable_classes();
 
    return $geshi->parse_code();
  }
}
 

 

In this class we configure the post form to show the title and content widgets. In the content widget it is been used the great sfWidgetFormTextareaTinyMCE class from the sfFormExtraPlugin.

In the updateContentColumn method it is applied the processCode method for each coincidence of

< pre>[(text)] ..... [/(text)]< /pre>

, where text is the type of desired code to be highlighted, for example it could be: php, java, yaml, css, javascript, etc. For further explanation about that part see the previous post about highlighting code with symfony and Geshi.

 

Also note that the parsed code is assigned to the content of the PostIndex object:

 
$this->object->getPostIndex()->setContent($index_content);
 

 

 

TinyMCE

Now it is the time to talk about one big issue with this editor when trying to paste code.

The heart of the problem lies in the fact that each time some code is pasted in TinyMCE, automatically it is added html formatting, that is tags with a lot of &nbps; and all weird characters like -> are escape.

The problem with this is GeSHI needs clean code, like the one produced when copying to a simple textarea, but not the TinyMCE formatted one.

The solution is the PreElementFix Plugin for TinyMCE. The instructions for the installation are in the previous link. The great thing about this plugin is that it does not allow the addition of tags when pasting some code. The bad things is that there is still escape sequences. Well this is not a bad thing at all, it has to be this way for the code to be HTML compliant.

To configure the widget you can use the updated render method in the sfWidgetFormTextareaTinyMCE class:

 

 
  public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    $textarea = parent::render($name, $value, $attributes, $errors);
 
    $js = sprintf(<<< script type="text/javascript">
  tinyMCE.init({
    mode:                              "exact",
    elements:                          "%s",
    theme:                             "%s",
    plugins :                          "paste,preelementfix",
    %s
    %s
    theme_advanced_toolbar_location:   "top",
    theme_advanced_toolbar_align:      "left",
    theme_advanced_statusbar_location: "bottom",
    theme_advanced_resizing:           true,
    paste_auto_cleanup_on_paste : true
    %s
  });
EOF
    return $textarea.$js;
  }
 

 

And now, the solution to the escape sequences is to return them to their previous state. We can do this with this two lines of code in the form class:

 
$code = str_replace('& nbsp;', ' ', $matches[2]);
$code = html_entity_decode($code);
 

 

You can think, why to use the first line when at first sight html_entity_decode will do the job. However this function has some problems with &nbps; check that in the html_entity_decode php info.

 

After following all of these steps you will have an TinyMCE like:

 

TinyMCE image example

 

 

Finally, it is shown that with the use of a combination of tools like symfony, GeSHI and TinyMCE we can get clean and highlighted code like the one you saw in this post.

 

PS: An exercise for the reader is to discover why there are tiny on purpose errors on the code above, for example with &nbps; being & nbsp; .


Comments


pozycjonowanie said about about 1 year later

from you seen this terrific medical purlieus i unquestionably liked it and found legitimate constitution understanding

pharmacology of stimulant drugs for adhd said about about 1 year later

jeqJuF hmm what them is that? selfmade?

south maerica investments said about about 1 year later

w0w this is good

amazon Suggest Ripper said about about 1 year later

good blog as usual

Cyclobenzaprine said about about 1 year later

mmmmm sushi

captcha sniper said about about 1 year later

are valued, it creates

PS3 error said about about 1 year later

shoudlnt i try it?

tax haven said about about 1 year later

isnhOZ can i comment here

delaware said about about 1 year later

SH5n0r yeeeee good one!

Local Anaesthesia said about about 1 year later

i had to type here

iphone 6 said about about 1 year later

Write a post about leaving comments

iphone 6 said about about 1 year later

Write a post about leaving comments

usa insurance quotes said about about 1 year later

thx for dat post, was preety decent

Order veterinary medications said about about 1 year later

is that american ?

noclegi Szklarska Poreba said about about 1 year later

mmm pizza

buy accutane online said about about 1 year later

Good post.

wow gold said about about 1 year later

is that american ?

health dln said about about 1 year later

:) this made me laugh a bit

Adderall XR said about about 1 year later

Looking forward to it

przeprowadzki warszawa said about about 1 year later

like these posts most of all

strony edukacyjne said about about 1 year later

go into their my space now

Susan said about about 1 year later

I am so much excited after reading your blog. Your blog is very much innovative and much helpful.
Ahighbloodpressurediet.com

software tips said about about 1 year later

go into their my space now

Misa said about about 1 year later

Thanks a ton for stating your opinions. Being a writer, I am always in need of unique and different solutions to think about a topic. I actually uncover fantastic creativity in doing this. Many thanks
https://www.smallbusinessplansoftware.net/

click here said about about 1 year later

love to comment



Hey Stranger, leave a comment:

Last Posts

Autoloading: Symfony vs Yii

Symfony2, PhpBB4 and Drupal8

Type and boolean columns with Doctrine and Symfony

Refactoring queries with Doctrine

Extending your Doctrine Model: Template Classes

Integrating Doctrine: Symfony vs Yii

Passing parameters from the action to the view: Symfony vs Yii

Yii framework

Adding custom information to your Doctrine schema

PHP Advent Calendar 2010


My Tweets

about 1 month ago
Writing clean code in PHP 5.4 | Web Builder Zone: https://t.co/IAFFj3A8 via @addthis
4 months ago
Interesting tips about #scaling https://t.co/QC2paoDK
4 months ago
Learning to use #Windows #ActiveDirectory
4 months ago
Ayni - Blog: CUESTIONARIOS PARA ANÁLISIS-https://localhost:8080/AYNI-war/faces/ListarComentario.xhtml?txtIdPublicacion=2
5 months ago
Día tranquilo en casa :)#fbb
5 months ago
#Adobe Reader App crushed my new #Android movile :/
5 months ago
Testing from Android =D
7 months ago
#Refactoring code.
7 months ago
You should Snog, Marry or Avoid me https://t.co/pZCDIwW
7 months ago
Yahoo’s Options v@TechCrunchnhttps://t.co/yjzsKstKst What will Yahoo do?
7 months ago
Just realize my post https://t.co/nSuJjSp, written so many time ago, really contribute to make it happen: https://t.co/wMqYrV3
7 months ago
An Introduction to Redirecting URLs on an Apache Server https://t.co/s79ThOy via @WebmasterWorld
7 months ago
Actualizando a #Eclipse Indigo!
7 months ago
Vota x lamula.pe: https://t.co/WTWMkFG via @addthis
8 months ago
Apple Pushes Past Exxon To Become The Most Valuable Public Company In The World (Temporarily) via@TechCrunchhhttps://t.co/lowONSZZ
9 months ago
finally this class #semester is finished, new #projects in mind
9 months ago
Mark Zuckerberg Explains His Law Of Social Sharing [Video]https://t.co/sqq10Ehh via@TechCrunchh
9 months ago
retomando mi #twitter #notime
10 months ago
How Facebook Can Put Google Out of Businesshttps://t.co/HqDfQGoo via@TechCrunchh
11 months ago
So why not just cut out the middle man? Microsoft.https://techcrunch.com/2011/05/15/samsung-series-5-chromebook/
11 months ago
really like to #design class hierarchies with #compositepattern
11 months ago
Similarly, Microsoft.com started to use jQuery instead of their own ASP.NET Ajax framework. They are still using Windows, for whatever XD
11 months ago
Estudiantes de la PUCP le “voltean” campaña a esposo de Keiko | yoperiodhttps://t.co/ruI9UnmI9Unm@lamulaamula
11 months ago
Reading: Apress.-.Pro.html"https://www.twitter.com/search?q=#php">#php #performance #read
11 months ago
debug_backtrace() is very important on certain situations. #php #debug
12 months ago
@alvarezrodrich felicitaciones!
12 months ago
time to do some #uml diagrams, #classdiagram
12 months ago
hoy es el día,#votaa conciente#peruu
12 months ago
@skoop @funkatron I think someone had a bad day!, #Frameworks are there but you don't have to use them.
12 months ago
making #wireframes for a new #functionality
12 months ago
so much #spam on my #blog =(
12 months ago
learning new topics and tools that I did not use before #rcp
12 months ago
aprendiendo muchos temas y herramientas que no utilizaba antes #rcp
about 1 year ago
#tweaking httpd.conf #virtualhost
about 1 year ago
thanks #symfony 1.4, even when i'm not using the entire #framework, yours classes save my life!
about 1 year ago
integrating with #SOA using #soap
about 1 year ago
It seems my most #productive working hours are on #sunday #afternoon #evening! XD
about 1 year ago
#ASOT 500 =)
about 1 year ago
installing SCA_SDO on #Centos #php
about 1 year ago
My web service using #soap worked!!!!! #php #SCA #SDO
about 1 year ago
Working in a new place since last week!, #RCP: Red Científica Peruana, the one which sells the .pe domains in#Perúú ->#happypy
about 1 year ago
Finally with a new #laptop: #Toshiba =)
about 1 year ago
@pasku1 Thanks, I will try Pivotaltracker.
about 1 year ago
@doolphy thanks for your answer doolphy! I'll try you!
about 1 year ago
@jmasson thanks for your answer! Jira + Confluence is a good combination.
about 1 year ago
Which is the best project management and collaboration tool right now? #projectmanagement #tool #collaboration
about 1 year ago
What a #voice! Sied Van Riel feat Nicole McKenna - Stealing Time (Aly & Fila Remix) + #ASOT 497 #trancefamily
about 1 year ago
working on a situation where #php #traits would be very useful
about 1 year ago
It is #awesome when you finish doing a lot of changes and nothing is broken =) #TDD #testing
about 1 year ago
oh, happy birthday! @mtabini o mejor dicho feliz cumpleaños!
about 1 year ago
why do #IE8 not accept #javascript "const" keyword?
about 1 year ago
Discovering there is much #more to do with #javascript ... a lot.
about 1 year ago
#composition over #inheritance: #javascript
about 1 year ago
Awesome #song!: Cerf, Mitiska & Jaren - Another World (Original Vocal Mix) #ASOT 495 #trancefamily
about 1 year ago
My legal woman is #PHP, but I have an affair with #Javascript, overall when she wears #jQuery.
about 1 year ago
This presentation is one of the best I've seen about #unit #testing https://www.slideshare.net/avalanche123/clean-code-5609451
about 1 year ago
where to do a master on #IT: US or Spain? #survey #php #master plz RT
about 1 year ago
Bobina feat. Betsie Larkin - You Belong To Me: What a #beautiful voice -> #ASOT 494 #arminvanbuuren
about 1 year ago
OH NO, IT'S MONDAY -- 2011-02-07 https://t.co/pgaIxe5 via @gojkoadzic
about 1 year ago
it was not a + b, it was parserInt(a) + parseInt(b) =(, #javascript #fail
about 1 year ago
The models are complete representations of the system, whereas an #architectural #view focuses only on what is architecturally #significant.
about 1 year ago
@jmasson that would be great and finally #wikimedia, #drupal, #wordpress and maybe #joomla would push towards the same side, the #php side.
about 1 year ago
@jmasson Thanks!, #PHP has a bright #future ahead.
about 1 year ago
A new #blog post about not reinventing the #wheel: /article/symfony2_phpbb4_and_drupal8 #symfony #phpbb #drupal
about 1 year ago
@giorgiosironi #indeed, that's a very good #question. I think an average of 4 but also depends on how much that #developer work.
about 1 year ago
It seems the new platform for deploying, managing and scaling PHP apps is https://orchestra.io/ #cool
about 1 year ago
#Phase project planning vs #iteration project #planning - #project #management
about 1 year ago
#jeditable with #jquery save my life =)
about 1 year ago
Amazing #song -> Sied van Riel feat. Nicole McKenna - Stealing Time #ASOT 493 #trancefamily
about 1 year ago
I simply love "offset" #jquery function =)
about 1 year ago
New #blog post: type and boolean columns /article/type_and_boolean_columns_with_doctrine_and_symfony #doctrine #symfony
about 1 year ago
I really don't understand why projects like #drupal does not base their components in projects like #doctrine and #symfony
about 1 year ago
OH NO, IT'S MONDAY -- 2011-01-17 https://t.co/37pr4Bd via @gojkoadzic
about 1 year ago
@alvarezrodrich me alegra ver q ya borró su cuenta Sr. Rodrich,#twitterr es malo jajja, XD
about 1 year ago
Acabo de hacerle a mi #brother @diegonl89 un blog para que hable de #actualidad en general: https://www.elgatotechero.com #peru
about 1 year ago
I just made to my #brother a #blog to talk about current #events in #peru: https://www.elgatotechero.com
about 1 year ago
Amazing things can be done with #javascript and #css, and of course with the help of #jQuery =)
about 1 year ago
An architecturally significant element is an element that is important for #understanding the #system.
about 1 year ago
An architecturally element has a wide impact on the #structure, #performance, #robustness, #evolvability, and #scalability of a #system.
about 1 year ago
@giorgiosironi Definitely!
about 1 year ago
Playing with #table #inheritance in #Doctrine
about 1 year ago
Reading about #RUP, and how addresses the #major difficulties in a new #project.
about 1 year ago
A new blog #post about #refactoring your queries with #Doctrine: /article/refactoring_queries_with_doctrine #symfony
about 1 year ago
Yandex in 2010: 43 percent revenue growth https://t.co/cpjT5Jw via @cnet
about 1 year ago
Going forward!!!!! =) poco a poco llegan los resultados de tanto esfuerzo #fb
about 1 year ago
Perfect #system with respect to the #requirements but the #wrong system with respect to the #real #problem at the time of #delivery.
about 1 year ago
Going #forward! =) #fb
about 1 year ago
A new #blog #post about integrating #Doctrine with #Yii and #Symfony: /article/integrating_doctrine_symfony_vs_yii
about 1 year ago
@sam_dark Ok thanks!, but I don't understand why in #Yii documentantion use $_GET and $_POST instead of CHttpRequest https://bit.ly/i5emoL