You are here

fckeditor.module in FCKeditor - WYSIWYG HTML editor 6.2

Same filename and directory in other branches
  1. 5.2 fckeditor.module
  2. 5 fckeditor.module
  3. 6 fckeditor.module

FCKeditor - The text editor for Internet - http://www.fckeditor.net Copyright (C) 2003-2008 Frederico Caldeira Knabben

== BEGIN LICENSE ==

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

== END LICENSE ==

FCKeditor Module for Drupal 6.x

This module allows Drupal to replace textarea fields with FCKeditor.

This HTML text editor brings to the web many of the powerful functionalities of known desktop editors like Word. It's really lightweight and doesn't require any kind of installation on the client computer.

File

fckeditor.module
View source
<?php

/**
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
 * Copyright (C) 2003-2008 Frederico Caldeira Knabben
 *
 * == BEGIN LICENSE ==
 *
 * Licensed under the terms of any of the following licenses at 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
 * FCKeditor Module for Drupal 6.x
 *
 * This module allows Drupal to replace textarea fields with FCKeditor.
 *
 * This HTML text editor brings to the web many of the powerful functionalities
 * of known desktop editors like Word. It's really  lightweight and doesn't
 * require any kind of installation on the client computer.
 */

/**
 * The name of simplified toolbar which should be forced
 * Be sure that this toolbar is defined in fckeditor.config.js or fckconfig.js
 */
define('FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME', 'DrupalBasic');
global $_fckeditor_configuration;
global $_fckeditor_js_ids;
$_fckeditor_configuration = array();
$_fckeditor_js_ids = array();
function _fckeditor_ckfinder_path() {
  return drupal_get_path('module', 'fckeditor') . '/ckfinder/ckfinder.php';
}

/**
 * Implementation of hook_help().
 *
 * This function delegates execution to fckeditor_help_delegate() in fckeditor.help.inc to
 * lower the amount of code in fckeditor.module
 */
function fckeditor_help($path, $arg) {
  module_load_include('help.inc', 'fckeditor');
  return module_invoke('fckeditor', 'help_delegate', $path, $arg);
}

/**
 * Implementation of hook_user().
 *
 * This function delegates execution to fckeditor_user_delegate() in fckeditor.user.inc to
 * lower the amount of code in fckeditor.module
 */
function fckeditor_user($type, $edit, &$user, $category = NULL) {
  if ($type == 'form' && $category == 'account' && user_access('access fckeditor') || $type == 'validate') {
    module_load_include('user.inc', 'fckeditor');
    return fckeditor_user_delegate($type, $edit, $user, $category);
  }
  return NULL;
}

/*
 *  Run if there is old menu information in database
 */
function fckeditor_admin($arg = NULL) {
  drupal_set_message(t('The FCKeditor module is not installed correctly. You should run the !updatephplink immediately.', array(
    '!updatephplink' => l(t('database update script'), 'update.php'),
  )), 'error');
  return FALSE;
}

/**
 * Implementation of hook_perm().
 * Administer -> User management -> Permissions
 */
function fckeditor_perm() {
  return array(
    'administer fckeditor',
    'access fckeditor',
    'allow fckeditor file uploads',
  );
}

/**
 * Implementation of hook_elements().
 * Replace textarea with FCKeditor using callback function (fckeditor_process_textarea)
 */
function fckeditor_elements() {
  $type = array();
  $type['textfield'] = array(
    '#process' => array(
      'fckeditor_process_input',
    ),
  );
  if (user_access('access fckeditor')) {

    // only roles with permission get the fckeditor
    if (fckeditor_is_compatible_client()) {

      // it would be useless to dig deeper if we're not able or allowed to
      $type['textarea'] = array(
        '#process' => array(
          'fckeditor_process_textarea',
        ),
      );
      $type['form'] = array(
        '#after_build' => array(
          'fckeditor_process_form',
        ),
      );
    }
  }
  return $type;
}

/**
 * AJAX callback - XSS filter
 */
function fckeditor_filter_xss() {
  global $user;
  header('Content-Type: text/plain; charset=utf-8');
  $GLOBALS['devel_shutdown'] = FALSE;
  if (!isset($_POST['text']) || !is_string($_POST['text']) || !isset($_POST['token']) || !drupal_valid_token($_POST['token'], 'fckeditorAjaxCall', FALSE)) {
    exit;
  }
  $profile = fckeditor_user_get_profile($user);
  if ($profile == FALSE) {
    exit;
  }
  $filter_format_id = filter_resolve_format((int) $_POST['input_format']);
  if (!filter_access($filter_format_id)) {
    exit;
  }
  $settings = $profile->settings;
  $text = $_POST['text'];
  $text = strtr($text, array(
    '<!--' => '__COMMENT__START__',
    '-->' => '__COMMENT__END__',
  ));
  foreach ($settings['filters'] as $module_delta => $active) {
    $module = strtok($module_delta, "/");
    $delta = strtok("/");
    $format = strtok("/");
    if (!module_hook($module, 'filter')) {
      continue;
    }

    // Built-in filter module, a special case where we would like to strip XSS and nothing more
    if ($module == 'filter' && $delta == 0) {
      preg_match_all("|</?([a-z][a-z0-9]*)(?:\\b[^>]*)>|i", $text, $matches);
      if ($matches[1]) {

        // Sources of inspiration:
        // http://www.w3.org/TR/html4/index/elements.html
        // http://www.w3.org/TR/html-markup/elements.html
        // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
        $base_allowed_tags = array(
          'a',
          'abbr',
          'acronym',
          'address',
          'area',
          'article',
          'aside',
          'audio',
          'b',
          'base',
          'basefont',
          'bdi',
          'bdo',
          'big',
          'blockquote',
          'body',
          'br',
          'button',
          'canvas',
          'caption',
          'center',
          'cite',
          'code',
          'col',
          'colgroup',
          'command',
          'datalist',
          'dd',
          'del',
          'details',
          'dfn',
          'dialog',
          'dir',
          'div',
          'dl',
          'dt',
          'em',
          'fieldset',
          'figcaption',
          'figure',
          'font',
          'footer',
          'form',
          'h1',
          'h2',
          'h3',
          'h4',
          'h5',
          'h6',
          'head',
          'header',
          'hgroup',
          'hr',
          'html',
          'i',
          'img',
          'input',
          'ins',
          'isindex',
          'kbd',
          'keygen',
          'label',
          'legend',
          'li',
          'main',
          'map',
          'mark',
          'menu',
          'menuitem',
          'meter',
          'nav',
          'noframes',
          'noscript',
          'ol',
          'optgroup',
          'option',
          'output',
          'p',
          'param',
          'pre',
          'progress',
          'q',
          'rp',
          'rt',
          'ruby',
          's',
          'samp',
          'section',
          'select',
          'small',
          'source',
          'span',
          'strike',
          'strong',
          'sub',
          'summary',
          'sup',
          'table',
          'tbody',
          'td',
          'textarea',
          'tfoot',
          'th',
          'thead',
          'time',
          'title',
          'tr',
          'track',
          'tt',
          'u',
          'ul',
          'var',
          'video',
          'wbr',
        );

        // Get tags allowed in filter settings
        $filter_allowed_tags = preg_split('/\\s+|<|>/', variable_get("allowed_html_{$filter_format_id}", ''), -1, PREG_SPLIT_NO_EMPTY);

        // Combine allowed tags
        $tags = array_merge($base_allowed_tags, $filter_allowed_tags);

        // Tags provided by hook
        $hooks_allowed_tags = module_invoke_all('fckeditor_filter_xss_allowed_tags');
        if (!empty($hooks_allowed_tags) && is_array($hooks_allowed_tags)) {
          foreach ($hooks_allowed_tags as $tag) {
            if (!empty($tag) && is_string($tag) && !in_array($tag, $tags)) {
              array_push($tags, $tag);
            }
          }
        }
        $text = filter_xss($text, $tags);
      }
    }
    else {
      $text = module_invoke($module, 'filter', 'process', $delta, $format, $text);
    }
  }
  $text = strtr($text, array(
    '__COMMENT__START__' => '<!--',
    '__COMMENT__END__' => '-->',
  ));
  echo $text;
  exit;
}
function fckeditor_process_form(&$form) {
  global $_fckeditor_configuration, $_fckeditor_js_ids;
  static $processed_textareas = array();
  static $found_textareas = array();

  //Skip if:

  // - we're not editing an element
  // - fckeditor is not enabled (configuration is empty)
  if (arg(1) == "add" || arg(1) == "reply" || !count($_fckeditor_configuration)) {
    return $form;
  }
  $fckeditor_filters = array();

  // Iterate over element children; resetting array keys to access last index.
  if ($children = array_values(element_children($form))) {
    foreach ($children as $index => $item) {
      $element =& $form[$item];
      if (isset($element['#id']) && in_array($element['#id'], array_keys($_fckeditor_js_ids))) {
        $found_textareas[$element['#id']] =& $element;
      }

      // filter_form() always uses the key 'format'. We need a type-agnostic
      // match to prevent false positives. Also, there must have been at least
      // one element on this level.
      if ($item === 'format' && $index > 0) {

        // Make sure we either match a input format selector or input format
        // guidelines (displayed if user has access to one input format only).
        if (isset($element['#type']) && $element['#type'] == 'fieldset' || isset($element['format']['guidelines'])) {

          // The element before this element is the target form field.
          $field =& $form[$children[$index - 1]];
          $textarea_id = $field['#id'];

          // The following condition may fail when there are textareas with formats that do not have FCKeditor enabled.
          if (isset($_fckeditor_js_ids[$textarea_id])) {
            $js_id = $_fckeditor_js_ids[$textarea_id];
            array_push($processed_textareas, $js_id);

            //search for checkxss1/2 class
            if (empty($field['#attributes']['class']) || strpos($field['#attributes']['class'], "checkxss") === FALSE) {
              continue;
            }

            // Determine the available input formats. The last child element is a
            // link to "More information about formatting options". When only one
            // input format is displayed, we also have to remove formatting
            // guidelines, stored in the child 'format'.
            $formats = element_children($element);
            foreach ($formats as $format_id) {
              $format = !empty($element[$format_id]['#default_value']) ? $element[$format_id]['#default_value'] : $element[$format_id]['#value'];
              break;
            }
            $enabled = filter_list_format($format);
            $fckeditor_filters = array();

            //loop through all enabled filters
            foreach ($enabled as $id => $filter) {

              //but use only that one selected in FCKeditor profile
              if (in_array($id, array_keys($_fckeditor_configuration[$textarea_id]['filters'])) && $_fckeditor_configuration[$textarea_id]['filters'][$id]) {
                if (!isset($fckeditor_filters[$js_id])) {
                  $fckeditor_filters[$js_id] = array();
                }
                $fckeditor_filters[$js_id][] = $id . "/" . $format;
              }
            }
            drupal_add_js(array(
              'fckeditor' => array(
                'settings' => array(
                  $textarea_id => array(
                    'input_format' => $format,
                  ),
                ),
              ),
            ), 'setting');

            //No filters assigned, remove xss class
            if (empty($fckeditor_filters[$js_id])) {
              $field['#attributes']['class'] = preg_replace("/checkxss(1|2)/", "", $field['#attributes']['class']);
            }
            else {
              $field['#attributes']['class'] = strtr($field['#attributes']['class'], array(
                "checkxss1" => "filterxss1",
                "checkxss2" => "filterxss2",
              ));
            }
            array_pop($formats);
            unset($formats['format']);
          }
        }

        // If this element is 'format', do not recurse further.
        continue;
      }

      // Recurse into children.
      fckeditor_process_form($element);
    }
  }

  //We're in a form
  if (isset($form['#action'])) {

    //some textareas associated with FCKeditor has not been processed
    if (count($processed_textareas) < count($_fckeditor_js_ids)) {

      //loop through all found textfields
      foreach (array_keys($found_textareas) as $id) {
        $element =& $found_textareas[$id];

        //if not processed yet (checkxss class is before final processing)
        if (strpos($element['#attributes']['class'], "checkxss") !== FALSE && !in_array($_fckeditor_js_ids[$element['#id']], $processed_textareas) && !empty($_fckeditor_configuration[$id]['filters']) && array_sum($_fckeditor_configuration[$id]['filters'])) {

          //assign default Filtered HTML to be safe on fields that do not have input format assigned, but only if at least one security filter is enabled in Security settings
          $js_id = $_fckeditor_js_ids[$element['#id']];
          $fckeditor_filters[$js_id][] = "filter/0/1";
          $element['#attributes']['class'] = strtr($element['#attributes']['class'], array(
            "checkxss1" => "filterxss1",
            "checkxss2" => "filterxss2",
          ));
        }
      }
    }
  }
  if (!empty($fckeditor_filters)) {
    drupal_add_js(array(
      'fckeditor_filters' => $fckeditor_filters,
    ), 'setting');
  }
  return $form;
}

/**
 * Allow more than 255 chars in Allowed HTML tags textfield
 */
function fckeditor_process_input($element) {
  if ($element['#id'] == 'edit-allowed-html-1') {
    $element['#maxlength'] = max($element['#maxlength'], 1024);
  }
  return $element;
}

/**
 * Implementation of hook_menu().
 */
function fckeditor_menu() {
  $items = array();
  $items['fckeditor/xss'] = array(
    'title' => 'XSS Filter',
    'description' => 'XSS Filter.',
    'page callback' => 'fckeditor_filter_xss',
    'access arguments' => array(
      'access fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/fckeditor'] = array(
    'title' => 'FCKeditor',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'fckeditor_admin_main',
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/settings/fckeditor/add'] = array(
    'title' => 'Add new FCKeditor profile',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fckeditor_admin_profile_form',
    ),
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/fckeditor/clone/%fckeditor_profile'] = array(
    'title' => 'Clone FCKeditor profile',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fckeditor_admin_profile_clone_form',
      4,
    ),
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/fckeditor/edit/%fckeditor_profile'] = array(
    'title' => 'Edit FCKeditor profile',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fckeditor_admin_profile_form',
      4,
    ),
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/fckeditor/delete/%fckeditor_profile'] = array(
    'title' => 'Delete FCKeditor profile',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fckeditor_admin_profile_delete_form',
      4,
    ),
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/fckeditor/addg'] = array(
    'title' => 'Add FCKeditor Global profile',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fckeditor_admin_global_profile_form',
      'add',
    ),
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/fckeditor/editg'] = array(
    'title' => 'Edit FCKeditor Global profile',
    'description' => 'Configure the rich text editor.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fckeditor_admin_global_profile_form',
      'edit',
    ),
    'file' => 'fckeditor.admin.inc',
    'access arguments' => array(
      'administer fckeditor',
    ),
    'type' => MENU_CALLBACK,
  );

  // img_assist integration
  $items['img_assist/load/fckeditor'] = array(
    'title' => 'Image assist',
    'page callback' => 'fckeditor_wrapper_img_assist_loader',
    'file' => 'fckeditor.user.inc',
    'access arguments' => array(
      'access img_assist',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_init().
 */
function fckeditor_init() {
  drupal_add_css(drupal_get_path('module', 'fckeditor') . '/fckeditor.css');
}

/**
 * Implementation of hook_file_download().
 * Support for private downloads.
 * FCKeditor does not implement any kind of potection on private files.
 */
function fckeditor_file_download($file) {
  if ($path = file_create_path($file)) {
    $result = db_query("SELECT f.* FROM {files} f WHERE filepath = '%s'", $path);
    if (db_fetch_object($result)) {
      return NULL;
    }

    //No info in DB? Probably a file uploaded with FCKeditor
    $global_profile = fckeditor_profile_load("FCKeditor Global Profile");

    //Assume that files inside of fckeditor directory belong to the FCKeditor. If private directory is set, let the decision about protection to the user.
    $private_dir = isset($global_profile->settings['private_dir']) ? trim($global_profile->settings['private_dir'], '\\/') : '';
    $private_dir = preg_quote($private_dir, '#');
    $private_dir = strtr($private_dir, array(
      '%u' => '(\\d+)',
      '%n' => '([\\x80-\\xF7 \\w@.-]+)',
    ));

    // regex for %n taken from user_validate_name() in user.module
    $private_dir = trim($private_dir, '\\/');
    $regex = '#^' . preg_quote(file_directory_path() . '/', '#') . $private_dir . '#';

    //If path to the file points to the FCKeditor private directory, allow downloading
    if (preg_match($regex, $path)) {
      $ctype = ($info = @getimagesize($path)) ? $info['mime'] : (function_exists('mime_content_type') ? mime_content_type($path) : 'application/x-download');
      return array(
        'Content-Type: ' . $ctype,
      );
    }
  }
}

/**
 * Load all profiles. Just load one profile if $name is passed in.
 */
function fckeditor_profile_load($name = '', $clear = FALSE) {
  static $profiles = array();
  if (empty($profiles) || $clear === TRUE) {
    $result = db_query("SELECT * FROM {fckeditor_settings}");
    while ($data = db_fetch_object($result)) {
      $data->settings = unserialize($data->settings);
      $data->rids = array();
      $profiles[$data->name] = $data;
    }
    $roles = user_roles();
    $result = db_query("SELECT name, rid FROM {fckeditor_role}");
    while ($data = db_fetch_object($result)) {
      $profiles[$data->name]->rids[$data->rid] = $roles[$data->rid];
    }
  }
  return $name ? isset($profiles[urldecode($name)]) ? $profiles[urldecode($name)] : FALSE : $profiles;
}

/**
 * @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 FCKeditor is enabled
 */
function fckeditor_is_enabled($excl_mode, $excl_regex, $element_id, $get_q) {
  $front = variable_get('site_frontpage', 'node');
  $excl_regex = str_replace('<front>', $front, $excl_regex);
  $nodetype = fckeditor_get_nodetype($get_q);
  $element_id = str_replace('.', '\\.', $element_id);
  $match = !empty($excl_regex) && preg_match($excl_regex, $nodetype . '@' . $get_q . '.' . $element_id);
  return $excl_mode == '0' xor $match;
}

/**
 * This function create the HTML objects required for the FCKeditor
 *
 * @param $element
 *   A fully populated form elment to add the editor to
 * @return
 *   The same $element with extra FCKeditor markup and initialization
 */
function fckeditor_process_textarea($element) {
  static $is_running = FALSE;
  static $num = 1;
  global $user, $language, $_fckeditor_configuration, $_fckeditor_js_ids;
  $enabled = TRUE;

  //hack for module developers that want to disable FCKeditor on their textareas
  if (key_exists('#wysiwyg', $element) && !$element['#wysiwyg']) {
    return $element;
  }
  if (isset($element['#access']) && !$element['#access']) {
    return $element;
  }

  //skip this one, surely nobody wants WYSIWYG here
  switch ($element['#id']) {
    case 'edit-log':
      return $element;
      break;
  }
  if (isset($element['#attributes']['disabled']) && $element['#attributes']['disabled'] == 'disabled') {
    return $element;
  }
  $global_profile = fckeditor_profile_load('FCKeditor Global Profile');
  if ($global_profile) {
    $global_conf = $global_profile->settings;
    if ($global_conf) {
      $enabled = fckeditor_is_enabled(empty($global_conf['excl_mode']) ? '0' : $global_conf['excl_mode'], empty($global_conf['excl_regex']) ? '' : $global_conf['excl_regex'], $element['#id'], $_GET['q']);
    }
  }
  if ($enabled) {
    $profile = fckeditor_user_get_profile($user, $element['#id']);
    if ($profile) {
      $conf = array();
      $conf = $profile->settings;
      if ($conf['allow_user_conf'] == 't') {
        foreach (array(
          'default',
          'show_toggle',
          'popup',
          'skin',
          'toolbar',
          'expand',
          'width',
          'lang',
          'auto_lang',
        ) as $setting) {
          $conf[$setting] = fckeditor_user_get_setting($user, $profile, $setting);
        }
      }
      if ($conf['popup'] == 't' && $conf['show_toggle'] == 't') {
        $conf['show_toggle'] = 'f';
      }
    }
    else {
      $enabled = FALSE;
    }
  }

  //old profile info, assume Filtered HTML is enabled
  if (!isset($conf['ss'])) {
    $conf['ss'] = 2;
    $conf['filters']['filter/0'] = 1;
  }
  if (!isset($conf['filters'])) {
    $conf['filters'] = array();
  }
  $themepath = fckeditor_path_to_theme() . '/';
  $host = base_path();
  if (!isset($element['#suffix'])) {
    $element['#suffix'] = '';
  }

  // only replace textarea when it has enough rows and it is enabled
  if ($enabled && ($element['#rows'] > $conf['min_rows'] || $conf['min_rows'] <= 1 && empty($element['#rows']))) {
    $textarea_id = $element['#id'];
    if (!isset($element['#attributes'])) {
      $element['#attributes'] = array();
    }
    if (!isset($element['#attributes']['class'])) {
      $element['#attributes']['class'] = 'fckeditor';
    }
    else {
      $element['#attributes']['class'] .= ' fckeditor';
    }
    $js_id = 'oFCK_' . $num++;
    $_fckeditor_js_ids[$element['#id']] = $js_id;
    $fckeditor_on = $conf['default'] == 't' ? 1 : 0;
    $xss_check = 0;

    //it's not a problem when adding new content/comment
    if (arg(1) != "add" && arg(1) != "reply") {
      $_fckeditor_configuration[$element['#id']] = $conf;

      //let FCKeditor know when perform XSS checks auto/manual
      if ($conf['ss'] == 1) {
        $xss_class = 'checkxss1';
      }
      else {
        $xss_class = 'checkxss2';
      }
      $element['#attributes']['class'] .= ' ' . $xss_class;
      $xss_check = 1;
    }

    //settings are saved as strings, not booleans
    if ($conf['show_toggle'] == 't') {
      $content = '';
      if (isset($element['#post']['teaser_js'])) {
        $content .= $element['#post']['teaser_js'] . '<!--break-->';
      }
      $content .= $element['#value'];
      $wysiwyg_link = '';
      $wysiwyg_link .= "<a href=\"javascript:Toggle('{$textarea_id}','" . str_replace("'", "\\'", t('Switch to plain text editor')) . "','" . str_replace("'", "\\'", t('Switch to rich text editor')) . "'," . $xss_check . ");\" id=\"switch_{$textarea_id}\" " . ($fckeditor_on ? "style=\"display:none\"" : "") . ">";
      $wysiwyg_link .= $fckeditor_on ? t('Switch to plain text editor') : t('Switch to rich text editor');
      $wysiwyg_link .= '</a>';

      // Make sure to append to #suffix so it isn't completely overwritten
      $element['#suffix'] .= $wysiwyg_link;
    }

    //convert contents to HTML if necessary
    if ($conf['autofixplaintext'] == 't') {
      module_load_include('lib.inc', 'fckeditor');
      if (fckeditor_is_plaintext($element['#value'])) {
        $element['#value'] = _filter_autop($element['#value']);
      }
    }

    // setting some variables
    $module_drupal_path = drupal_get_path('module', 'fckeditor');
    $module_full_path = $host . $module_drupal_path;
    $editor_path = fckeditor_path(FALSE);
    $editor_local_path = fckeditor_path(TRUE);

    // get the default drupal files path
    $files_path = $host . file_directory_path();

    // module_drupal_path:
    //  'modules/fckeditor' (length=17)
    // module_full_path:
    //  '/drupal5/modules/fckeditor' (length=26)
    // files_path:
    //  '/drupal5/files' (length=14)
    // configured in settings
    $width = $conf['width'];

    // sensible default for small toolbars
    $height = intval($element['#rows']) * 14 + 140;
    if (!$is_running) {
      drupal_add_js($module_drupal_path . '/fckeditor.utils.js');

      /* In D6 drupal_add_js() can't add external JS, in D7 use drupal_add_js(...,'external') */
      drupal_set_html_head('<script type="text/javascript" src="' . $editor_path . '/fckeditor.js?I"></script>');
      drupal_add_js(array(
        'fckeditor' => array(
          'ajaxToken' => drupal_get_token('fckeditorAjaxCall'),
        ),
      ), 'setting');
      $is_running = TRUE;
    }
    $toolbar = $conf['toolbar'];

    //$height += 100; // for larger toolbars
    $force_simple_toolbar = fckeditor_is_enabled('1', empty($conf['simple_incl_regex']) ? '' : $conf['simple_incl_regex'], $element['#id'], $_GET['q']);
    if (!$force_simple_toolbar) {
      $force_simple_toolbar = fckeditor_is_enabled('1', empty($global_conf['simple_incl_regex']) ? '' : $global_conf['simple_incl_regex'], $element['#id'], $_GET['q']);
    }
    if ($force_simple_toolbar) {
      $toolbar = FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME;
    }
    if (!empty($conf['theme_config_js']) && $conf['theme_config_js'] == 't' && file_exists($themepath . 'fckeditor.config.js')) {
      $fckeditor_config_path = $host . $themepath . 'fckeditor.config.js?' . @filemtime($themepath . 'fckeditor.config.js');
    }
    else {
      $fckeditor_config_path = $module_full_path . "/fckeditor.config.js?" . @filemtime($module_drupal_path . "/fckeditor.config.js");
    }
    $js = $js_id . " = new FCKeditor( '" . $textarea_id . "' );\n" . $js_id . ".defaultState = " . ($fckeditor_on && $conf['popup'] == 'f' ? 1 : 0) . ";\n" . $js_id . ".BasePath = '" . $editor_path . "/';\n" . $js_id . ".DrupalId = '" . $js_id . "';\n" . $js_id . ".Config['PluginsPath'] = '" . $module_full_path . "/plugins/';\n" . $js_id . ".Config['CustomConfigurationsPath'] = \"" . $fckeditor_config_path . "\";\n" . $js_id . ".Config['TextareaID'] = \"" . $element['#id'] . "\";\n" . $js_id . ".Config['BodyId'] = \"" . $element['#id'] . "\";";

    //if ($conf['appearance_conf'] == 'f') {
    $js .= "\n" . $js_id . ".ToolbarSet = \"" . $toolbar . "\";\n" . $js_id . ".Config['SkinPath'] = " . $js_id . ".BasePath + \"editor/skins/" . $conf['skin'] . "/\";\n" . $js_id . ".Config['DefaultLanguage'] = \"" . $conf['lang'] . "\";\n" . $js_id . ".Config['AutoDetectLanguage'] = " . ($conf['auto_lang'] == "t" ? "true" : "false") . ";\n" . $js_id . ".Height = \"" . $height . "\";\n" . $js_id . ".Config['ToolbarStartExpanded'] = " . ($conf['expand'] == "t" ? "true" : "false") . ";\n" . $js_id . ".Width = \"" . $width . "\";\n";

    //}

    //if ($conf['output_conf'] == 'f') {
    $js .= "\n" . $js_id . ".Config['EnterMode'] = '" . $conf['enter_mode'] . "';\n" . $js_id . ".Config['ShiftEnterMode'] = \"" . $conf['shift_enter_mode'] . "\";\n" . $js_id . ".Config['FontFormats'] = \"" . str_replace(",", ";", $conf['font_format']) . "\";\n" . $js_id . ".Config['FormatSource'] = " . ($conf['format_source'] == "t" ? "true" : "false") . ";\n" . $js_id . ".Config['FormatOutput'] = " . ($conf['format_output'] == "t" ? "true" : "false") . ";\n";

    //}
    if (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) {
      $js .= $js_id . ".Config['ContentLangDirection'] = 'rtl';\n";
    }

    // add code for filebrowser for users that have access
    if (user_access('allow fckeditor file uploads') == 1) {
      $filebrowser = !empty($conf['filebrowser']) ? $conf['filebrowser'] : 'none';
      if ($filebrowser == 'imce' && !module_exists('imce')) {
        $filebrowser = 'none';
      }
      if ($filebrowser == 'ib' && !module_exists('imagebrowser')) {
        $filebrowser = 'none';
      }
      if ($filebrowser == 'webfm' && !module_exists('webfm_popup')) {
        $filebrowser = 'none';
      }
      if ($filebrowser == 'ckfinder' && !file_exists(_fckeditor_ckfinder_path())) {
        $filebrowser = 'none';
      }
      $quickupload = !empty($conf['quickupload']) && $conf['quickupload'] == 't';

      // load variables used by both quick upload and filebrowser
      // and assure that the $_SESSION variables are loaded
      if ($quickupload || $filebrowser == 'builtin') {
        if (file_exists($editor_local_path . "/editor/filemanager/connectors/php/connector.php")) {
          $connector_path = $editor_path . "/editor/filemanager/connectors/php/connector.php";
        }
        elseif (file_exists($editor_local_path . "/editor/filemanager/upload/php/connector.php")) {
          $connector_path = $editor_path . "/editor/filemanager/upload/php/connector.php";
        }
        if (file_exists($editor_local_path . "/editor/filemanager/connectors/php/upload.php")) {
          $upload_path = $editor_path . "/editor/filemanager/connectors/php/upload.php";
        }
        elseif (file_exists($editor_local_path . "/editor/filemanager/upload/php/upload.php")) {
          $upload_path = $editor_path . "/editor/filemanager/upload/php/upload.php";
        }
        if (!empty($profile->settings['UserFilesPath'])) {
          $_SESSION['FCKeditor']['UserFilesPath'] = strtr($profile->settings['UserFilesPath'], array(
            "%f" => file_directory_path(),
            "%u" => $user->uid,
            "%b" => $host,
            "%n" => $user->name,
          ));
        }
        if (!empty($profile->settings['UserFilesAbsolutePath'])) {
          $_SESSION['FCKeditor']['UserFilesAbsolutePath'] = strtr($profile->settings['UserFilesAbsolutePath'], array(
            "%f" => file_directory_path(),
            "%u" => $user->uid,
            "%b" => base_path(),
            "%d" => $_SERVER['DOCUMENT_ROOT'],
            "%n" => $user->name,
          ));
        }
        if (variable_get('file_downloads', '') == FILE_DOWNLOADS_PRIVATE) {
          $private_dir = isset($global_profile->settings['private_dir']) ? trim($global_profile->settings['private_dir'], '\\/') : '';
          if (!empty($private_dir)) {
            $private_dir = strtr($private_dir, array(
              '%u' => $user->uid,
              '%n' => $user->name,
            ));
            $_SESSION['FCKeditor']['UserFilesPath'] = url('system/files') . '/' . $private_dir . '/';
            $_SESSION['FCKeditor']['UserFilesAbsolutePath'] = realpath(file_directory_path()) . DIRECTORY_SEPARATOR . $private_dir . DIRECTORY_SEPARATOR;
          }
          else {
            $_SESSION['FCKeditor']['UserFilesPath'] = url('system/files') . '/';
            $_SESSION['FCKeditor']['UserFilesAbsolutePath'] = realpath(file_directory_path()) . DIRECTORY_SEPARATOR;
          }
        }
      }
      if ($quickupload) {
        $js .= $js_id . ".Config['LinkUpload'] = true;\n";
        $js .= $js_id . ".Config['ImageUpload'] = true;\n";
        $js .= $js_id . ".Config['FlashUpload'] = true;\n";
        $js .= $js_id . ".Config['LinkUploadURL'] = '" . $upload_path . "';\n";
        $js .= $js_id . ".Config['ImageUploadURL'] = '" . $upload_path . "?Type=Image';\n";
        $js .= $js_id . ".Config['FlashUploadURL'] = '" . $upload_path . "?Type=Flash';\n";
      }
      else {
        $js .= $js_id . ".Config['LinkUpload'] = false;\n";
        $js .= $js_id . ".Config['ImageUpload'] = false;\n";
        $js .= $js_id . ".Config['FlashUpload'] = false;\n";
      }
      switch ($filebrowser) {
        case 'imce':
          $js .= $js_id . ".Config['LinkBrowser']= true;\n";
          $js .= $js_id . ".Config['ImageBrowser']= true;\n";
          $js .= $js_id . ".Config['FlashBrowser']= true;\n";
          $js .= $js_id . ".Config['LinkBrowserURL']= '" . $host . "index.php?q=imce&app=FCKEditor|url@txtLnkUrl,txtUrl';\n";
          $js .= $js_id . ".Config['ImageBrowserURL']= '" . $host . "index.php?q=imce&app=FCKEditor|url@txtUrl|width@txtWidth|height@txtHeight';\n";
          $js .= $js_id . ".Config['FlashBrowserURL']= '" . $host . "index.php?q=imce&app=FCKEditor|url@txtUrl';\n";
          break;
        case 'webfm':
          $js .= $js_id . ".Config['LinkBrowser']= true;\n";
          $js .= $js_id . ".Config['ImageBrowser']= true;\n";
          $js .= $js_id . ".Config['FlashBrowser']= true;\n";
          $js .= $js_id . ".Config['ImageDlgHideLink']= true;\n";
          $js .= $js_id . ".Config['LinkBrowserURL']= '" . $host . "index.php?q=webfm_popup&url=txtUrl&caller=fckeditor';\n";
          $js .= $js_id . ".Config['ImageBrowserURL']= '" . $host . "index.php?q=webfm_popup&url=txtUrl&caller=fckeditor';\n";
          $js .= $js_id . ".Config['FlashBrowserURL']= '" . $host . "index.php?q=webfm_popup&url=txtUrl&caller=fckeditor';\n";
          break;
        case 'builtin':
          $js .= $js_id . ".Config['LinkBrowser'] = true;\n";
          $js .= $js_id . ".Config['ImageBrowser'] = true;\n";
          $js .= $js_id . ".Config['FlashBrowser'] = true;\n";
          $js .= $js_id . ".Config['LinkBrowserURL'] = '" . $editor_path . "/editor/filemanager/browser/default/browser.html?Connector=" . $connector_path . "&ServerPath=" . $files_path . "';\n";
          $js .= $js_id . ".Config['ImageBrowserURL'] = '" . $editor_path . "/editor/filemanager/browser/default/browser.html?Type=Image&Connector=" . $connector_path . "&ServerPath=" . $files_path . "';\n";
          $js .= $js_id . ".Config['FlashBrowserURL'] = '" . $editor_path . "/editor/filemanager/browser/default/browser.html?Type=Flash&Connector=" . $connector_path . "&ServerPath=" . $files_path . "';\n";
          break;
        case 'ib':
          $js .= $js_id . ".Config['ImageBrowser']= true;\n";
          $js .= $js_id . ".Config['LinkBrowser']= true;\n";
          $js .= $js_id . ".Config['FlashBrowser']= false;\n";
          $js .= $js_id . ".Config['ImageBrowserURL']= '" . $host . "index.php?q=imagebrowser/view/browser&app=FCKEditor';\n";
          $js .= $js_id . ".Config['LinkBrowserURL']= '" . $host . "index.php?q=imagebrowser/view/browser&app=FCKEditor';\n";
          $js .= $js_id . ".Config['ImageBrowserWindowWidth']= '680';";
          $js .= $js_id . ".Config['ImageBrowserWindowHeight'] = '439';";
          $js .= $js_id . ".Config['LinkBrowserWindowWidth']= '680';";
          $js .= $js_id . ".Config['LinkBrowserWindowHeight'] = '439';";
          break;
        case 'ckfinder':
          $js .= $js_id . ".Config['LinkBrowser'] = true;\n";
          $js .= $js_id . ".Config['ImageBrowser'] = true;\n";
          $js .= $js_id . ".Config['FlashBrowser'] = true;\n";
          $js .= $js_id . ".Config['LinkBrowserURL'] = '" . $module_full_path . "/ckfinder/ckfinder.html';\n";
          $js .= $js_id . ".Config['ImageBrowserURL'] = '" . $module_full_path . "/ckfinder/ckfinder.html?type=Images';\n";
          $js .= $js_id . ".Config['FlashBrowserURL'] = '" . $module_full_path . "/ckfinder/ckfinder.html?type=Flash';\n";
          break;
        default:
        case 'none':
          $js .= $js_id . ".Config['LinkBrowser'] = false;\n";
          $js .= $js_id . ".Config['ImageBrowser'] = false;\n";
          $js .= $js_id . ".Config['FlashBrowser'] = false;\n";
          break;
      }
    }
    else {
      $js .= $js_id . ".Config['LinkBrowser'] = false;\n";
      $js .= $js_id . ".Config['ImageBrowser'] = false;\n";
      $js .= $js_id . ".Config['FlashBrowser'] = false;\n";
      $js .= $js_id . ".Config['LinkUpload'] = false;\n";
      $js .= $js_id . ".Config['ImageUpload'] = false;\n";
      $js .= $js_id . ".Config['FlashUpload'] = false;\n";
    }
    if (!empty($conf['js_conf'])) {
      $lines = preg_split("/[\n\r]+/", $conf['js_conf']);
      foreach ($lines as $l) {
        if (strlen($l) > 5) {
          $eqpos = strpos($l, '=');
          if (FALSE !== $eqpos) {
            $option = str_replace('FCKConfig.', '', substr($l, 0, $eqpos));
            $js .= "\n" . $js_id . ".Config['" . trim($option) . "'] =" . substr($l, $eqpos + 1);
          }
        }
      }
    }

    // add custom xml stylesheet if it exists
    if (!empty($conf['css_style']) && $conf['css_style'] == 'theme') {
      if (file_exists($themepath . 'fckstyles.xml')) {
        $styles_xml_path = $host . $themepath . 'fckstyles.xml';
        $js .= $js_id . ".Config['StylesXmlPath'] = \"" . $styles_xml_path . "\";\n";
      }
    }
    elseif (!empty($conf['css_style']) && $conf['css_style'] == 'self') {
      $conf['styles_path'] = str_replace("%h%t", "%t", $conf['styles_path']);
      $js .= $js_id . ".Config['StylesXmlPath'] = \"" . str_replace(array(
        '%h',
        '%t',
        '%m',
      ), array(
        $host,
        $host . $themepath,
        $module_drupal_path,
      ), $conf['styles_path']) . "\";\n";
    }

    // add custom xml templae if it exists
    if (!empty($conf['templatefile_mode']) && $conf['templatefile_mode'] == 'theme') {
      if (file_exists($themepath . 'fcktemplates.xml')) {
        $styles_xml_path = $host . $themepath . 'fcktemplates.xml';
        $js .= $js_id . ".Config['TemplatesXmlPath'] = \"" . $styles_xml_path . "\";\n";
      }
    }
    elseif (!empty($conf['templatefile_mode']) && $conf['templatefile_mode'] == 'self') {
      $conf['templatefile_path'] = str_replace("%h%t", "%t", $conf['templatefile_path']);
      $js .= $js_id . ".Config['TemplatesXmlPath'] = \"" . str_replace(array(
        '%h',
        '%t',
        '%m',
      ), array(
        $host,
        $host . $themepath,
        $module_drupal_path,
      ), $conf['templatefile_path']) . "\";\n";
    }

    // add custom stylesheet if configured
    // lets hope it exists but we'll leave that to the site admin
    $cssfiles = array(
      $module_full_path . '/fckeditor.css',
    );
    switch ($conf['css_mode']) {
      case 'theme':
        global $language, $theme, $theme_info, $base_theme_info;
        $style_css = $themepath . 'style.css';
        if (!empty($theme_info->stylesheets)) {
          $css_files = array();
          $editorcss = "\"";
          foreach ($base_theme_info as $base) {

            // Grab stylesheets from base theme
            if (!empty($base->stylesheets)) {

              // may be empty when the base theme reference in the info file is invalid
              foreach ($base->stylesheets as $type => $stylesheets) {
                if ($type != "print") {
                  foreach ($stylesheets as $name => $path) {
                    if (file_exists($path)) {
                      $css_files[$name] = $host . $path;
                    }
                  }
                }
              }
            }
          }
          if (!empty($theme_info->stylesheets)) {

            // Grab stylesheets from current theme
            foreach ($theme_info->stylesheets as $type => $stylesheets) {
              if ($type != "print") {
                foreach ($stylesheets as $name => $path) {
                  if (file_exists($path)) {
                    $css_files[$name] = $host . $path;
                  }
                  elseif (!empty($css_files[$name])) {
                    unset($css_files[$name]);
                  }
                }
              }
            }
          }
          if (!empty($css_files)) {
            $editorcss .= implode(",", $css_files) . ",";
          }

          // Grab stylesheets from color module
          $color_paths = variable_get('color_' . $theme . '_stylesheets', array());
          if (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) {
            if (!empty($color_paths[1])) {
              $editorcss .= $host . $color_paths[1] . ",";
            }
          }
          elseif (!empty($color_paths[0])) {
            $editorcss .= $host . $color_paths[0] . ",";
          }
          $editorcss .= $module_full_path . "/fckeditor.css\";\n";
          $js .= $js_id . ".Config['EditorAreaCSS'] = " . $editorcss;
        }
        elseif (file_exists($style_css)) {
          $js .= $js_id . ".Config['EditorAreaCSS'] = \"" . $host . $style_css . "," . $module_full_path . "/fckeditor.css\";";
        }
        else {
          $js .= $js_id . ".Config['EditorAreaCSS'] = \"" . $module_full_path . "/fckeditor.css\";";
        }
        break;
      case 'self':
        $conf['css_path'] = str_replace("%h%t", "%t", $conf['css_path']);
        $cssfiles[] = str_replace(array(
          '%h',
          '%t',
        ), array(
          $host,
          $host . $themepath,
        ), $conf['css_path']);
        $js .= $js_id . ".Config['EditorAreaCSS'] = '" . implode(',', $cssfiles) . "';\n";
        break;
      case 'none':
        $js .= $js_id . ".Config['EditorAreaCSS'] = " . $js_id . ".BasePath + 'editor/css/fck_editorarea.css,' + '" . implode(',', $cssfiles) . "';\n";
        break;
    }
    if ($num == 2) {
      $js .= 'var fckInstances = {};';
    }
    $js .= 'fckInstances[\'' . $textarea_id . '\'] = ' . $js_id . ";\n";
    drupal_add_js('var ' . $js_id . ';if (Drupal.jsEnabled) {' . $js . '}', 'inline');
    if ($conf['popup'] == 't') {
      $element['#suffix'] .= ' <span class="fckeditor_popuplink">(<a href="#" onclick="FCKeditor_OpenPopup(\'' . $module_full_path . '/fckeditor.popup.html\', \'' . $js_id . '\', \'' . $element['#id'] . '\', \'' . $conf['width'] . '\'); return false;">' . t('Open rich text editor') . "</a>)</span>";
    }
  }

  // display the field id for administrators
  if (user_access('administer fckeditor') && (!isset($global_conf['show_fieldnamehint']) || $global_conf['show_fieldnamehint'] == 't')) {
    module_load_include('admin.inc', 'fckeditor');
    $element['#suffix'] .= '<div class="textarea-identifier description">' . t('The ID for !excludingorincludinglink this element is %fieldname.', array(
      '!excludingorincludinglink' => l(t('excluding or including'), 'admin/settings/fckeditor'),
      '%fieldname' => fckeditor_rule_to_string(fckeditor_rule_create(fckeditor_get_nodetype($_GET['q']), $_GET['q'], $element['#id'])),
    )) . '</div>';
  }
  return $element;
}

/**
 * sort roles according to precedence settings. previously sorted roles are followed by latest added roles.
 */
function fckeditor_sorted_roles($clear = FALSE) {
  static $order;
  if (isset($order) && $clear !== TRUE) {
    return $order;
  }
  $order = array();
  $roles = user_roles(0, 'access fckeditor');
  $result = db_query("SELECT settings FROM {fckeditor_settings} WHERE name='FCKeditor 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;
}

/**
 * Test if client can render the FCKeditor
 * Use built-in test method in fckeditor.php
 * If fckeditor.php is not found, return false (probably in such case fckeditor is not installed correctly)
 *
 * @return
 *   TRUE if the browser is reasonably capable
 */
function fckeditor_is_compatible_client() {
  $editor_local_path = fckeditor_path(TRUE);
  $fckeditor_main_file = $editor_local_path . '/fckeditor.php';
  if (!function_exists('version_compare') || version_compare(phpversion(), '5', '<')) {
    $fckeditor_target_file = $editor_local_path . '/fckeditor_php4.php';
  }
  else {
    $fckeditor_target_file = $editor_local_path . '/fckeditor_php5.php';
  }
  if (file_exists($fckeditor_target_file)) {
    include_once $fckeditor_target_file;

    //FCKeditor 2.6.1+
    if (function_exists('FCKeditor_IsCompatibleBrowser')) {
      return FCKeditor_IsCompatibleBrowser();
    }
    elseif (class_exists('FCKeditor')) {

      //FCKeditor 2.5.1 up to 2.6 with definition of FCKeditor_IsCompatibleBrowser() in fckeditor.php
      if (filesize($fckeditor_main_file) > 1500) {
        include_once $fckeditor_main_file;
      }

      //FCKeditor 2.5.0 and earlier
      $fck = new FCKeditor('fake');
      return $fck
        ->IsCompatible();
    }
  }
  return FALSE;
}

/**
 * Read FCKeditor path from Global profile
 *
 * @return
 *   path to FCKeditor folder
 */
function fckeditor_path($local = FALSE) {
  static $fck_path;
  static $fck_local_path;
  if (!$fck_path) {
    $mod_path = drupal_get_path('module', 'fckeditor');
    $global_profile = fckeditor_profile_load('FCKeditor Global Profile');

    //default: path to fckeditor subdirectory in the fckeditor module directory (starting from the document root)

    //e.g. for http://example.com/drupal it will be /drupal/sites/all/modules/fckeditor/fckeditor
    $fck_path = base_path() . $mod_path . '/fckeditor';

    //default: path to fckeditor subdirectory in the fckeditor module directory (relative to index.php)

    //e.g.: sites/all/modules/fckeditor/fckeditor
    $fck_local_path = $mod_path . '/fckeditor';
    if ($global_profile) {
      $gs = $global_profile->settings;
      if (isset($gs['fckeditor_path'])) {
        $tmp_path = $gs['fckeditor_path'];
        $tmp_path = strtr($tmp_path, array(
          "%b" => base_path(),
          "%m" => base_path() . $mod_path,
        ));
        $tmp_path = str_replace('\\', '/', $tmp_path);
        $tmp_path = str_replace('//', '/', $tmp_path);
        $tmp_path = rtrim($tmp_path, ' \\/');
        if (substr($tmp_path, 0, 1) != '/') {
          $tmp_path = '/' . $tmp_path;

          //starts with '/'
        }
        $fck_path = $tmp_path;
        if (empty($gs['fckeditor_local_path'])) {

          //fortunately wildcards are used, we can easily get the right server path
          if (false !== strpos($gs['fckeditor_path'], "%m")) {
            $gs['fckeditor_local_path'] = strtr($gs['fckeditor_path'], array(
              "%m" => $mod_path,
            ));
          }
          if (false !== strpos($gs['fckeditor_path'], "%b")) {
            $gs['fckeditor_local_path'] = strtr($gs['fckeditor_path'], array(
              "%b" => ".",
            ));
          }
        }
      }

      //fckeditor_path is defined, but wildcards are not used, we need to try to find out where is

      //the document root located and append fckeditor_path to it.
      if (!empty($gs['fckeditor_local_path'])) {
        $fck_local_path = $gs['fckeditor_local_path'];
      }
      elseif (!empty($gs['fckeditor_path'])) {
        module_load_include('lib.inc', 'fckeditor');
        $local_path = fckeditor_resolve_url($gs['fckeditor_path'] . "/");
        if (FALSE !== $local_path) {
          $fck_local_path = $local_path;
        }
      }
    }
  }
  if ($local) {
    return $fck_local_path;
  }
  else {
    return $fck_path;
  }
}
function fckeditor_user_get_setting($user, $profile, $setting) {
  $default = array(
    'default' => 't',
    'show_toggle' => 't',
    'popup' => 'f',
    'skin' => 'default',
    'toolbar' => 'default',
    'expand' => 't',
    'width' => '100%',
    'lang' => 'en',
    'auto_lang' => 't',
  );
  if ($profile->settings['allow_user_conf']) {
    $status = isset($user->{'fckeditor_' . $setting}) ? $user->{'fckeditor_' . $setting} : (isset($profile->settings[$setting]) ? $profile->settings[$setting] : $default[$setting]);
  }
  else {
    $status = isset($profile->settings[$setting]) ? $profile->settings[$setting] : $default[$setting];
  }
  return $status;
}
function fckeditor_user_get_profile($user, $element_id = NULL) {
  $rids = array();

  // Since fckeditor_profile_load() makes a db hit, only call it when we're pretty sure
  // we're gonna render fckeditor.
  $sorted_roles = fckeditor_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 {fckeditor_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 {fckeditor_settings} s INNER JOIN {fckeditor_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 = fckeditor_profile_load($profile_name);
        $conf = $profile->settings;
        $enabled = fckeditor_is_enabled(empty($conf['excl_mode']) ? '0' : $conf['excl_mode'], empty($conf['excl_regex']) ? '' : $conf['excl_regex'], $element_id, $_GET['q']);
        if ($enabled) {
          return $profile;
        }
      }
    }
  }
  return FALSE;
}
function fckeditor_get_nodetype($get_q) {
  static $nodetype;
  if (!isset($nodetype)) {
    $nodetypes = node_get_types('names');
    $menuitem = menu_get_item();
    $nodetype = '*';
    if (!empty($menuitem['page_arguments']) && is_array($menuitem['page_arguments'])) {
      foreach ($menuitem['page_arguments'] as $item) {
        if (!empty($item->nid) && !empty($item->type)) {

          // not 100% valid check if $item is a node
          $nodetype = $item->type;
          break;
        }
      }
    }
    if ($nodetype == '*') {
      $get_q = explode("/", $get_q, 3);
      if ($get_q[0] == "node" && $get_q[1] == "add" && !empty($get_q[2])) {
        $nodetype = $get_q[2];

        // underscores are translated to dashes in node/add/ urls. try to translate it back
        if (!isset($nodetypes[$nodetype])) {
          foreach (array_keys($nodetypes) as $nt) {
            if (strtr($nt, '_', '-') === $nodetype) {
              $nodetype = $nt;
              break;
            }
          }
        }
      }
    }

    // Sanity check
    if (!isset($nodetypes[$nodetype])) {
      $nodetype = NULL;
    }
  }
  return $nodetype;
}
function fckeditor_path_to_theme() {
  global $theme_key;
  static $themepath;
  if (empty($themepath) && !empty($theme_key)) {
    $themepath = drupal_get_path('theme', $theme_key);
  }

  // fall back
  if (empty($themepath)) {
    return path_to_theme();
  }
  return $themepath;
}

Functions

Namesort descending Description
fckeditor_admin
fckeditor_elements Implementation of hook_elements(). Replace textarea with FCKeditor using callback function (fckeditor_process_textarea)
fckeditor_file_download Implementation of hook_file_download(). Support for private downloads. FCKeditor does not implement any kind of potection on private files.
fckeditor_filter_xss AJAX callback - XSS filter
fckeditor_get_nodetype
fckeditor_help Implementation of hook_help().
fckeditor_init Implementation of hook_init().
fckeditor_is_compatible_client Test if client can render the FCKeditor Use built-in test method in fckeditor.php If fckeditor.php is not found, return false (probably in such case fckeditor is not installed correctly)
fckeditor_is_enabled
fckeditor_menu Implementation of hook_menu().
fckeditor_path Read FCKeditor path from Global profile
fckeditor_path_to_theme
fckeditor_perm Implementation of hook_perm(). Administer -> User management -> Permissions
fckeditor_process_form
fckeditor_process_input Allow more than 255 chars in Allowed HTML tags textfield
fckeditor_process_textarea This function create the HTML objects required for the FCKeditor
fckeditor_profile_load Load all profiles. Just load one profile if $name is passed in.
fckeditor_sorted_roles sort roles according to precedence settings. previously sorted roles are followed by latest added roles.
fckeditor_user Implementation of hook_user().
fckeditor_user_get_profile
fckeditor_user_get_setting
_fckeditor_ckfinder_path

Constants

Namesort descending Description
FCKEDITOR_FORCE_SIMPLE_TOOLBAR_NAME The name of simplified toolbar which should be forced Be sure that this toolbar is defined in fckeditor.config.js or fckconfig.js

Globals