Joseph Crawford Using wordpress because he is lazy

28Jan/0923

Integrating FCKeditor Into CakePHP

There are two different methods of FCKeditor integration to choose from with CakePHP: PHP and JavaScript. I will cover both of them here but I think my personal choice would be using the JavaScript method and replacing the textarea. That way there is a fall back for people with JavaScript turned off. For this example I will be using my previous post about creating a CMS using Cake and adding the famous rich text editor to the add and edit content pages.

The first thing you want to do is download the latest version of FCKeditor. At the time of this writing the current stable version is 2.6.3. You could try the beta version or the nightly build if you feel adventurous but personally I like sticking with stable versions.

PHP Integration Method

After you have finished downloading FCKeditor extract the archive and upload the fckeditor folder to your webroot, /app/webroot/fckeditor. I know most people might say to put this in /app/vendors but I have a reason. Instead of using App::import to import the /fckeditor/fckeditor.php file I took the contents of that file and created a helper with it. I would have imported the distributed class into the helper to make it more portable, but I wanted to rewrite the CreateHtml function to make it a little more CakePHP friendly.

Here is the Fck Helper.

/app/views/helpers/fck.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?php
class FckHelper extends AppHelper {
    var $InstanceName;
    var $InstanceId;
    var $BasePath;
    var $Width;
    var $Height;
    var $ToolbarSet;
    var $Config;
    var $Value;
    var $Error;

    function __construct() {
        $this->BasePath     = '/fckeditor/';
        $this->Width        = '100%';
        $this->Height       = '400';
        $this->ToolbarSet   = 'Default';
        $this->Config       = array();
    }

    function IsCompatible() {
        if ( isset( $_SERVER ) ) {
            $sAgent = $_SERVER['HTTP_USER_AGENT'] ;
        } else {
            global $HTTP_SERVER_VARS ;
            if ( isset( $HTTP_SERVER_VARS ) ) {
                $sAgent = $HTTP_SERVER_VARS['HTTP_USER_AGENT'] ;
            } else {
                global $HTTP_USER_AGENT ;
                $sAgent = $HTTP_USER_AGENT ;
            }
        }
        if ( strpos($sAgent, 'MSIE') !== false && strpos($sAgent, 'mac') === false && strpos($sAgent, 'Opera') === false ) {
            $iVersion = (float)substr($sAgent, strpos($sAgent, 'MSIE') + 5, 3) ;
            return ($iVersion >= 5.5) ;
        } else if ( strpos($sAgent, 'Gecko/') !== false ) {
            $iVersion = (int)substr($sAgent, strpos($sAgent, 'Gecko/') + 6, 8) ;
            return ($iVersion >= 20030210) ;
        } else if ( strpos($sAgent, 'Opera/') !== false ) {
            $fVersion = (float)substr($sAgent, strpos($sAgent, 'Opera/') + 6, 4) ;
            return ($fVersion >= 9.5) ;
        } else if ( preg_match( "|AppleWebKit/(\d+)|i", $sAgent, $matches ) ) {
            $iVersion = $matches[1] ;
            return ( $matches[1] >= 522 ) ;
        } else return false ;
    }

    function Create($instance) {
        $instance = explode('/', $instance);
        $this->InstanceName = 'data['.$instance[0].']['.$instance[1].']';
        $this->InstanceId   = $instance[0].Inflector::camelize($instance[1]);
        $HtmlValue = htmlspecialchars( $this->Value ) ;
        $Html = '' ;
        if ( $this->IsCompatible() ) {
            if ( isset( $_GET['fcksource'] ) && $_GET['fcksource'] == "true" )
                $File = 'fckeditor.original.html' ;
            else
                $File = 'fckeditor.html' ;
            $Link = "{$this->BasePath}editor/{$File}?InstanceName={$this->InstanceName}";
            if ( $this->ToolbarSet != '' )
                $Link .= "&amp;Toolbar={$this->ToolbarSet}" ;
            $Html .= "<input type=\"hidden\" id=\"{$this-/>InstanceId}\" name=\"{$this->InstanceName}\" value=\"{$HtmlValue}\" style=\"display:none\" />";
            $Html .= "<input type=\"hidden\" id=\"{$this-/>InstanceId}___Config\" value=\"" . $this->GetConfigFieldString() . "\" style=\"display:none\" />";
            $Html .= "<iframe id=\"{$this->InstanceId}___Frame\" src=\"{$Link}\" width=\"{$this->Width}\" height=\"{$this->Height}\" frameborder=\"0\" scrolling=\"no\"></iframe>";
        } else {
            if ( strpos( $this->Width, '%' ) === false )
                $WidthCSS = $this->Width . 'px';
            else
                $WidthCSS = $this->Width;
            if ( strpos( $this->Height, '%' ) === false )
                $HeightCSS = $this->Height . 'px' ;
            else
                $HeightCSS = $this->Height ;
            $Html .= "<textarea name=\"{$this->InstanceName}\" id=\"{$this->InstanceId}\" rows=\"4\" cols=\"40\" style=\"width: {$WidthCSS}; height: {$HeightCSS}\">{$HtmlValue}</textarea>" ;
        }
        if ( !empty( $this->Error ) ) {
            $Html .= '<div class="error-message">'.$this->Error.'</div>';
        }
        return $Html ;
    }

    function GetConfigFieldString() {
        $sParams = '' ;
        $bFirst = true ;
        foreach ( $this->Config as $sKey => $sValue ) {
            if ( $bFirst == false )
                $sParams .= '&amp;' ;
            else
                $bFirst = false ;
            if ( $sValue === true )
                $sParams .= $this->EncodeConfig( $sKey ) . '=true' ;
            else if ( $sValue === false )
                $sParams .= $this->EncodeConfig( $sKey ) . '=false' ;
            else
                $sParams .= $this->EncodeConfig( $sKey ) . '=' . $this->EncodeConfig( $sValue ) ;
        }
        return $sParams ;
    }

    function EncodeConfig( $valueToEncode ) {
        $chars = array(
            '&' => '%26',
            '=' => '%3D',
            '"' => '%22' ) ;
        return strtr( $valueToEncode,  $chars ) ;
    }
}
?>

After you have this helper in place you can enable it by adding it to your $helpers array in whatever controller you are working with. I will be using the content controller from my previous post about a simple cms.

<?php
class ContentController extends AppController {
    var $name = 'Content';
    var $helpers = array('Fck');

After you have that, open your view and pick the field you want to change. Still following the cms example you will want to edit /app/views/content/add.ctp and /app/views/content/edit.ctp like the following.

1
2
3
4
5
6
7
8
// Replace this
echo $form->input('body', array('rows' => '10', 'cols' => '70'));
// With this
if(!empty($this->data['Content']['body']))
    $fck->Value = $this->data['Content']['body'];
if(!empty($this->validationErrors['Content']['body']))
    $fck->Error = $this->validationErrors['Content']['body'];
echo $fck->Create('Content/body');

There are several other configuration options available. You can look at the documentation for more information. Any options available with their php integration method should also be available with this helper.

The only problem with this method so far is there is no non-JavaScript fall back to a plain text area. There is if the browser is not supported. Maybe there is something there that can be expanded on?

For more information on the PHP Integration method please check out the FCKeditor documentation.

JavaScript Integration Method

There are 3 different JavaScript integration methods to choose from. As I mentioned earlier I personally prefer the textarea replacement method with JQuery. So that is the only one I will go over here.

To get started go ahead and download FCKeditor and place it in your webroot just like with the php method above. Then download JQuery and put it in your /app/webroot/js folder. I usually rename the file to jquery.js to make updates easier. Now enable the JavaScript helper in your controller and add the javascript to your view. Using the cms example the add content view would look like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<h2>New Page</h2>
<?php
echo $form->create('Content', array('url' => '/content/add'));
echo '<div>';
echo '  <label for="ContentParentId">Parent</label>';
echo '  <select name="data[Content][parent_id]" id="ContentParentId">';
echo '      <option value="0"'.((@$this->data['Content']['parent_id'] == 0) ? ' selected' : '').'>None</option>';
if(!empty($parents)) {
    foreach($parents as $i) {
        echo '<option value="'.$i['Content']['id'].'" style="padding-left: '.($i['Content']['depth'] + 1).'em;"'.((@$this->data['Content']['parent_id'] == $i['Content']['id']) ? ' selected' : '').'>'.$i['Content']['title'].'</option>';
    }
}
echo '  </select>';
echo '</div>';
echo $form->input('title', array('size' => 60));
echo $form->input('slug', array('size' => '20', 'after' => ' The Slug is the word used for the URL of this page.'));
echo $form->input('body', array('rows' => '10', 'cols' => '70'));
echo '<div><input type="submit" value="Save" /> or '.$html->link('Cancel', array('action' => 'index')).'</div>';
echo '<fieldset>';
echo '  <legend>META Data</legend>';
echo $form->input('keywords', array('size' => '60'));
echo $form->input('description', array('rows' => '3', 'cols' => '65'));
echo '</fieldset>';
echo $form->end();

#FCKeditor stuff
$javascript->link('jquery', false);
$javascript->link('/fckeditor/fckeditor.js', false);
?>
<script type="text/javascript">
    $(document).ready(function() {
        var oFCKeditor = new FCKeditor('ContentBody', '100%', '400', 'Default');
        oFCKeditor.ReplaceTextarea();
    });
</script>

For this to work with everything in your webroot the way it is we also need to add a couple of lines to the bottom of our .htaccess file.

/app/webroot/.haccess

AddType application/x-javascript .js
AddType text/css .css

That should be all you need to do. If you would like more information on the JavaScript integration method please check out the FCKeditor documentation.

Enable The File/Image Upload

First you need to create 4 folders:

  • /app/webroot/files/file
  • /app/webroot/files/flash
  • /app/webroot/files/image
  • /app/webroot/files/media

Each folder need to be writable by the web server.

Now edit /app/webroot/fckeditor/editor/filemanager/connectors/php/config.php and make the following changes for each line (Line numbers may change according to your version).

30
$Config['Enabled'] = false ;
34
$Config['UserFilesPath'] = '/files/' ;

Be sure and set this next one to the full path of your files directory.

40
$Config['UserFilesAbsolutePath'] = '/var/www/mywebsite.com/htdocs/app/webroot/files/' ;

It should now be working. There is also a lot more information on the CakePHP Bakery that could be useful to you.

Comments (23) Trackbacks (1)
  1. I did exactly the things you said (both methods), but still it doesn’t work.

    for the javascript method there is the error: “FCKEditor is not defined”.

    Jquery installed, fckeditor in webroot. everything just the way you said.

    hoping for help…i did spend the whole day with this

    • It looks like I left something out for the JavaScript method. You need to include the fckeditor.js file.. here is an example of how you can do it with jquery after your form:

      <?php
      $javascript->link('/fckeditor/fckeditor.js', false);
      ?>
      <script type="text/javascript">
          $(document).ready(function() {
              var oFCKeditor = new FCKeditor('FieldId', '100%', '400', 'Default');
              oFCKeditor.ReplaceTextarea();
          });
      </script>

      The only thing here you will want to change is possibly the path to your fckeditor.js file and the Id of the textarea field you are replacing in the FCKeditor constructor.

      Hope that helps :-/

  2. Hello Joseph.

    error is gone, but still it doesn’t work.
    cakephp shows the form and everything, but doesn’t show “body textarea”, where I expect the fckeditor to be. first method didn’t worked as well. what do i wrong?
    it’s really frustrating. i am testing it on my local host (mamp/mac os x)…
    further looking for help…

  3. hello again.
    i followed the steps on http://bakery.cakephp.org/articles/view/using-fckeditor-with-cakephp and comment no. 11 helped.

    BUT…still FCKeditor doesn’t work on my local machine (mamp/mac os x). after uploading it to my server it works fine… any clue?

  4. Followed your tutorial line by line. Did your CMS tutorial and replaced those lines… does not work. Instead I get a “cannot find fckeditor.html” message. I copied the whole fckeditor folder into /webroot/ straight from the zip without removing or touching any file. I created the helper. Added the reference to the helper. Line by line. Did it 3 times.. same result. =(

    Will try javascript method.

  5. after doing some tinkering with the code I found that the helper only works if youre working on a project that’s on the ROOT folder of the directory meaning if im working in localhost/myproject I have to replace

    $this->BasePath = ‘/fckeditor/’;
    with $this->BasePath = ‘myproject/fckeditor/’;

    Thanks for the awesome tutorial! hope this helps someone.

  6. Thanks Leo. Your comment really helped me. Firstly, when I try your suggestion I still found error. Then I changed the code with this

    $this->BasePath = ‘../myproject/fckeditor/’;

    and it worked! :)

    FYI, I was using XAMPP and Windows XP. Thanks also for this tutorial :)

  7. Thank you so much for this tutorial – I wasted a whole evening trying 3 other methods – none of which worked.

    You’re a star!! :)

  8. thx a lot, for the script but i have an error when i try PHP Integration Method, the add.ctp work very good but edit.ctp don’t work!

    link for see error:
    http://www.cakephp-it.org/topic278-problema-con-fckeditor.html

  9. Hi Joseph, thank you!!
    But i have the same problem with edit.ctp

    Thanks again

  10. Hi again Joseph, but i fix it, i forgto to include the id field in edit ctp


Leave a comment

(required)