Skip to content
wysiwyg_editor.module 21.4 KiB
Newer Older
 * Integrate Wysiwyg editors into Drupal.
 */

/**
 * Implementation of hook_menu().
 */
function wysiwyg_editor_menu() {
  $items = array();
  $items['admin/settings/wysiwyg/profile'] = array(
    'title' => 'Wysiwyg Editor',
    'page callback' => 'wysiwyg_editor_admin',
    'description' => 'Configure the rich editor.',
    'access arguments' => array('administer site configuration'),
    'file' => 'wysiwyg_editor.admin.inc',
  );
/**
 * Implementation of hook_theme().
 */
function wysiwyg_editor_theme() {
  return array(
    'wysiwyg_editor_admin_button_table' => array('arguments' => array('form')),
  );
}

/**
 * Implementation of hook_help().
 */
function wysiwyg_editor_help($path, $arg) {
  switch ($path) {
    case 'admin/settings/wysiwyg/profile':
    case 'admin/help#wysiwyg_editor':
      $output = '<p>'. t('Profiles can be defined based on user roles. A Wysiwyg Editor profile can define which pages receive this Wysiwyg Editor capability, what buttons or themes are enabled for the editor, how the editor is displayed, and a few other editor functions. Lastly, only users with the %permission <a href="!url">user permission</a> are able to use Wysiwyg Editor.', array('%permission' => 'access wysiwyg editor', '!url' => url('admin/user/permissions'))) .'</p>';
      return $output;
  }
}

/**
 * Implementation of hook_perm().
 */
function wysiwyg_editor_perm() {
  $array = array('access wysiwyg editor');
  $wysiwyg_editor_mod_path = drupal_get_path('module', 'wysiwyg_editor');
  if (is_dir($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/imagemanager/')) {
    $array[] = 'access tinymce imagemanager';
  }
  if (is_dir($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/filemanager/')) {
    $array[] = 'access tinymce filemanager';
  }

  return $array;
}

/**
 * Implementation of hook_elements().
 */
function wysiwyg_editor_elements() {
  $type = array();
  if (user_access('access wysiwyg editor')) {
    // Let Wysiwyg Editor potentially process each textarea.
    $type['textarea'] = array('#process' => array('wysiwyg_editor_process_textarea'), '#wysiwyg' => TRUE, '#wysiwyg_style' => 'advanced');
/**
 * Implementation of hook_form_alter().
 */
function wysiwyg_editor_form_alter(&$form, &$form_state) {
  // Disable 'teaser' textarea.
  unset($form['body_field']['teaser_js']);
  $form['body_field']['teaser_include'] = array();
}

 * Load Wysiwyg Editor files and send configuration data.
function wysiwyg_editor_load() {
  global $user;
  static $loaded = FALSE;

  // We only load the Wysiwyg Editor js file once per page request.
    $path_wysiwyg = drupal_get_path('module', 'wysiwyg_editor');
    // Hard-coded for now.
    $path_editor = $path_wysiwyg .'/tinymce/jscripts/tiny_mce';
    $profile = wysiwyg_editor_profile_load(wysiwyg_editor_current_profile());
    $status = wysiwyg_editor_user_get_status($user, $profile);
    $enable  = t('Enable rich-text');
    $disable = t('Disable rich-text');
    $no_wysiwyg = t('Your current web browser does not support WYSIWYG editing.');

    // TinyMCE ImageManager
    if (user_access('access tinymce imagemanager') && is_dir($path_editor .'/plugins/imagemanager/')) {
      drupal_add_js($path_editor .'/plugins/imagemanager/jscripts/mcimagemanager.js');
    // TinyMCE FileManager
    if (user_access('access tinymce filemanager') && is_dir($path_editor .'/plugins/filemanager/')) {
      drupal_add_js($path_editor .'/plugins/filemanager/jscripts/mcfilemanager.js');
    }

    // TinyMCE Compressor >= 1.0.9
    if (file_exists($path_editor .'/tiny_mce_gzip.js')) {
      drupal_add_js($path_editor .'/tiny_mce_gzip.js', 'module', 'header', FALSE, FALSE, FALSE);
    }
    // TinyMCE Compressor < 1.0.9
    elseif (file_exists($path_editor .'/tiny_mce_gzip.php')) {
      drupal_add_js($path_editor .'/tiny_mce_gzip.php', 'module', 'header', FALSE, FALSE, FALSE);
      drupal_add_js($path_editor .'/tiny_mce.js', 'module', 'header', FALSE, FALSE);
    // Add wysiwyg_editor.js to the footer to ensure it's executed after the
    // Drupal.settings array has been rendered and populated.
    drupal_add_js($path_wysiwyg .'/wysiwyg_editor.js', 'module', 'footer');
    drupal_add_js(array('wysiwygEditor' => array(
      'configs' => array(),
      'showToggle' => $profile->settings['show_toggle'],
      'disable' => $disable,
      'enable' => $enable,
      'noWysiwyg' => $no_wysiwyg,
      'status' => $status,
    )), 'setting');

    // We have to do this because of some unfocused CSS in certain themes.
    // @see http://drupal.org/node/18879
    drupal_set_html_head('<style type="text/css" media="all">.mceEditor img { display: inline; }</style>');
 * Process a textarea for Wysiwyg Editor.
function wysiwyg_editor_process_textarea($element) {
  wysiwyg_editor_filter_elements($element);

  if ($element['#wysiwyg']) {
    $profile = wysiwyg_editor_profile_load(wysiwyg_editor_current_profile());
    if ($profile && _wysiwyg_editor_page_match($profile)) {
      // Default to advanced theme if the requested one is not installed.
      $themes = _wysiwyg_editor_get_themes();
      if (!isset($themes[$element['#wysiwyg_style']])) {
        $element['#wysiwyg_style'] = 'advanced';
      }

      wysiwyg_editor_load();
      wysiwyg_editor_load_config($element['#wysiwyg_style']);
      wysiwyg_editor_load_plugins();
      if (!isset($element['#attributes'])) {
        $element['#attributes'] = array();
      }
      if (!isset($element['#attributes']['class'])) {
        $element['#attributes']['class'] = 'wysiwyg wysiwyg-'. $element['#wysiwyg_style'];
        $element['#attributes']['class'] .= ' wysiwyg wysiwyg-'. $element['#wysiwyg_style'];
      }
      // Set resizable to false to avoid drupal.js resizable function from
      // taking control of the textarea
      $element['#resizable'] = FALSE;
    }
  }

  return $element;
}

/**
 * Register a theme.
 */
function wysiwyg_editor_load_config($theme) {
  static $themes = array();
  // Only send a theme's configuration data once.
  if (!in_array($theme, $themes)) {
    $profile = wysiwyg_editor_profile_load(wysiwyg_editor_current_profile());
    $config = wysiwyg_editor_config($profile, $theme);
    // Convert the config values into the form expected by Wysiwyg Editor.
    foreach ($config as $key => $value) {
      if (is_bool($value)) {
        continue;
      }
        case 'true':
          $config[$key] = TRUE;
          break;
        case 'false':
          $config[$key] = FALSE;
          break;
      }
      if (is_array($value)) {
        $config[$key] = implode(',', $config[$key]);
      }
    }
    drupal_add_js('Drupal.settings.wysiwygEditor.configs["'. $theme .'"] = '. drupal_to_js($config) .';', 'inline');
/**
 * Load external plugins.
 * 
 * Note: tinyMCE.loadPlugin() need not be invoked more than once.
 */
function wysiwyg_editor_load_plugins() {
  static $plugins;
  
  if (!isset($plugins)) {
    $plugins = array();
    $module_plugins = module_invoke_all('wysiwyg_plugin', 'tinymce');
    foreach ($module_plugins as $name => $plugin) {
      if (!isset($plugin['internal'])) {
        $name = _wysiwyg_editor_plugin_name('remove', $name);
        $plugins[$name] = base_path() . $plugin['path'];
      }
    }
    drupal_add_js('Drupal.settings.wysiwygEditor.plugins = '. drupal_to_js($plugins) .';', 'inline');
  }
}

/**
 * Implementation of hook_user().
 */
function wysiwyg_editor_user($type, &$edit, &$user, $category = NULL) {
  if ($type == 'form' && $category == 'account' && user_access('access wysiwyg editor')) {
    $profile = wysiwyg_editor_user_get_profile($user);
    if (isset($profile->settings['user_choose']) && $profile->settings['user_choose']) {
      $form['wysiwyg'] = array(
        '#type' => 'fieldset',
        '#title' => t('Wysiwyg Editor settings'),
        '#weight' => 10,
        '#collapsible' => TRUE,
      $form['wysiwyg']['wysiwyg_editor_status'] = array(
        '#type' => 'select',
        '#title' => t('Default state'),
        '#default_value' => isset($user->wysiwyg_editor_status) ? $user->wysiwyg_editor_status : (isset($profile->settings['default']) ? $profile->settings['default'] : FALSE),
        '#options' => array(FALSE => t('Disabled'), TRUE => t('Enabled')),
        '#description' => t('Should rich-text editing be enabled or disabled by default in textarea fields?'),
      return array('wysiwyg_editor' => $form);
    }
  }
  if ($type == 'validate') {
    return array('wysiwyg_editor_status' => $edit['wysiwyg_editor_status']);
  }
}

/**
 * Filter for core text fields.
 *
 * @param $element
 *   The textarea element to be filtered.
 */
function wysiwyg_editor_filter_elements(&$element) {
  switch ($element['#name']) {
    // Disable Wysiwyg Editor for these textareas.
    case 'log': // book and page log
    case 'img_assist_pages':
    case 'caption': // signature
    case 'pages':
    case 'access_pages': //Wysiwyg Editor profile settings.
    case 'user_mail_welcome_body': // user config settings
    case 'user_mail_approval_body': // user config settings
    case 'user_mail_pass_body': // user config settings
    case 'synonyms': // taxonomy terms
    case 'description': // taxonomy terms
      $element['#wysiwyg'] = FALSE;
      break;

    // Force the 'simple' theme for some of the smaller textareas.
    case 'signature':
    case 'site_mission':
    case 'site_footer':
    case 'site_offline_message':
    case 'page_help':
    case 'user_registration_help':
    case 'user_picture_guidelines':
      $element['#wysiwyg_style'] = 'simple';
      break;
  }
}

/**
 * Grab the themes available to Wysiwyg Editor.
 * Wysiwyg Editor themes control the functionality and buttons that are available to a
 * user. Themes are only looked for within the default Wysiwyg Editor theme directory.
 *
 * @return
 *   An array of theme names.
 */
function _wysiwyg_editor_get_themes() {
  static $themes = array();

  if (!$themes) {
    $theme_loc = drupal_get_path('module', 'wysiwyg_editor') .'/tinymce/jscripts/tiny_mce/themes/';
    if (is_dir($theme_loc) && $dh = opendir($theme_loc)) {
      while (($file = readdir($dh)) !== false) {
        if (!in_array($file, array('.', '..', 'CVS')) && is_dir($theme_loc . $file)) {
          $themes[$file] = $file;
        }
      }
      closedir($dh);
      asort($themes);
    }
  }

  return $themes;
}

/**
 * Return plugin metadata from the plugin registry.
 *
 * @return
 *   An array for each plugin.
 */
function _wysiwyg_editor_get_buttons($metadata = FALSE) {
  include_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.plugins.inc';
  return module_invoke_all('wysiwyg_plugin', 'tinymce');
}
/**
 * Add or remove leading hiven to/of (external) plugin names.
 *
 * TinyMCE requires that external plugins that should not be loaded from
 * its own plugin repository include a leading hiven in the name.
 * 
 * @param string $op
 *   Operation to perform, 'add' or 'remove'.
 * @param string $name
 *   A plugin name.
 */
function _wysiwyg_editor_plugin_name($op, $name) {
  if ($op == 'add') {
    if (strpos($name, '-') !== 0) {
      return '-'. $name;
    return $name;
  }
  else if ($op == 'remove') {
    if (strpos($name, '-') === 0) {
      return substr($name, 1);
    }
    return $name;
 * Return an array of initial Wysiwyg Editor config options from the current role.
function wysiwyg_editor_config($profile, $theme) {
  if (!is_object($profile)) {
    return array();
  }
  $path_wysiwyg = drupal_get_path('module', 'wysiwyg_editor');
  // Hard-coded for now.
  $path_editor = $path_wysiwyg .'/tinymce/jscripts/tiny_mce';
  // Drupal theme path.
  $themepath = path_to_theme();
  $base_path = base_path();
  $settings = $profile->settings;

  // Build a default list of Wysiwyg Editor settings.
  $init = array();
  $init['mode'] = 'none';
  $init['theme'] = $theme;
  $init['relative_urls'] = FALSE;
  $init['document_base_url'] = "$base_path";
  $init['language'] = $settings['language'] ? $settings['language'] : 'en';
  $init['safari_warning'] = $settings['safari_message'] ? $settings['safari_message'] : FALSE;
  $init['entity_encoding'] = 'raw';
  $init['verify_html'] = $settings['verify_html'] ? $settings['verify_html'] : TRUE;
  $init['preformatted'] = $settings['preformatted'] ? $settings['preformatted'] : FALSE;
  $init['convert_fonts_to_spans'] = $settings['convert_fonts_to_spans'] ? $settings['convert_fonts_to_spans'] : TRUE;
  $init['remove_linebreaks'] = $settings['remove_linebreaks'] ? $settings['remove_linebreaks'] : TRUE;
  $init['apply_source_formatting'] = $settings['apply_source_formatting'] ? $settings['apply_source_formatting'] : FALSE;
  $init['theme_advanced_resize_horizontal'] = FALSE;
  $init['theme_advanced_resizing_use_cookie'] = FALSE;
  if (user_access('access tinymce imagemanager') && is_dir($path_editor .'/plugins/imagemanager/')) {
    // we probably need more security than this
    $init['file_browser_callback'] = "mcImageManager.filebrowserCallBack";
  }
  if (user_access('access tinymce filemanager') && is_dir($path_editor .'/plugins/filemanager/')) {
    // we probably need more security than this
    $init['file_browser_callback'] = "mcImageManager.filebrowserCallBack";
  }

  if ($settings['css_classes']) {
    $init['theme_advanced_styles'] = $settings['css_classes'];
  }

  if ($settings['css_setting'] == 'theme') {
    $css = $themepath .'/style.css';
    if (file_exists($css)) {
      $init['content_css'] = $base_path . $css;
    }
  }
  else if ($settings['css_setting'] == 'self') {
    $init['content_css'] = str_replace(array('%b', '%t'), array($base_path, $themepath), $settings['css_path']);
  }

  // Add theme-specific settings.
  switch ($theme) {
    case 'advanced':
      $init['plugins'] = array();
      $init['theme_advanced_toolbar_location'] = $settings['toolbar_loc'] ? $settings['toolbar_loc'] : 'bottom';
      $init['theme_advanced_toolbar_align'] = $settings['toolbar_align'] ? $settings['toolbar_align'] : 'left';
      $init['theme_advanced_path_location'] = $settings['path_loc'] ? $settings['path_loc'] : 'bottom';
      $init['theme_advanced_resizing'] = $settings['resizing'] ? $settings['resizing'] : TRUE;
      $init['theme_advanced_blockformats'] = $settings['block_formats'] ? $settings['block_formats'] : 'p,address,pre,h2,h3,h4,h5,h6';
      if (!empty($settings['buttons']) && is_array($settings['buttons'])) {
        $init['buttons'] = $init['extensions'] = $init['extended_valid_elements'] = array();
        // Find the enabled buttons and the button row they belong on.
        // Also map the plugin metadata for each button.
        $plugins = _wysiwyg_editor_get_buttons();
        foreach ($settings['buttons'] as $plugin => $buttons) {
          foreach ($buttons as $button => $enabled) {
            foreach (array('buttons', 'extensions') as $type) {
              // Skip disabled plugins.
              if (!isset($plugins[$plugin][$type][$button])) {
                continue;
              // Add buttons.
              if ($type == 'buttons') {
                $init[$type][] = $button;
              }
              // Add external plugins to the list of extensions.
              if ($type == 'buttons' && !isset($plugins[$plugin]['internal'])) {
                $init['extensions'][_wysiwyg_editor_plugin_name('add', $plugin)] = 1;
              }
              // Add internal buttons that also need to be loaded as extension.
              else if ($type == 'buttons' && isset($plugins[$plugin]['load'])) {
                $init['extensions'][$plugin] = 1;
              }
              // Add internal extensions.
              else if ($type == 'extensions') {
                $init[$type][$plugin] = 1;
              // Allow plugins to add valid HTML elements.
              if (!empty($plugins[$plugin]['extended_valid_elements'])) {
                $init['extended_valid_elements'] = array_merge($init['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']);
              }
              // Allow plugins to add or override configuration settings.
              if (!empty($plugins[$plugin]['options'])) {
                $init = array_merge($init, $plugins[$plugin]['options']);
        // Clean-up.
        // Hard-coded for TinyMCE for now.
        $init['extended_valid_elements'] = array_unique($init['extended_valid_elements']);
        if ($init['extensions']) {
          $init['plugins'] = array_keys($init['extensions']);
          unset($init['extensions']);
        } else {
          unset($init['extensions']);
        }

        // Shuffle buttons around so that row 1 always has the most buttons,
        // followed by row 2, etc. Note: These rows need to be set to NULL otherwise
        // Wysiwyg Editor loads its own buttons inherited from the theme.
        $init += array(
          'theme_advanced_buttons1' => array(),
          'theme_advanced_buttons2' => array(),
          'theme_advanced_buttons3' => array(),
        );
        $row = $button_count = 1;
        for ($i = 0; $i < count($init['buttons']); $i++) {
          if (!isset($init['theme_advanced_buttons'. $row])) {
            $init['theme_advanced_buttons'. $row] = array();
          }
          $init['theme_advanced_buttons'. $row][] = $init['buttons'][$i];
          if (strpos($init['buttons'][$i], 'font') !== FALSE) {
            $button_count += 5;
          }
          if ($button_count < 23) {
            $button_count++;
          }
          else {
            $row++;
            $button_count = 1;
          }
        }
        unset($init['buttons']);
        // Minimum number of buttons per row.
        $min_btns = 5;
        foreach (range($row, 1) as $row) {
          if (count($init['theme_advanced_buttons'. $row]) < $min_btns) {
            // Squish the rows together, since row is empty.
            $init['theme_advanced_buttons'. ($row - 1)] = array_merge($init['theme_advanced_buttons'. ($row - 1)], $init['theme_advanced_buttons'. $row]);
            $init['theme_advanced_buttons'. $row] = array();
          }
        }
      }
      break;
  }
  return $init;
}

/**
 * Return the name of the current user's default profile.
 */
function wysiwyg_editor_current_profile() {
  static $profile_name;
  if (!$profile_name) {
    global $user;
    $profile_name = db_result(db_query_range('SELECT p.name FROM {wysiwyg_editor_profile} p INNER JOIN {wysiwyg_editor_role} r ON r.name = p.name WHERE r.rid IN (%s) ORDER BY plugin_count DESC', implode(',', array_keys($user->roles)), 0, 1));
  }
  return $profile_name;
}

/**
 * Load all profiles. Just load one profile if $name is passed in.
 */
function wysiwyg_editor_profile_load($name = '') {
  static $profiles;
  if ($name === FALSE) {
    return FALSE;
  }
  if (!isset($profiles)) {
    $profiles = array();
    $roles = user_roles();
    $result = db_query('SELECT * FROM {wysiwyg_editor_profile}');
    while ($profile = db_fetch_object($result)) {
      $profile->settings = unserialize($profile->settings);
      $result2 = db_query("SELECT rid FROM {wysiwyg_editor_role} WHERE name = '%s'", $profile->name);
      $profile_roles = array();
      while ($role = db_fetch_object($result2)) {
        $profile_roles[$role->rid] = $roles[$role->rid];
      $profile->rids = $profile_roles;
      $profiles[$profile->name] = $profile;
  return ($name && isset($profiles[$name]) ? $profiles[$name] : ($name ? FALSE : $profiles));
 * Determine if Wysiwyg Editor has permission to be used on the current page.
 *
 * @return
 *   TRUE if can render, FALSE if not allowed.
 */
function _wysiwyg_editor_page_match($profile) {
  $page_match = FALSE;
  if (!is_object($profile)) {
    return FALSE;
  }
  // Kill Wysiwyg Editor if we're editing a textarea with PHP in it!
  // PHP input formats are #2 in the filters table.
  if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'edit') {
    $node = node_load(arg(1));
    if ($node->format == 2) {
      return FALSE;
    }
  }

  if ($profile->settings['access_pages']) {
    // If the PHP option wasn't selected
    if ($profile->settings['access'] < 2) {
      $path = drupal_get_path_alias($_GET['q']);
      $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($profile->settings['access_pages'], '/')) .')$/';
      $page_match = !($profile->settings['access'] xor preg_match($regexp, $path));
      $page_match = drupal_eval($profile->settings['access_pages']);
    }
  }
  // No pages were specified to block so show on all
  else {
    $page_match = TRUE;
  }

  return $page_match;
}

function wysiwyg_editor_user_get_profile($account) {
  $profile_name = db_result(db_query('SELECT s.name FROM {wysiwyg_editor_profile} s INNER JOIN {wysiwyg_editor_role} r ON r.name = s.name WHERE r.rid IN (%s)', implode(',', array_keys($account->roles))));
    return wysiwyg_editor_profile_load($profile_name);
function wysiwyg_editor_user_get_status($user, $profile) {
  $settings = $profile->settings;

  if ($settings['user_choose']) {
    $status = isset($user->wysiwyg_editor_status) ? $user->wysiwyg_editor_status : (isset($settings['default']) ? $settings['default'] : FALSE);
    $status = isset($settings['default']) ? $settings['default'] : FALSE;
  return $status;