// and Richard Bennett /** * @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; if (!$may_cache) { // For some crazy reason IE will only load this JS file if the absolute reference is given to it. drupal_set_html_head(''); //We may need this so TinyMCE has the right path to Drupal to invoke drupal tinymce plugins. //drupal_set_html_head(''); // 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(''); } } /** * 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; 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 %tinymce-directory. Please download TinyMCE, 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()); $output = form_group(t('Themes'), $group); $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 'blog' for the blog page and 'blog/*' for every personal blog. '<front>' 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; global $user; $theme_name = $user->tinymce_theme ? $user->tinymce_theme : variable_get('tinymce_theme', 'simple'); $user_status = $user->tinymce_status != NULL ? $user->tinymce_status : TRUE; if ($op == 'pre' && _tinymce_page_match() && $user_status) { global $base_url; // 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. '"'; } $tinymce_settings = implode(",\n ", $settings); $output = << tinyMCE.init({ $tinymce_settings }); EOD; // 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; 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']); } } /** * @addtogroup themeable * @{ */ /** * Customize a TinyMCE theme. * * @param init * 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'; return $init; } } /** @} End of addtogroup themeable */ /** * 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)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), array('|', '.*', '\1'. variable_get('site_frontpage', 'node') .'\2'), preg_quote($pages, '/')) .')$/'; $page_match = preg_match($regexp, $path); } return $page_match; } } ?>