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 .= "&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 .= '&' ; 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.
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 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.
February 23rd, 2009 - 09:51
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
February 23rd, 2009 - 11:41
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:
$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 :-/
February 24th, 2009 - 11:15
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…
February 24th, 2009 - 13:16
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?
February 24th, 2009 - 23:28
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.
February 24th, 2009 - 23:55
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.
March 18th, 2009 - 06:08
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
May 29th, 2009 - 03:40
Thank you so much for this tutorial – I wasted a whole evening trying 3 other methods – none of which worked.
You’re a star!!
July 6th, 2009 - 12:13
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
July 31st, 2009 - 17:32
Hi Joseph, thank you!!
But i have the same problem with edit.ctp
Thanks again
July 31st, 2009 - 17:43
Hi again Joseph, but i fix it, i forgto to include the id field in edit ctp