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

/**
 * Implementation of hook_menu().
 */
function wysiwyg_editor_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/wysiwyg/profile',
      'title' => t('Wysiwyg Editor'),
      'callback' => 'wysiwyg_editor_admin',
      'description' => t('Configure the rich editor.'),
      'access' => user_access('administer site configuration'),
    );
  }
  return $items;
}

/**
 * Implementation of hook_help().
 */
function wysiwyg_editor_help($section) {
  switch ($section) {
    case 'admin/settings/wysiwyg/profile#pages':
      return "node/*\nuser/*\ncomment/*";
    case 'admin/settings/wysiwyg/profile':
    case 'admin/help#wysiwyg_editor':
      return t('<p style="font-size:x-small">$Revision$ $Date$</p>'.
        '<p>Wysiwyg Editor adds what-you-see-is-what-you-get (WYSIWYG) html editing to textareas. This editor can be enabled/disabled without reloading the page by clicking a link below each textarea.</p>
                <p>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.</p>
                <p>Lastly, only users with the <code>access wysiwyg editor</code> <a href="!url">permission</a> will be able to use Wysiwyg Editor.</p>', array('!url' => url('admin/user/access'))
  }
}

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

/**
 * Callback handler for admin pages; menu callback.
 */
function wysiwyg_editor_admin($arg = NULL) {
  require_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.admin.inc';
  $op = isset($_POST['op']) ? $_POST['op'] : NULL;
  $op = ($arg && !$op ? $arg : $op);
  return _wysiwyg_editor_admin($op);
}

/**
 * Implementation of hook_elements().
 */
function wysiwyg_editor_elements() {
  if (user_access('access wysiwyg editor')) {
    // Let Wysiwyg Editor potentially process each textarea.
    $type['textarea'] = array('#process' => array('wysiwyg_editor_process_textarea' => array()), '#wysiwyg' => TRUE, '#wysiwyg_style' => 'advanced');
 * 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.
    $profile = wysiwyg_editor_profile_load(wysiwyg_editor_current_profile());

    $enable  = t('enable rich-text');
    $disable = t('disable rich-text');

    $status = wysiwyg_editor_user_get_status($user, $profile);

    $no_wysiwyg = t('Your current web browser does not support WYSIWYG editing.');

    $wysiwyg_editor_mod_path = drupal_get_path('module', 'wysiwyg_editor');
    if (is_dir($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/imagemanager/') && user_access('access tinymce imagemanager')) {
      // if tinymce imagemanager is installed
      drupal_add_js($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/imagemanager/jscripts/mcimagemanager.js');
    if (is_dir($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/filemanager/') && user_access('access tinymce filemanager')) {
      // if tinymce filemanager is installed
      drupal_add_js($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/filemanager/jscripts/mcfilemanager.js');
    }

    // TinyMCE Compressor >= 1.0.9
    if (file_exists($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce_gzip.js')) {
      drupal_add_js($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce_gzip.js');
    }
    // TinyMCE Compressor < 1.0.9
    elseif (file_exists($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce_gzip.php')) {
      drupal_add_js($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce_gzip.php');
    }
    else {
      // For some crazy reason IE will only load this JS file if the absolute reference is given to it.
      drupal_add_js($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/tiny_mce.js');
    // 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($wysiwyg_editor_mod_path .'/wysiwyg_editor.js', 'footer');
    drupal_add_js(array('wysiwygEditor' => array('configs' => array(), 'showToggle' => ($profile->settings['show_toggle'] == 'true'), 'disable' => $disable, 'enable' => $enable, 'noWysiwyg' => $no_wysiwyg, 'linkText' => $link_text, 'status' => $status)), 'setting');
    // We have to do this because of some unfocused CSS in certain themes. See http://drupal.org/node/18879 for details
    drupal_set_html_head('<style type="text/css" media="all">.mceEditor img { display: inline; }</style>');
    $loaded = TRUE;
  }
}

/**
 * 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 (_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_set_config($element['#wysiwyg_style']);
      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_set_config($theme) {
  static $themes = array();
  // Only sent 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) {
        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');
    $themes[] = $theme;
  }
}

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

    // because the settings are saved as strings we need to test for the string 'true'
    if ($profile->settings['user_choose'] == 'true') {
      $form['wysiwyg'] = array(
        '#type' => 'fieldset',
        '#title' => t('Wysiwyg Editor rich-text settings'),
        '#weight' => 10,
        '#collapsible' => TRUE,
      $form['wysiwyg']['wysiwyg_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.
 *
 * We also scrape each plugin's *.js file for the human friendly name and help
 * text URL of each plugin.
 *
 * @return
 *   An array for each plugin.
 */
function _wysiwyg_editor_get_buttons($skip_metadata = TRUE) {
  include_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.plugins.inc';
  $plugins = _wysiwyg_editor_plugins();

  if ($skip_metadata == FALSE && is_array($plugins)) {
    foreach ($plugins as $name => $plugin) {
      $file = drupal_get_path('module', 'wysiwyg_editor') .'/tinymce/jscripts/tiny_mce/plugins/'. $name .'/editor_plugin_src.js';
      // Grab the plugin metadata by scanning the *.js file.
      if (file_exists($file)) {
        $has_longname = FALSE;
        foreach ($lines as $line) {
          if ($has_longname && $has_infourl) {
            break;
          }
          if (strstr($line, 'longname')) {
            $start = strpos($line, "'") + 1;
            $end = strrpos($line, "'") - $start;
            $metadata[$name]['longname'] = substr($line, $start, $end);
            $has_longname = TRUE;
          }
          elseif (strstr($line, 'infourl')) {
            $start = strpos($line, "'") + 1;
            $end = strrpos($line, "'") - $start;
            $metadata[$name]['infourl'] = substr($line, $start, $end);
            $has_infourl = TRUE;
          }
        }
      }

      // Find out the buttons a plugin has.
      foreach ($plugin as $k => $v) {
        if (strstr($k, 'theme_advanced_buttons')) {
          $metadata[$name]['buttons'] = array_merge((array) $metadata[$name]['buttons'], $plugin[$k]);
        }
      }
    }
    return $metadata;
  }

  return $plugins;
}

/**
 * Return an array of initial Wysiwyg Editor config options from the current role.
function wysiwyg_editor_config($profile, $theme) {
  global $user;

  // Drupal theme path.
  $themepath = path_to_theme() .'/';
  $host = base_path();

  $settings = $profile->settings;

  // Build a default list of Wysiwyg Editor settings.
  $init['mode'] = 'none';
  $init['theme'] = $theme;
  $init['relative_urls'] = 'false';
  $init['document_base_url'] = "$host";
  $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'] : 'false';
  $init['preformatted'] = $settings['preformatted'] ? $settings['preformatted'] : 'false';
  $init['convert_fonts_to_spans'] = $settings['convert_fonts_to_spans'] ? $settings['convert_fonts_to_spans'] : 'false';
  $init['remove_linebreaks'] = $settings['remove_linebreaks'] ? $settings['remove_linebreaks'] : 'true';
  $init['apply_source_formatting'] = $settings['apply_source_formatting'] ? $settings['apply_source_formatting'] : 'true';
  $init['theme_advanced_resize_horizontal'] = 'false';
  $init['theme_advanced_resizing_use_cookie'] = 'false';


  $wysiwyg_editor_mod_path = drupal_get_path('module', 'wysiwyg_editor');
  if (is_dir($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/imagemanager/') && user_access('access tinymce imagemanager')) {
    // we probably need more security than this
    $init['file_browser_callback'] = "mcImageManager.filebrowserCallBack";
  }
  if (is_dir($wysiwyg_editor_mod_path .'/tinymce/jscripts/tiny_mce/plugins/filemanager/') && user_access('access tinymce 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'] = $host . $css;
    }
  }
  else if ($settings['css_setting'] == 'self') {
    $init['content_css'] = str_replace(array('%h', '%t'), array($host, $themepath), $settings['css_path']);
  }

  // Add theme-specific settings.
  switch ($theme) {
    case 'advanced':

      $init['theme_advanced_resize_horizontal'] = 'false';
      $init['theme_advanced_resizing_use_cookie'] = 'false';
      $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,h1,h2,h3,h4,h5,h6';

      if (is_array($settings['buttons'])) {
        // This gives us the $plugins variable.
        $plugins = _wysiwyg_editor_get_buttons();

        // Find the enabled buttons and the mce row they belong on. Also map the
        // plugin metadata for each button.
        $plugin_tracker = array();
        // Plugin name
        foreach ($plugins as $rname => $rplugin) {
          // Wysiwyg Editor key
          foreach ($rplugin as $mce_key => $mce_value) {
            // Buttons
            foreach ($mce_value as $k => $v) {
              if (isset($settings['buttons'][$rname .'-'. $v])) {
                // Font isn't a true plugin, rather it's buttons made available by the advanced theme
                if (!in_array($rname, $plugin_tracker) && $rname != 'font') {
                  $plugin_tracker[] = $rname;
                }
                $init[$mce_key][] = $v;
              }
            }
          }
          // Some advanced plugins only have an $rname and no buttons
          if (isset($settings['buttons'][$rname])) {
            if (!in_array($rname, $plugin_tracker)) {
              $plugin_tracker[] = $rname;
            }
        // Add the rest of the Wysiwyg Editor config options to the $init array for each button.
        if (is_array($plugin_tracker)) {
          foreach ($plugin_tracker as $pname) {
            if ($pname != 'default') {
              $init['plugins'][] = $pname;
            }
            foreach ($plugins[$pname] as $mce_key => $mce_value) {
              // Don't overwrite buttons or extended_valid_elements
              if ($mce_key == 'extended_valid_elements') {
                // $mce_value is an array for extended_valid_elements so just grab the first element in the array (never more than one)
                $init[$mce_key][] = $mce_value[0];
              }
              else if (!strstr($mce_key, 'theme_advanced_buttons')) {
                $init[$mce_key] = $mce_value;
              }
            }
          }
        }

        // Cleanup
        foreach ($init as $mce_key => $mce_value) {
          if (is_array($mce_value)) {
            $mce_value = array_unique($mce_value);
          }
          $init[$mce_key] = $mce_value;
        }

        // 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.
        if (!isset($init['theme_advanced_buttons1'])) {
          $init['theme_advanced_buttons1'] = array();
        }
        if (!isset($init['theme_advanced_buttons2'])) {
          $init['theme_advanced_buttons2'] = array();
        }
        if (!isset($init['theme_advanced_buttons3'])) {
          $init['theme_advanced_buttons3'] = array();
        }
        // Minimum number of buttons per row.
        $min_btns = 5;
        $num1     = count($init['theme_advanced_buttons1']);
        $num2     = count($init['theme_advanced_buttons2']);
        $num3     = count($init['theme_advanced_buttons3']);

        if ($num3 < $min_btns) {
          $init['theme_advanced_buttons2'] = array_merge($init['theme_advanced_buttons2'], $init['theme_advanced_buttons3']);
          $init['theme_advanced_buttons3'] = array();
          $num2 = count($init['theme_advanced_buttons2']);
        }
        if ($num2 < $min_btns) {
          $init['theme_advanced_buttons1'] = array_merge($init['theme_advanced_buttons1'], $init['theme_advanced_buttons2']);
          // Squish the rows together, since row 2 is empty
          $init['theme_advanced_buttons2'] = $init['theme_advanced_buttons3'];
          $init['theme_advanced_buttons3'] = array();
          $num1 = count($init['theme_advanced_buttons1']);
        }
        if ($num1 < $min_btns) {
          $init['theme_advanced_buttons1'] = array_merge($init['theme_advanced_buttons1'], $init['theme_advanced_buttons2']);
          // Squish the rows together, since row 2 is empty
          $init['theme_advanced_buttons2'] = $init['theme_advanced_buttons3'];
          $init['theme_advanced_buttons3'] = 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('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($user->roles))));
  }
  return $profile_name;
}

/**
 * Load all profiles. Just load one profile if $name is passed in.
 */
function wysiwyg_editor_profile_load($name = '') {
  static $profiles = array();

  if (!$profiles) {
    $roles = user_roles();
    $result = db_query('SELECT * FROM {wysiwyg_editor_profile}');
    while ($data = db_fetch_object($result)) {
      $data->settings = unserialize($data->settings);
      $result2 = db_query("SELECT rid FROM {wysiwyg_editor_role} WHERE name = '%s'", $data->name);
      $role = array();
      while ($r = db_fetch_object($result2)) {
        $role[$r->rid] = $roles[$r->rid];
      }
      $data->rids = $role;

      $profiles[$data->name] = $data;
    }
  }

  return ($name ? $profiles[$name] : $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($edit) {
  // Kill Wysiwyg Editor if we're editing a textarea with PHP in it!
  // PHP input formats are #2 in the filters table.
  if (is_numeric(arg(1)) && arg(2) == 'edit') {
    $node = node_load(arg(1));
    if ($node->format == 2) {
      return FALSE;
    }
  }

  if ($edit->settings['access_pages']) {
    // If the PHP option wasn't selected
    if ($edit->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($edit->settings['access_pages'], '/')) .')$/';
      $page_match = !($edit->settings['access'] xor preg_match($regexp, $path));
    }
    else {
      $page_match = drupal_eval($edit->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');
  }
  else {
    $status = isset($settings['default']) ? $settings['default'] : 'false';
  }

  return $status == 'true' ? TRUE : FALSE;
}