Skip to content
fckeditor.module 44.2 KiB
Newer Older
 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
Wiktor Walc's avatar
Wiktor Walc committed
 * 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
Wiktor Walc's avatar
Wiktor Walc committed
 * 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') ;
Wiktor Walc's avatar
Wiktor Walc committed
global $_fckeditor_configuration;
global $_fckeditor_js_ids;
Wiktor Walc's avatar
Wiktor Walc committed
$_fckeditor_configuration = array();
$_fckeditor_js_ids = array();
 * 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);
}
 * 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);
Wiktor Walc's avatar
Wiktor Walc committed
/*
 *  Run if there is old menu information in database
 */
Wiktor Walc's avatar
Wiktor Walc committed
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');
Wiktor Walc's avatar
Wiktor Walc committed
  return FALSE;
}

Wiktor Walc's avatar
Wiktor Walc committed
 * Administer -> User management -> Permissions
 */
function fckeditor_perm() {
  return array('administer fckeditor', 'access fckeditor', 'allow fckeditor file uploads');
 * Replace textarea with FCKeditor using callback function (fckeditor_process_textarea)
function fckeditor_elements() {
  $type = array();
    '#process' => array(
  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'));
/**
 * AJAX callback - XSS filter
 */
function fckeditor_filter_xss() {
  $GLOBALS['devel_shutdown'] = FALSE;

  if (!isset($_POST['text']) || !is_string($_POST['text']) || !is_array($_POST['filters'])) {
    exit;
  }
  $text = $_POST['text'];
  $text = strtr($text, array('<!--' => '__COMMENT__START__', '-->' => '__COMMENT__END__'));
  foreach ($_POST['filters'] as $module_delta) {
    $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]) {
        $tags = array_unique($matches[1]);
        $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) {
Wiktor Walc's avatar
Wiktor Walc committed
  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)
Wiktor Walc's avatar
Wiktor Walc committed
  if (arg(1) == "add" || arg(1) == "reply" || !count($_fckeditor_configuration)) {
  $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];

Wiktor Walc's avatar
Wiktor Walc committed
      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'];
Wiktor Walc's avatar
Wiktor Walc committed
          $js_id = $_fckeditor_js_ids[$textarea_id];
          array_push($processed_textareas, $js_id);
          //search for checkxss1/2 class
Wiktor Walc's avatar
Wiktor Walc committed
          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'.
          foreach ($formats as $format_id) {
            $format = !empty($element[$format_id]['#default_value']) ? $element[$format_id]['#default_value'] : $element[$format_id]['#value'];
          $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();
              }
Wiktor Walc's avatar
Wiktor Walc committed
              $fckeditor_filters[$js_id][] = $id ."/". $format;
          //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
Wiktor Walc's avatar
Wiktor Walc committed
    if (count($processed_textareas) < count($_fckeditor_js_ids)) {
      //loop through all found textfields
      foreach (array_keys($found_textareas) as $id) {
Wiktor Walc's avatar
Wiktor Walc committed
        $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
Wiktor Walc's avatar
Wiktor Walc committed
          $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');
  }
/**
 * 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(
    '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,
  );

function fckeditor_init() {
  drupal_add_css(drupal_get_path('module', 'fckeditor') .'/fckeditor.css');
 * 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)) {
    //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);
      $profiles[$data->name] = $data;
    }
    $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);
Jorrit Schippers's avatar
Jorrit Schippers committed
  $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;
Wiktor Walc's avatar
Wiktor Walc committed
  global $user, $language, $_fckeditor_configuration, $_fckeditor_js_ids;
Jorrit Schippers's avatar
Jorrit Schippers committed
  $enabled = TRUE;
  //hack for module developers that want to disable FCKeditor on their textareas
  if (key_exists('#wysiwyg', $element) && !$element['#wysiwyg']) {
  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);
        }
      }
Jorrit Schippers's avatar
Jorrit Schippers committed
      if ($conf['popup'] == 't' && $conf['show_toggle'] == 't') {
        $conf['show_toggle'] = 'f';
Jorrit Schippers's avatar
Jorrit Schippers committed
      $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();

Wiktor Walc's avatar
Wiktor Walc committed
  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';
      $element['#attributes']['class'] .= ' fckeditor';
    }
    $js_id = 'oFCK_'. $num++;
Wiktor Walc's avatar
Wiktor Walc committed
    $_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") {
Wiktor Walc's avatar
Wiktor Walc committed
      $_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
      $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']);
      }
    }

    $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)
    $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>');

    $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 ."' );
". $js_id .".defaultState = ". (($fckeditor_on && $conf['popup'] == 'f') ? 1 : 0) .";
". $js_id .".BasePath = '". $editor_path ."/';
". $js_id .".DrupalId = '". $js_id ."';
". $js_id .".Config['PluginsPath'] = '". $module_full_path ."/plugins/';
". $js_id .".Config['CustomConfigurationsPath'] = \"". $fckeditor_config_path ."\";
". $js_id .".Config['TextareaID'] = \"". $element['#id'] ."\";
". $js_id .".Config['BodyId'] = \"". $element['#id'] ."\";";

    //if ($conf['appearance_conf'] == 'f') {
    $js .= "\n". $js_id .".ToolbarSet = \"". $toolbar ."\";
". $js_id .".Config['SkinPath'] = ". $js_id .".BasePath + \"editor/skins/". $conf['skin'] ."/\";
". $js_id .".Config['DefaultLanguage'] = \"". $conf['lang'] ."\";
". $js_id .".Config['AutoDetectLanguage'] = ". ($conf['auto_lang']=="t"?"true":"false") .";
". $js_id .".Height = \"". $height ."\";
". $js_id .".Config['ToolbarStartExpanded'] = ". ($conf['expand']=="t"?"true":"false") .";
". $js_id .".Width = \"". $width ."\";\n";
    //}
    //if ($conf['output_conf'] == 'f') {
    $js .= "\n". $js_id .".Config['EnterMode'] = '". $conf['enter_mode'] ."';
". $js_id .".Config['ShiftEnterMode'] = \"". $conf['shift_enter_mode'] ."\";
". $js_id .".Config['FontFormats'] = \"". str_replace(",", ";", $conf['font_format']) ."\";
". $js_id .".Config['FormatSource'] = ". ($conf['format_source']=="t"?"true":"false") .";
". $js_id .".Config['FormatOutput'] = ". ($conf['format_output']=="t"?"true":"false") .";\n";
Wiktor Walc's avatar
Wiktor Walc committed
    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';
      }
      $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;
          }
        $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";
        $js .= $js_id .".Config['LinkUpload'] = false;\n";
        $js .= $js_id .".Config['ImageUpload'] = false;\n";
        $js .= $js_id .".Config['FlashUpload'] = false;\n";
          $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";
        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';\n";
          $js .= $js_id .".Config['ImageBrowserURL']= '". $host ."index.php?q=webfm_popup&url=txtUrl';\n";
          $js .= $js_id .".Config['FlashBrowserURL']= '". $host ."index.php?q=webfm_popup&url=txtUrl';\n";
          break;

          $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";
        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;

          $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['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";
Wiktor Walc's avatar
Wiktor Walc committed
    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
Wiktor Walc's avatar
Wiktor Walc committed
    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)) {
          $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\";";
      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";
        $js .=  $js_id .".Config['EditorAreaCSS'] = ". $js_id .".BasePath + 'editor/css/fck_editorarea.css,' + '". implode(',', $cssfiles) ."';\n";
    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>';
/**
 * 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);
Wiktor Walc's avatar
Wiktor Walc committed
  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
      return $fck->IsCompatible();
    }
  }

/**
 * 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) {