<?php
// $Id: fckeditor.module,v 1.20.2.34.2.91 2010/03/10 07:43:32 jorrit Exp $
/**
 * 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();

/**
 * 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() {
  $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) {
  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'];
          $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;
            }
          }

          //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>');
      $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 ."' );
". $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";
    //}

    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;
          }
        }
      }

      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';\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;

        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;

        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)) {
    $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];
      }
    }
  }

  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;
}
