CKFinder Drupal Integration

drupal-banner-tiny

Replacing TinyMCE with CKEditor and CKFinder.

TinyMCEis a popular WYSIWYG editor I used by default on a few sites. However, it’s file/image uploader is very buggy and didn’t work well.

CKEditor is more actively developed and has a comparable file/image uploader.

CKEditor Install

There is a dedicated Drupal module for the CKEditor. However, the recommended method is to use the WYSIWYG module.

CKFinder Install

Unfortunately, CKFinder integration with CKEditor is not an easy process. Several core files need to be modified during this process.

Download and extract CKFinder into ~sites/all/libraries.

Modify drupal sites/default/settings.php

In order for CKFinder to work, it needs to verify that it’s running on the same domain it’s configured for. To help it out, we need to set a site cookie in the drupal config file:

vi sites/default/settings.php

$cookie_domain = 'internal.gemini.edu';</pre>
<h4>Modify <kbd>ckfinder/config.php</kbd></h4>
Add the following to override the default authentication mechanism.
<pre>//http://drupal.org/node/648038
function CheckAuthentication()
{
    //WARNING : DO NOT simply return "true". By doing so, you are allowing
    //"anyone" to upload and list the files in your server. You must implement
    //some kind of session validation here. Even something very simple as...

    // return isset($_SESSION['IsAuthorized']) &amp;&amp; $_SESSION['IsAuthorized'];

    //... where $_SESSION['IsAuthorized'] is set to "true" as soon as the
    //user logs in your system.

    static $authenticated;

    if (!isset($authenticated) &amp;&amp; empty($authenticated) == TRUE || $authenticate
d == FALSE) {
        $drupal_path = "../../../../";
        if(!file_exists($drupal_path . "/includes/bootstrap.inc")) {
            $drupal_path = "../../..";
            do {
                $drupal_path .= "/..";
                $depth = substr_count($drupal_path, "..");
            }
            while(!($bootstrapFileFound = file_exists($drupal_path . "/includes/
bootstrap.inc")) &amp;&amp; $depthsession)) {
                    $user-&gt;session = array();
                }
                if (is_null($user-&gt;session['authenticated'])) {
                    $user-&gt;session['authenticated'] = '';
                }

                if (is_array($user-&gt;roles) &amp;&amp; is_null($user-&gt;roles[1])) {
                    $user-&gt;session['authenticated'] = true;
                } else {
                    $user-&gt;session['authenticated'] = false;
                }

                $authenticated = $user-&gt;session['authenticated'];

            }

        chdir($cwd);
    }

    return $authenticated;
}

CheckAuthentication();

We now need to tell CKFinder where our DocRoot is. Find all instances of the following and replace:

$baseUrl = '/';
$baseDir = '/export/web/docs/internal/html/';</pre>
<h4>Modify WYSIWYG module ckeditor file</h4>
Modify the WYSIWYG editor to include CKFinder integration.

The following lines must be included:
<pre>// Attach editor.
editor = CKEDITOR.replace(params.field, settings);
CKFinder.setupCKEditor( editor, '/sites/all/libraries/ckfinder/' );

The following is the code section that needs to be modified:

vi wysiwyg/editors/js/ckeditor-3.0.js

/**
 * Attach this editor to a target element.
 */
Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
  // Apply editor instance settings.
  CKEDITOR.config.customConfig = '';

  settings.on = {
    instanceReady: function(ev) {
      var editor = ev.editor;
      // Get a list of block, list and table tags from CKEditor's XHTML DTD.
      // @see <a title="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Output_Formatting. " href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Output_Formatting. " rel="nofollow">http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Output_Formatting. </a>var dtd = CKEDITOR.dtd;
      var tags = CKEDITOR.tools.extend({}, dtd.$block, dtd.$listItem, dtd.$tableContent);
      // Set source formatting rules for each listed tag except</pre>
<pre>.
      // Linebreaks can be inserted before or after opening and closing tags.
      if (settings.apply_source_formatting) {
        // Mimic FCKeditor output, by breaking lines between tags.
        for (var tag in tags) {
          if (tag == 'pre') {
            continue;
          }
          this.dataProcessor.writer.setRules(tag, {
            indent: true,
            breakBeforeOpen: true,
            breakAfterOpen: false,
            breakBeforeClose: false,
            breakAfterClose: true
          });
        }
      }
      else {
        // No indents or linebreaks;
        for (var tag in tags) {
          if (tag == 'pre') {
            continue;
          }
          this.dataProcessor.writer.setRules(tag, {
            indent: false,
            breakBeforeOpen: false,
            breakAfterOpen: false,
            breakBeforeClose: false,
            breakAfterClose: false
          });
        }
      }
    },

    pluginsLoaded: function(ev) {
      // Override the conversion methods to let Drupal plugins modify the data.
      var editor = ev.editor;
      if (editor.dataProcessor &amp;&amp; Drupal.settings.wysiwyg.plugins[params.format]) {
        editor.dataProcessor.toHtml = CKEDITOR.tools.override(editor.dataProcessor.toHtml, function(originalToHtml) {
          // Convert raw data for display in WYSIWYG mode.
          return function(data, fixForBody) {
            for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
              if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
                data = Drupal.wysiwyg.plugins[plugin].attach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
                data = Drupal.wysiwyg.instances[params.field].prepareContent(data);
              }
            }
            return originalToHtml.call(this, data, fixForBody);
          };
        });
        editor.dataProcessor.toDataFormat = CKEDITOR.tools.override(editor.dataProcessor.toDataFormat, function(originalToDataFormat) {
          // Convert WYSIWYG mode content to raw data.
          return function(data, fixForBody) {
            data = originalToDataFormat.call(this, data, fixForBody);
            for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
              if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
                data = Drupal.wysiwyg.plugins[plugin].detach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
              }
            }
            return data;
          };
        });
      }
    },
    focus: function(ev) {
      Drupal.wysiwyg.activeId = ev.editor.name;
    }
  };

  // Attach editor.
  editor = CKEDITOR.replace(params.field, settings);
  CKFinder.setupCKEditor( editor, '/sites/all/libraries/ckfinder/' );

};

Create Drupal module for CKFinder

We need to include the CKFinder javascript init file into every Drupal page.

I’ve written a Drupal module that does just this. You find it in subversion here:

svn+ssh://root@hbfsvn1.hi.gemini.edu/var/svn/apps/gem_ckfinder/branches/1.0

Resources

Original Drupal integration posting:
http://drupal.org/node/648038

CKFinder javascript api change which resulted in an error because the setupCKEditor method is now starts with a lowercase ‘s’:
http://cksource.com/blog/CKEditor_for_jQuery