Skip to content
tinymce.module 9.82 KiB
Newer Older
<?php
// $Id$
// A collaborative project by Matt Westgate <drupal at asitis dot org>
// and Richard Bennett <richard.b@gritechnologies.com>

/**
 * @file
 * Integrate the TinyMCE editor (http://tinymce.moxiecode.com/) into Drupal.
 */

/**
 * Implementation of hook_menu().
 *
 * Add the JavaScript file to the page head. Doing this in a menu hook is a
 * little less expensive that in *_init since we don't need to load the head for
 * Drupal-cached pages.
 *
 */
function tinymce_menu($may_cache) {
  global $base_url;
Matt Westgate's avatar
Matt Westgate committed
    // For some crazy reason IE will only load this JS file if the absolute reference is given to it.
    drupal_set_html_head('<script language="javascript" type="text/javascript" src="'. $base_url . '/'. drupal_get_path('module', 'tinymce') .'/tinymce/jscripts/tiny_mce/tiny_mce.js"></script>');
    //We may need this so TinyMCE has the right path to Drupal to invoke drupal tinymce plugins.
    //drupal_set_html_head('<script language="javascript" type="text/javascript"> var baseUrl = "'. $base_url .'"; </script>');
    // We have to do this becuase 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>');
  }
}

/**
 * Implementation of hook_help().
 */
function tinymce_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('The TinyMCE Javascript HTML WYSIWYG editor.');

    case 'admin/settings/tinymce#pages':
      return "node/*\nuser/*\ncomment/*";
  }
}

/**
 * Implementation of hook_perm().
 */
function tinymce_perm() {
  return array('access tinymce', 'choose own tinymce theme');
/**
 * Implementation of hook_img_assist_head().
 */
function tinymce_img_assist_head() {
  $popup_path = drupal_get_path('module', 'tinymce'). '/tinymce/jscripts/tiny_mce';

$output = <<<EOD
<script language="javascript" src="$popup_path/tiny_mce_popup.js"></script>
<script language="javascript">
  function insertImage(form) {
    if (window.opener) {
      var thumb = (form['edit-thumb'].checked) ? 1 : 0;

      if (thumb == 1) {
        var src    = form['edit[thumbpath]'].value;
        var width  = form['edit[thumbWidth]'].value;
        var height = form['edit[thumbHeight]'].value;
      }
      else {
        var src    = form['edit[filepath]'].value;
        var width  = form['edit[width]'].value  != '' ? form['edit[width]'].value  : form['edit[origWidth]'].value;
        var height = form['edit[height]'].value != '' ? form['edit[height]'].value : form['edit[origHeight]'].value;
      }
      var alt = form['edit[alt]'].value;

      window.opener.tinyMCE.insertImage(src, alt, '', '', '', width, height);
    }
  }
</script>
EOD;

  return $output;
}

/**
 * Implementation of hook_img_assist_on_submit().
 */
function tinymce_img_assist_on_submit() {
  return 'parent.insertImage(this.form);';
}

/**
 * Implementation of hook_settings().
 */
function tinymce_settings() {
  drupal_set_title(t('TinyMCE settings (%revision)', array('%revision' => '$Revision$')));

  //Check if TinyMCE is installed.
  $tinymce_loc = drupal_get_path('module', 'tinymce') .'/tinymce/';
  if (!is_dir($tinymce_loc)) {
    drupal_set_message(t('Could not find the TinyMCE engine installed at <strong>%tinymce-directory</strong>. Please <a href="http://tinymce.moxiecode.com/">download TinyMCE</a>, uncompress it and copy the folder into %tinymce-path.', array('%tinymce-path' => drupal_get_path('module', 'tinymce'), '%tinymce-directory' => drupal_get_path('module', 'tinymce'). '/tinymce/')), 'error');
  }

  $group = form_radios(t('Default theme'), 'tinymce_theme', variable_get('tinymce_theme', 'simple'), _tinymce_get_themes());
  $group = form_radios(t('access tinymce'), 'tinymce_all', variable_get('tinymce_all', 1), array(t('on specific pages'), t('on all textareas')));
  if (!variable_get('tinymce_all', 1)) {
    $group .= form_textarea(t('Pages'), 'tinymce_pages', variable_get('tinymce_pages', tinymce_help('admin/settings/tinymce#pages')), 40, 5, t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '<em>blog</em>' for the blog page and '<em>blog/*</em>' for every personal blog. '<em>&lt;front&gt;</em>' is the front page."));
  }
  $output .= form_group('', $group);
  return $output;
}

/**
 * Implementation of hook_textarea().
 */
function tinymce_textarea($op, $name) {
  static $is_running = FALSE;
  if (!user_access('access tinymce')) return NULL;
  $theme_name = $user->tinymce_theme ? $user->tinymce_theme : variable_get('tinymce_theme', 'simple');
Matt Westgate's avatar
Matt Westgate committed
  $user_status = $user->tinymce_status != NULL ? $user->tinymce_status : TRUE;
Matt Westgate's avatar
Matt Westgate committed
  if ($op == 'pre' && _tinymce_page_match() && $user_status) {
    // Build a default list of TinyMCE settings.
    $init['mode'] = 'textareas';
    $init['theme'] = $theme_name;
    $init['document_base_url'] = "$base_url/";

    // Merge and overrivide user-defined TinyMCE settings.
    $init = array_merge($init, (array) theme('tinymce_theme', $init, $textarea_name, $theme_name, $is_running));
    foreach ($init as $k => $v) {
      $settings[] = $k. ' : "'. $v. '"';
    }
Matt Westgate's avatar
Matt Westgate committed
    $tinymce_settings = implode(",\n    ", $settings);
$output = <<<EOD
<script language="javascript" type="text/javascript">
  tinyMCE.init({
    // We only invoke TinyMCE once per request, not once per textarea. We could
    // do this check earlier in the conditional, but it's probably wise to let
    // the themed functions know what's going on.
    if (!$is_running) {
      $is_running = TRUE;
Matt Westgate's avatar
Matt Westgate committed
      drupal_set_html_head($output);
  }
}

/**
 * Implementation of hook_user().
 */
function tinymce_user($type, &$edit, &$user, $category = NULL) {
  if ($type == 'form' && $category == 'account' && user_access('access tinymce')) {
    $user_status = $edit['tinymce_status'] != NULL ? $edit['tinymce_status'] : ($user->tinymce_status != NULL ? $user->tinymce_status : 1);
    $form = form_radios(t('Status'), 'tinymce_status', $user_status, array(t('Disabled'), t('Enabled')), t('Would you like to enable rich-text editing of your content?'));
    if ($user_status && user_access('choose own tinymce theme')) {
      $form .= form_radios(t('Default theme'), 'tinymce_theme', $edit['tinymce_theme'] ? $edit['tinymce_theme'] : variable_get('tinymce_theme', 'simple'), _tinymce_get_themes());
    }
    return array(array('title' => t('TinyMCE settings'), 'data' => $form));
  }
  if ($type == 'validate') {
    return array('tinymce_theme' => $edit['tinymce_theme'], 'tinymce_status' => $edit['tinymce_status']);
  }
}

 *   An array of settings TinyMCE should invoke a theme. You may override any
 *   of the TinyMCE settings. Details here:
 *
 *    http://tinymce.moxiecode.com/wrapper.php?url=tinymce/docs/using.htm
 *
 * @param textarea_name
 *   The name of the textarea TinyMCE wants to enable.
 *
 * @param theme_name
 *   The default theme name to be enabled for this textarea. The sitewide
 *   default is 'simple', but the user may also override this.
 *
 * @param is_running
 *   A boolean flag that identifies id TinyMCE is currently running for this
 *   request life cycle. If it's already running then anything returned by this
 *   will be ignored. This is necessary since TinyMCE works by being invoked
 *   once per request and not once per textarea.
function theme_tinymce_theme($init, $textarea_name, $theme_name, $is_running) {
  switch ($theme_name) {
    case 'advanced':
      $init['extended_valid_elements'] = 'a[href|target|name]';
      $init['theme_advanced_buttons3_add_before'] = 'tablecontrols,separator';
      $init['plugins'] = module_exist('img_assist') ? 'drupalimage,table,emotions,print' : 'table,emotions,print';
      $init['theme_advanced_buttons3_add'] = 'emotions,separator,print';
/**
 * Grab the themes available to TinyMCE.
 *
 * TinyMCE themes control the functionality and buttons that are available to a
 * user. Themes are only looked for within the default TinyMCE theme directory.
 *
 * @return
 *   An array of theme names.
 */
function _tinymce_get_themes() {
  static $themes = array();

  if (!$themes) {
    $theme_loc = drupal_get_path('module', 'tinymce') .'/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;
}

/**
 * Determine if TinyMCE can render the current page.
 *
 * @return
 *   TRUE if can render, FALSE if not allowed.
 */
function _tinymce_page_match() {
  $edit = $_POST['edit'];

  //Kill TinyMCE if we're editing a textarea with PHP in it!
  if ($edit) {
    if ($edit['format'] == 2) {
      return FALSE;
    }
  }
  else {
    // PHP input formats are #2 in the filters table.
    preg_match("|^node/(\d+)(/edit)$|", $_GET['q'], $match);
    if (intval($match[1]) > 0) {
      if (db_result(db_query('SELECT format FROM {node} WHERE nid = %d AND format = 2', $match[1]))) {
        return FALSE;
      }
    }
  }

  if (variable_get('tinymce_all', 1)) {
    return TRUE;
  }
  else {
    $page_match = FALSE;
    $pages = variable_get('tinymce_pages', tinymce_help('admin/settings/tinymce#pages'));
    if ($pages) {
      $path = drupal_get_path_alias($_GET['q']);
      $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. variable_get('site_frontpage', 'node') .'\2'), preg_quote($pages, '/')) .')$/';
      $page_match = preg_match($regexp, $path);
    }
    return $page_match;
  }
}