Fixing < code > blocks when using TinyMCE in BlogCFC

While working on setting up and integrating the superb BlogCFC with my own custom template, I discovered that the admin section does not utilise a richtext editor (Ray is hardcore :). However, Ray has been very helpful by splitting out the textarea HTML used on the blog entry input page which makes it very easy to plug in a suitable rich text editor.

The one I have chosen is TinyMCE. It's very nice to configure (incredibly easy - easy is good) and pretty lightweight. Nick Tong over at succor.co.uk blogged about adding TinyMCE to BlogCFC last year and it's a nice clear guide to anyone who wants to do this. Check out his blog post here.

Now, while it's a pretty trivial job to get TinyMCE working with BlogCFC there are a number of initial problems that break some functionality. These are to do with the custom tags which you can use to embed certain formatting / features into a blog post. As the tags are HTML style TinyMCE decides to nuke them from the source code view and escapes them in visual mode - not good.

The first of these is the <more/> tag which allows you to break up a blog post so that on the index page you only see a summary and then click 'more' to view the entire post. A fix to this is to modify BlogCFC to support [ more/ ] instead. Nick Tong has again blogged about making the changes here.

The second problem is in including a nicely colour coded scrolling code box which you can use to include Coldfusion code samples on your blost posts. Out of the box, the way to do this is to enclose the code sample with <code> </code> tags. As above, this is a no no with TinyMCE as it doesn't recognise the tags as valid HTML and so removes them. If you try including them in the visual mode (which is what I wanted) it escapes the characters and breaks the functionality.

I spent a couple of hours looking into this and have come up with a modification which allows the code output functionality to work in visual editing mode, but using { code } and { /code } tags instead (without the spaces - I am having to do that to avoid my mod kicking in and showing 'and' in code display :).

It's not perfect as any e-mails then get sent out will include the { code } and { /code } tags, although this could be fixed by modifying the e-mail sender function.

Anyway, here is the code. The first block shows what code you need to replace. The second block is the code you will need to copy and paste into blog.cfc. This mod is based on version 5.9 of BlogCFC though it may work in earlier versions:

File: org/camden/blog/blog.cfc - Lines 2087 - 2113:

<!--- Check for code blocks --->
  <cfif findNoCase("<code>",arguments.string) and findNoCase("</code>",arguments.string)>
   <cfset counter = findNoCase("<code>",arguments.string)>
   <cfloop condition="counter gte 1">
        <cfset codeblock = reFindNoCase("(?s)(.*)(<code>)(.*)(</code>)(.*)",arguments.string,1,1)>
         <cfif arrayLen(codeblock.len) gte 6>
              <cfset codeportion = mid(arguments.string, codeblock.pos[4], codeblock.len[4])>
              <cfif len(trim(codeportion))>
                    <cfif arguments.printformat>
                         <cfset style = "codePrint">
                    <cfelse>
                         <cfset style = "code">
                     </cfif>
                    <cfset result = variables.utils.coloredcode(codeportion, style)>
                    <cfelse>
                         <cfset result = "">
                    </cfif>
                    <cfset newbody = mid(arguments.string, 1, codeblock.len[2]) & result & mid(arguments.string,codeblock.pos[6],codeblock.len[6])>
 
                    <cfset arguments.string = newbody>
                    <cfset counter = findNoCase("<code>",arguments.string,counter)>
               <cfelse>
                    <!--- bad crap, maybe <code> and no ender, or maybe </code><code> --->
               <cfset counter = 0>
          </cfif>
   </cfloop>
  </cfif>

Replace the above code with the following:

<!--- Check for code blocks --->
  <cfset var codeTags = ArrayNew(1)>
  <cfset codeTags[ArrayLen(codeTags)+1] = StructNew()>
  <cfset codeTags[ArrayLen(codeTags)].startTag = "<code>">
  <cfset codeTags[ArrayLen(codeTags)].endTag = "</code>">
  <cfset codeTags[ArrayLen(codeTags)+1] = StructNew()>
  <cfset codeTags[ArrayLen(codeTags)].startTag = "{code}">
  <cfset codeTags[ArrayLen(codeTags)].endTag = "{/code}">
  <cfset codeTags[ArrayLen(codeTags)].richText = true>
 
  <cfloop from="1" to="#ArrayLen(codeTags)#" index="codeTagCount">    
   <cfif findNoCase("#codeTags[codeTagCount].startTag#",arguments.string) and findNoCase("#codeTags[codeTagCount].endTag#",arguments.string)>  
    <cfset counter = findNoCase("#codeTags[codeTagCount].startTag#",arguments.string)>
    <cfloop condition="counter gte 1">
     <cfset codeblock = reFindNoCase("(?s)(.*)(#codeTags[codeTagCount].startTag#)(.*)(#codeTags[codeTagCount].endTag#)(.*)",arguments.string,1,1)>
                
     <cfif arrayLen(codeblock.len) gte 6>
      <cfset codeportion = mid(arguments.string, codeblock.pos[4], codeblock.len[4])>
     
      <cfif StructKeyExists(codeTags[codeTagCount],"richText")>
       <!--- Replace various elements which the rich text editor will have inserted which we don't want for code view --->
       <cfset codeportion = Replace(codeportion,"<p>","#Chr(13)#","ALL")>
       <cfset codeportion = Replace(codeportion,"</p>","","ALL")>
       <cfset codeportion = Replace(codeportion,"<br />","#Chr(13)#","ALL")>

       <cfset codeportion = Replace(codeportion,"&lt;","<","ALL")>
       <cfset codeportion = Replace(codeportion,"&gt;",">","ALL")>
       <cfset codeportion = Replace(codeportion,"&nbsp;","[nbsp]","ALL")>
       <cfset codeportion = Replace(codeportion,"&amp;##","&##","ALL")>
      </cfif>
           
      <cfif len(trim(codeportion))>
       <cfif arguments.printformat>
        <cfset style = "codePrint">
       <cfelse>
        <cfset style = "code">
       </cfif>
       <cfset result = variables.utils.coloredcode(codeportion, style)>
      <cfelse>
       <cfset result = "">
      </cfif>
     
      <cfif StructKeyExists(codeTags[codeTagCount],"richText")>
       <!--- Replace the nbsp placeholder back into a real &nbsp; --->
       <cfset result = Replace(result,"[nbsp]","&nbsp;","ALL")>
       <cfset result = Replace(result,"&amp;","&","ALL")>
      </cfif>
     
      <cfset newbody = mid(arguments.string, 1, codeblock.len[2]) & result & mid(arguments.string,codeblock.pos[6],codeblock.len[6])>

      <cfset arguments.string = newbody>
      <cfset counter = findNoCase("#codeTags[codeTagCount].startTag#",arguments.string,counter)>
     <cfelse>
      <!--- bad crap, maybe <code> and no ender, or maybe </code><code> --->
      <cfset counter = 0>
     </cfif>
    </cfloop>
   </cfif>
  </cfloop>

This should work nicely and allow you to add code blocks into TinyMCE visual mode.

You can change the start and end tags recognised for code output by editing this block of code:

  <cfset var codeTags = ArrayNew(1)>
  <cfset codeTags[ArrayLen(codeTags)+1] = StructNew()>
  <cfset codeTags[ArrayLen(codeTags)].startTag = "<code>">
  <cfset codeTags[ArrayLen(codeTags)].endTag = "</code>">
  <cfset codeTags[ArrayLen(codeTags)+1] = StructNew()>
  <cfset codeTags[ArrayLen(codeTags)].startTag = "{code}">
  <cfset codeTags[ArrayLen(codeTags)].endTag = "{/code}">
  <cfset codeTags[ArrayLen(codeTags)].richText = true>

The 'richText' key signifies that the start and end tags contain text from a richtext editor and thus require certain character filtering etc.

Let me know if you find any problems with this. I have a nasty suspicion there may be a few problems with certain code samples but I haven't had too much time to test this yet. Looking good so far though.

Apologies in advance if anyone comes up with a cleaner and simpler way of doing this - this was really just a quick fix to get TinyMCE working with code output - , but if you do please post in the comments.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Michael Groves's Gravatar Great article James.

I'm having a problem getting richtext to work with cftextarea in a cfgrid.
The cftextarea is populated when one clicks on an entry in the cfgrid which in
turn is populated by a query. When I remove the cfgrid and corresponding code
for the textarea and just leave the textarea in place the richtext editor will show
as well as in instances created using tinymce encoded.

I've even tried installing Tinymce site specific and using the various and many ways
to instantiate the editor upon the cftextarea. Nothing works.

Is this an unknown problem with the cftextarea?

Regards,
Michael
sentiant@parallelcomputersolutions.com
# Posted By Michael Groves | 12/25/07 12:44 AM
James Allen's Gravatar Hi Michael,

Thanks for your comment - much appreciated.

I'm afraid I haven't come across the problem you have with <cfgrid>, <cftextarea> and TinyMCE.
I imagine that the javascript which <cfgrid> is using is causing somethig to break in TinyMCE.
I haven't really used those tags yet though so I'm not sure how they work technically.

Sorry I couldn't be any more help but good look finding the solution.
# Posted By James Allen | 1/8/08 9:20 PM
Michael Groves's Gravatar The client asked me not to pursue it further. However, I did notify Adobe
of this issue and have yet to hear from them about it. Great article by
the way. I've used MCE on my coldfusion sites for a few years now and
really like its being lightweight and so on. It does have some issues with
handling line feeds and links but once you look at the code underlying
its pretty easy to work with. Most users of a site might not know
how to do this though.
# Posted By Michael Groves | 1/8/08 9:42 PM
James Allen's Gravatar Oh I hear you on that one. I think TinyMCE is one of the better ones out there for code generation but I don't think any of the Richtext editors are perfect.

In my last job we used SoEditor Pro by SiteObjects. That would often generate appalling code and the amount of problems it gave us when clients were let lose on it.. I ended up writing code to clean up the HTML upon form submission. To be fair though, it must have been about 4 years old and we continued to use it due to the codebase I had built around it.

I'm thinking of focusing on FCKEditor for a big project I'm working on now due to it being built into CF8. Apparentely it works really well and has some excellent ongoing development by it's author. Saying that though, I think TinyMCE really hits the nail on the head when it comes to lightweight deployment..
# Posted By James Allen | 1/8/08 9:48 PM
Kris Boldt's Gravatar I AM HAVING A HARD TIME EXPANDING THE VERTICAL HEIGHT OF THE TEXT AREA . Does anybody know how to control the width and height of the editor?
# Posted By Kris Boldt | 2/2/08 5:36 AM
Edward Beckett's Gravatar @ Kris ... I had the same issue ... it's really easy to adjust ...

Did you look in the TinyMCE docs for the <a href="http://wiki.moxiecode.com/index.php/TinyMCE:Config...; width and height attributes? </a> ...
# Posted By Edward Beckett | 3/1/08 6:05 AM
© 2010 James Allen | Contact Me
This blog runs on the awesome power of BlogCFC - created by Raymond Camden. This blog is running version 5.9.