You are here

ckeditor.lib.inc in CKEditor - WYSIWYG HTML editor 6

Same filename and directory in other branches
  1. 7 includes/ckeditor.lib.inc

CKEditor - The text editor for the Internet - http://ckeditor.com Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.

== BEGIN LICENSE ==

Licensed under the terms of any of the following licenses of your choice:

== END LICENSE ==

CKEditor Module for Drupal 6.x

This module allows Drupal to replace textarea fields with CKEditor.

CKEditor is an online rich text editor that can be embedded inside web pages. It is a WYSIWYG (What You See Is What You Get) editor which means that the text edited in it looks as similar as possible to the results end users will see after the document gets published. It brings to the Web popular editing features found in desktop word processors such as Microsoft Word and OpenOffice.org Writer. CKEditor is truly lightweight and does not require any kind of installation on the client computer.

File

includes/ckeditor.lib.inc
View source
<?php

/**
 * CKEditor - The text editor for the Internet - http://ckeditor.com
 * Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
 *
 * == BEGIN LICENSE ==
 *
 * Licensed under the terms of any of the following licenses of your
 * choice:
 *
 *  - GNU General Public License Version 2 or later (the "GPL")
 *    http://www.gnu.org/licenses/gpl.html
 *
 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 *    http://www.gnu.org/licenses/lgpl.html
 *
 *  - Mozilla Public License Version 1.1 or later (the "MPL")
 *    http://www.mozilla.org/MPL/MPL-1.1.html
 *
 * == END LICENSE ==
 *
 * @file
 * CKEditor Module for Drupal 6.x
 *
 * This module allows Drupal to replace textarea fields with CKEditor.
 *
 * CKEditor is an online rich text editor that can be embedded inside web pages.
 * It is a WYSIWYG (What You See Is What You Get) editor which means that the
 * text edited in it looks as similar as possible to the results end users will
 * see after the document gets published. It brings to the Web popular editing
 * features found in desktop word processors such as Microsoft Word and
 * OpenOffice.org Writer. CKEditor is truly lightweight and does not require any
 * kind of installation on the client computer.
 */

/**
 * Guess the absolute server path to the document root
 * Usually it should return $_SERVER['DOCUMENT_ROOT']
 *
 * @todo Improve me!!
 * Returns absolute path or false on failure
 *
 * @return string|boolean
 */
function ckeditor_get_document_root_full_path() {
  $found = 0;
  $index_dir = realpath(dirname($_SERVER['SCRIPT_FILENAME']));

  // {/dir1/dir2/home/drupal}/index.php
  if (getcwd() == $index_dir) {
    $found++;
  }
  $drupal_dir = base_path();
  $index_dir = str_replace('\\', "/", $index_dir);
  $drupal_dir = str_replace('\\', "/", $drupal_dir);
  $document_root_dir = $index_dir . '/' . str_repeat('../', substr_count($drupal_dir, '/') - 1);
  $document_root_dir = realpath($document_root_dir);
  $document_root_dir = rtrim($document_root_dir, '/\\');
  if ($document_root_dir == $_SERVER['DOCUMENT_ROOT']) {
    $found++;
  }
  $document_root_dir = str_replace('\\', '/', $document_root_dir);
  if ($document_root_dir != '') {
    $found++;
  }
  if (file_exists($document_root_dir)) {
    $found++;
  }
  if (file_exists($document_root_dir . base_path() . 'includes/bootstrap.inc')) {
    $found++;
  }
  if ($found >= 3) {
    return $document_root_dir;
  }
  else {
    return FALSE;
  }
}

/**
 * Emulates the asp Server.mapPath function.
 * Given an url path return the physical directory that it corresponds to.
 *
 * Returns absolute path or false on failure
 *
 * @param string $path
 * @return @return string|boolean
 */
function ckeditor_resolve_url($path) {
  if (function_exists('apache_lookup_uri')) {
    $info = @apache_lookup_uri($path);
    if (!$info) {
      return FALSE;
    }
    return $info->filename . $info->path_info;
  }
  $document_root = ckeditor_get_document_root_full_path();
  if ($document_root !== FALSE) {
    return $document_root . $path;
  }
  return FALSE;
}
function ckeditor_load_skin_options() {
  $arr = array();
  $editor_local_path = ckeditor_path(TRUE);
  $skin_dir = $editor_local_path . '/skins';
  if (is_dir($skin_dir)) {
    $dh = @opendir($skin_dir);
    if (FALSE !== $dh) {
      while (($file = readdir($dh)) !== FALSE) {
        if (in_array($file, array(
          ".",
          "..",
          "CVS",
          ".svn",
        ))) {
          continue;
        }
        if (is_dir($skin_dir . DIRECTORY_SEPARATOR . $file)) {
          $arr[$file] = drupal_ucfirst($file);
        }
      }
      closedir($dh);
    }
  }

  //oops, we have no information about skins, let's use only default
  if (empty($arr)) {
    $arr = array(
      'kama' => 'Kama',
    );
  }
  asort($arr);
  return $arr;
}

/**
 * Return default skin for CKEditor
 *
 * @return string
 */
function ckeditor_default_skin() {
  $skin_options = ckeditor_load_skin_options();
  if (array_key_exists('moono', $skin_options)) {
    return 'moono';
  }
  if (array_key_exists('kama', $skin_options)) {
    return 'kama';
  }

  //if any default theme not exists select first from the list
  return key(reset($skin_options));
}
function ckeditor_load_lang_options() {
  $arr = array();
  $editor_local_path = ckeditor_path(TRUE);
  $lang_file = $editor_local_path . '/lang/_languages.js';
  if (file_exists($lang_file)) {
    $f = fopen($lang_file, 'r');
    $file = fread($f, filesize($lang_file));
    $tmp = explode('{', $file);
    if (isset($tmp[2])) {
      $tmp = explode('}', $tmp[2]);
    }
    $langs = explode(',', $tmp[0]);
    foreach ($langs as $lang) {
      preg_match("/'?(\\w+-?\\w+)'?:'([\\w\\s\\(\\)]+)'/i", $lang, $matches);
      if (isset($matches[1]) && isset($matches[2])) {
        $arr[$matches[1]] = $matches[2];
      }
    }
  }

  //oops, we have no information about languages, let's use those available in CKEditor 2.4.3
  if (empty($arr)) {
    $arr = array(
      'af' => 'Afrikaans',
      'ar' => 'Arabic',
      'bg' => 'Bulgarian',
      'bn' => 'Bengali/Bangla',
      'bs' => 'Bosnian',
      'ca' => 'Catalan',
      'cs' => 'Czech',
      'da' => 'Danish',
      'de' => 'German',
      'el' => 'Greek',
      'en' => 'English',
      'en-au' => 'English (Australia)',
      'en-ca' => 'English (Canadian)',
      'en-uk' => 'English (United Kingdom)',
      'eo' => 'Esperanto',
      'es' => 'Spanish',
      'et' => 'Estonian',
      'eu' => 'Basque',
      'fa' => 'Persian',
      'fi' => 'Finnish',
      'fo' => 'Faroese',
      'fr' => 'French',
      'gl' => 'Galician',
      'he' => 'Hebrew',
      'hi' => 'Hindi',
      'hr' => 'Croatian',
      'hu' => 'Hungarian',
      'it' => 'Italian',
      'ja' => 'Japanese',
      'km' => 'Khmer',
      'ko' => 'Korean',
      'lt' => 'Lithuanian',
      'lv' => 'Latvian',
      'mn' => 'Mongolian',
      'ms' => 'Malay',
      'nb' => 'Norwegian Bokmal',
      'nl' => 'Dutch',
      'no' => 'Norwegian',
      'pl' => 'Polish',
      'pt' => 'Portuguese (Portugal)',
      'pt-br' => 'Portuguese (Brazil)',
      'ro' => 'Romanian',
      'ru' => 'Russian',
      'sk' => 'Slovak',
      'sl' => 'Slovenian',
      'sr' => 'Serbian (Cyrillic)',
      'sr-latn' => 'Serbian (Latin)',
      'sv' => 'Swedish',
      'th' => 'Thai',
      'tr' => 'Turkish',
      'uk' => 'Ukrainian',
      'vi' => 'Vietnamese',
      'zh' => 'Chinese Traditional',
      'zh-cn' => 'Chinese Simplified',
    );
  }
  asort($arr);
  return $arr;
}

/**
 * List of CKEditor plugins
 *
 * @return array
 */
function ckeditor_load_plugins($render = FALSE) {
  $arr = array();
  $base_path = '%base_path%';
  $editor_path = '%editor_path%';
  $ckeditor_path = '%ckeditor_path%';
  $plugin_dir = '%plugin_dir%';
  $plugin_dir_additional = '%plugin_dir_extra%';
  $pattern = '#\\.addButton\\([\\s]*[\'"](.*?)[\'"][\\s]*\\,[\\s]*\\{[\\s]*(.*?)[\\s]*\\}#s';

  /*
   * External plugins
   */
  if (module_exists('ckeditor_swf') && file_exists(drupal_get_path('module', 'ckeditor_swf') . '/plugins/swf/plugin.js')) {
    $arr['ckeditor_swf'] = array(
      'name' => 'swf',
      'desc' => t('Support for the CKEditor SWF module'),
      'path' => $base_path . drupal_get_path('module', 'ckeditor_swf') . '/plugins/swf/',
      'buttons' => FALSE,
      'default' => 't',
    );
  }
  if (module_exists('ckeditor_link') && file_exists(drupal_get_path('module', 'ckeditor_link') . '/plugins/link/plugin.js')) {
    $arr['ckeditor_link'] = array(
      'name' => 'drupal_path',
      'desc' => t('Support for the CKEditor Link module'),
      'path' => $base_path . drupal_get_path('module', 'ckeditor_link') . '/plugins/link/',
      'buttons' => FALSE,
      'default' => 't',
    );
  }
  if (module_exists('linkit') && file_exists(drupal_get_path('module', 'linkit') . '/editors/ckeditor/plugin.js')) {
    $arr['linkit'] = array(
      'name' => 'Linkit',
      'desc' => t('Support for the Linkit module <em>(buttons: Linkit)</em>'),
      'path' => $base_path . drupal_get_path('module', 'linkit') . '/editors/ckeditor/',
      'buttons' => array(
        'Linkit' => array(
          'title' => 'Linkit',
          'icon' => $base_path . drupal_get_path('module', 'linkit') . '/editors/ckeditor/linkit.png',
        ),
      ),
      'default' => 't',
    );
  }

  /*
   * CKEditor build-in plugins
   */
  $_editor_path = ckeditor_path(TRUE) . '/';
  if (file_exists($_editor_path . 'plugins/tableresize/plugin.js')) {
    $arr['tableresize'] = array(
      'name' => 'tableresize',
      'desc' => t('Table Resize plugin'),
      'path' => $base_path . $editor_path . 'plugins/tableresize/',
      'buttons' => FALSE,
      'default' => 't',
    );
  }
  if (file_exists($_editor_path . 'plugins/autogrow/plugin.js')) {
    $arr['autogrow'] = array(
      'name' => 'autogrow',
      'desc' => t('Auto Grow plugin'),
      'path' => $base_path . $editor_path . 'plugins/autogrow/',
      'buttons' => FALSE,
      'default' => 'f',
    );
  }
  if (file_exists($_editor_path . 'plugins/stylesheetparser/plugin.js')) {
    $arr['stylesheetparser'] = array(
      'name' => 'stylesheetparser',
      'desc' => t('Stylesheet Parser plugin'),
      'path' => $base_path . $editor_path . 'plugins/stylesheetparser/',
      'buttons' => FALSE,
      'default' => 'f',
    );
  }

  /*
   * CKEditor module plugins
   */
  $_plugin_dir = drupal_get_path('module', 'ckeditor') . '/plugins/';
  if (is_dir($_plugin_dir) && ($handle = opendir($_plugin_dir))) {
    while (false !== ($file = readdir($handle))) {
      if (is_dir($_plugin_dir . $file) && file_exists($_plugin_dir . $file . '/plugin.js')) {
        $source = file_get_contents($_plugin_dir . $file . '/plugin.js');
        $buttons = array();
        if (preg_match_all($pattern, $source, $matches)) {
          foreach ($matches[1] as $i => $button_name) {
            if (preg_match('#(icon)[\\s]*\\:[\\s]*([^\\,\\n]*)#', $matches[2][$i], $matches2)) {
              $buttons[$button_name] = array();
              $buttons[$button_name]['label'] = $button_name;
              $matches2[2] = str_replace(array(
                'this.path',
                '+',
                '\'',
                '"',
              ), array(
                '',
                '',
                '',
                '',
              ), $matches2[2]);
              $buttons[$button_name]['icon'] = trim($matches2[2]);
            }
          }
        }
        if (preg_match('#@file ([^\\n\\r]*)#', $source, $matches)) {
          $arr[$file] = array(
            'name' => $file,
            'desc' => t($matches[1]),
            'path' => $base_path . $plugin_dir . $file . '/',
            'buttons' => count($buttons) > 0 ? $buttons : FALSE,
            'default' => 'f',
          );
        }
        else {
          $arr[$file] = array(
            'name' => $file,
            'desc' => t('Plugin file: ' . $file),
            'path' => $base_path . $plugin_dir . $file . '/',
            'buttons' => count($buttons) > 0 ? $buttons : FALSE,
            'default' => 'f',
          );
        }
      }
    }
    closedir($handle);
  }

  /*
   * CKEditor module plugins - additional directory
   */
  $_plugin_dir_additional = ckeditor_plugins_path(TRUE) . '/';
  if ($_plugin_dir != $_plugin_dir_additional && is_dir($_plugin_dir_additional) && ($handle = opendir($_plugin_dir_additional))) {
    while (false !== ($file = readdir($handle))) {
      if (is_dir($_plugin_dir_additional . $file) && file_exists($_plugin_dir_additional . $file . '/plugin.js')) {
        $source = file_get_contents($_plugin_dir_additional . $file . '/plugin.js');
        $buttons = array();
        if (preg_match_all($pattern, $source, $matches)) {
          foreach ($matches[1] as $i => $button_name) {
            if (preg_match('#(icon)[\\s]*\\:[\\s]*([^\\,\\n]*)#', $matches[2][$i], $matches2)) {
              $buttons[$button_name] = array();
              $buttons[$button_name]['label'] = $button_name;
              $matches2[2] = str_replace(array(
                'this.path',
                '+',
                '\'',
                '"',
              ), array(
                '',
                '',
                '',
                '',
              ), $matches2[2]);
              $buttons[$button_name]['icon'] = trim($matches2[2]);
            }
          }
        }
        if (preg_match('#@file ([^\\n\\r]*)#', $source, $matches)) {
          $arr[$file] = array(
            'name' => $file,
            'desc' => t($matches[1]),
            'path' => $base_path . $plugin_dir_additional . $file . '/',
            'buttons' => count($buttons) > 0 ? $buttons : FALSE,
            'default' => 'f',
          );
        }
        else {
          $arr[$file] = array(
            'name' => $file,
            'desc' => t('Plugin file: ' . $file),
            'path' => $base_path . $plugin_dir_additional . $file . '/',
            'buttons' => count($buttons) > 0 ? $buttons : FALSE,
            'default' => 'f',
          );
        }
      }
    }
    closedir($handle);
  }

  /*
   * CKEditor plugins registered by hook
   */
  $plugins = module_invoke_all('ckeditor_plugin');
  foreach ($plugins as $i => $plugin) {
    if (file_exists($plugin['path'] . 'plugin.js')) {
      $source = file_get_contents($plugin['path'] . 'plugin.js');
      $plugins[$i]['path'] = $base_path . $plugin['path'];
      if (!isset($plugin['buttons']) || count($plugin['buttons']) == 0) {
        $buttons = array();
        if (preg_match_all($pattern, $source, $matches)) {
          foreach ($matches[1] as $j => $button_name) {
            if (preg_match('#(icon)[\\s]*\\:[\\s]*([^\\,\\n]*)#', $matches[2][$j], $matches2)) {
              $buttons[$button_name] = array();
              $buttons[$button_name]['label'] = $button_name;
              $matches2[2] = str_replace(array(
                'this.path',
                '+',
                '\'',
                '"',
              ), array(
                '',
                '',
                '',
                '',
              ), $matches2[2]);
              $buttons[$button_name]['icon'] = trim($matches2[2]);
            }
          }
        }
        $plugins[$i]['buttons'] = count($buttons) > 0 ? $buttons : FALSE;
      }
    }
    else {
      unset($plugins[$i]);
    }
  }
  $arr = array_merge($arr, $plugins);
  if (isset($arr['linktomenu']) && module_exists('linktocontent') == FALSE) {
    unset($arr['linktomenu']);
  }
  if (isset($arr['linktonode']) && module_exists('linktocontent') == FALSE) {
    unset($arr['linktonode']);
  }
  if (isset($arr['imce']) && module_exists('imce') == FALSE) {
    unset($arr['imce']);
  }

  //remove page break button if there is no module to do this
  if (isset($arr['drupalbreaks']['buttons']['DrupalPageBreak']) && !module_exists('paging') && !module_exists('pagebreak')) {
    unset($arr['drupalbreaks']['buttons']['DrupalPageBreak']);
  }
  if (isset($arr['drupalbreaks'])) {
    $arr['drupalbreaks']['default'] = 't';
  }
  ksort($arr);
  if ($render === TRUE) {
    $arr = ckeditor_plugins_render($arr);
  }
  return $arr;
}

/**
 * Render CKEditor plugins path
 */
function ckeditor_plugins_render($plugins) {
  $render = array();
  $render["%base_path%"] = base_path();
  $render["%editor_path%"] = ckeditor_path(TRUE) . '/';
  $render["%ckeditor_path%"] = drupal_get_path('module', 'ckeditor') . '/';
  $render["%plugin_dir%"] = $render["%ckeditor_path%"] . 'plugins/';
  $render["%plugin_dir_extra%"] = ckeditor_plugins_path(TRUE) . '/';
  foreach ((array) $plugins as $i => $plugin) {
    $plugins[$i]['path'] = str_replace(array_keys($render), array_values($render), $plugin['path']);
  }
  return $plugins;
}
function ckeditor_user_get_setting($user, $profile, $setting) {
  $default = array(
    'default' => 't',
    'show_toggle' => 't',
    'popup' => 'f',
    'toolbar' => 'default',
    'width' => '100%',
    'lang' => 'en',
    'auto_lang' => 't',
  );
  if ($profile->settings['allow_user_conf']) {
    $status = isset($user->{'ckeditor_' . $setting}) ? $user->{'ckeditor_' . $setting} : (isset($profile->settings[$setting]) ? $profile->settings[$setting] : $default[$setting]);
  }
  else {
    $status = isset($profile->settings[$setting]) ? $profile->settings[$setting] : $default[$setting];
  }
  return $status;
}
function ckeditor_user_get_profile($user, $element_id = 'edit-body', $url = NULL) {
  $rids = array();
  if (is_null($url)) {
    $url = $_GET['q'];
  }

  // Since ckeditor_profile_load() makes a db hit, only call it when we're pretty sure
  // we're gonna render ckeditor.
  $sorted_roles = ckeditor_sorted_roles();
  foreach (array_keys($sorted_roles) as $rid) {
    if (isset($user->roles[$rid])) {
      $rids[] = $rid;
    }
  }
  if ($user->uid == 1 && !sizeof($rids)) {
    $r = db_fetch_object(db_query_range("SELECT r.rid FROM {ckeditor_role} r ORDER BY r.rid DESC", 1));
    $rids[] = $r->rid;
  }
  $profile_names = array();
  if (sizeof($rids)) {
    $result = db_query("SELECT r.rid, s.name FROM {ckeditor_settings} s INNER JOIN {ckeditor_role} r ON r.name = s.name WHERE r.rid IN (" . implode(",", $rids) . ")");
    while ($row = db_fetch_array($result)) {
      if (!isset($profile_names[$row['rid']])) {
        $profile_names[$row['rid']] = array();
      }
      array_push($profile_names[$row['rid']], $row['name']);
    }
  }
  foreach ($rids as $rid) {
    if (!empty($profile_names[$rid])) {
      foreach ($profile_names[$rid] as $profile_name) {
        $profile = ckeditor_profile_load($profile_name);
        $conf = $profile->settings;
        $enabled = ckeditor_is_enabled(empty($conf['excl_mode']) ? '0' : $conf['excl_mode'], empty($conf['excl_regex']) ? '' : $conf['excl_regex'], $element_id, $url);
        if ($enabled) {
          return $profile;
        }
      }
    }
  }
  return FALSE;
}

/**
 * sort roles according to precedence settings. previously sorted roles are followed by latest added roles.
 */
function ckeditor_sorted_roles($clear = FALSE) {
  static $order;
  if (isset($order) && $clear !== TRUE) {
    return $order;
  }
  $order = array();
  $roles = user_roles(0, 'access ckeditor');
  $result = db_query("SELECT settings FROM {ckeditor_settings} WHERE name='CKEditor Global Profile'");
  $data = db_fetch_object($result);
  if (!empty($data->settings)) {
    $settings = unserialize($data->settings);
    if (isset($settings['rank']) && !empty($settings['rank'])) {
      foreach ($settings['rank'] as $rid) {
        if (isset($roles[$rid])) {
          $order[$rid] = $roles[$rid];
          unset($roles[$rid]);
        }
      }
    }
  }
  krsort($roles);

  //sort the remaining unsorted roles by id, descending.
  $order += $roles;
  return $order;
}

/**
 * @param int $excl_mode 1/include, exclude otherwise
 * @param string $excl_regex paths (drupal paths with ids attached)
 * @param string $element_id current ID
 * @param string $get_q current path
 *
 * @return boolean
 *    returns true if CKEditor is enabled
 */
function ckeditor_is_enabled($excl_mode, $excl_regex, $element_id, $get_q) {
  global $theme;
  $front = variable_get('site_frontpage', 'node');
  $excl_regex = str_replace('<front>', $front, $excl_regex);
  $nodetype = ckeditor_get_nodetype($get_q);
  $element_id = str_replace('.', '\\.', $element_id);
  $match = !empty($excl_regex) && preg_match($excl_regex, $theme . ':' . $nodetype . '@' . $get_q . '.' . $element_id);
  return $excl_mode == '0' xor $match;
}
function _ckeditor_script_path() {
  $jspath = FALSE;
  $module_path = drupal_get_path('module', 'ckeditor');
  if (file_exists($module_path . '/ckeditor/ckeditor.js')) {
    $jspath = '%m/ckeditor';
  }
  elseif (file_exists($module_path . '/ckeditor/ckeditor/ckeditor.js')) {
    $jspath = '%m/ckeditor/ckeditor';
  }
  elseif (file_exists('sites/all/libraries/ckeditor/ckeditor.js')) {
    $jspath = '%b/sites/all/libraries/ckeditor';
  }
  return $jspath;
}

/**
 * Determines whether the CKEditor sources are present
 *
 * It checks if ckeditor.js is present.
 *
 * This function is used by ckeditor_requirements()
 *
 * @return boolean True if CKEditor is installed
 */
function _ckeditor_requirements_isinstalled() {
  $editor_path = ckeditor_path(TRUE);
  $jspath = $editor_path . '/ckeditor.js';
  $jsp = file_exists($jspath);
  if (!$jsp && ($editor_path = _ckeditor_script_path())) {
    $result = db_query("SELECT name, settings FROM {ckeditor_settings} WHERE name = 'CKEditor Global Profile'");
    if ($rs = db_fetch_array($result)) {
      $rs['settings'] = unserialize($rs['settings']);
      $rs['settings']['ckeditor_path'] = $editor_path;
      $rs['settings'] = serialize($rs['settings']);
      db_query("UPDATE {ckeditor_settings} SET settings='%s' WHERE name = 'CKEditor Global Profile'", $rs['settings']);
      $jsp = TRUE;
      ckeditor_path(TRUE, TRUE);
    }
  }
  return $jsp;
}

Functions

Namesort descending Description
ckeditor_default_skin Return default skin for CKEditor
ckeditor_get_document_root_full_path Guess the absolute server path to the document root Usually it should return $_SERVER['DOCUMENT_ROOT']
ckeditor_is_enabled
ckeditor_load_lang_options
ckeditor_load_plugins List of CKEditor plugins
ckeditor_load_skin_options
ckeditor_plugins_render Render CKEditor plugins path
ckeditor_resolve_url Emulates the asp Server.mapPath function. Given an url path return the physical directory that it corresponds to.
ckeditor_sorted_roles sort roles according to precedence settings. previously sorted roles are followed by latest added roles.
ckeditor_user_get_profile
ckeditor_user_get_setting
_ckeditor_requirements_isinstalled Determines whether the CKEditor sources are present
_ckeditor_script_path