'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': case 'admin/help#wysiwyg_editor': $output = '

'. 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 user permission are able to use Wysiwyg Editor.', array('%permission' => 'access wysiwyg editor', '!url' => url('admin/user/access'))) .'

'; 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; } /** * Callback handler for admin pages; menu callback. */ function wysiwyg_editor_admin($arg = '') { require_once drupal_get_path('module', 'wysiwyg_editor') .'/wysiwyg_editor.admin.inc'; $op = isset($_POST['op']) ? $_POST['op'] : ''; $op = ($arg && !$op ? $arg : $op); return _wysiwyg_editor_admin($op); } /** * 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' => array()), '#wysiwyg' => TRUE, '#wysiwyg_style' => 'advanced'); } return $type; } /** * 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. if (!$loaded) { $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'); $exec_mode = 'gzip'; } // TinyMCE Compressor < 1.0.9 elseif (file_exists($path_editor .'/tiny_mce_gzip.php')) { drupal_add_js($path_editor .'/tiny_mce_gzip.php'); $exec_mode = 'gzip'; } else { // @todo Turn this into a module setting. drupal_add_js($path_editor .'/tiny_mce.js'); $exec_mode = ''; // drupal_add_js($path_editor .'/tiny_mce_src.js'); // $exec_mode = 'src'; } // 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, // If JS compression is enabled, at least TinyMCE is unable to determine // its own base path and exec mode since it can't find the script name. 'editorBasePath' => base_path() . $path_editor, 'execMode' => $exec_mode, )), 'setting'); // Add our stylesheet to stack editor buttons into one row. drupal_add_css($path_wysiwyg .'/wysiwyg_editor.css'); $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 ($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']; } else { $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; } switch ($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; } } /** * 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, '#collapsed' => 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) { global $user; 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'] = implode(';', array_filter(explode("\n", str_replace("\r", '', $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']); } // Note: These rows need to be set to NULL otherwise TinyMCE loads its // own buttons as defined in advanced theme. $init += array( 'theme_advanced_buttons1' => array(), 'theme_advanced_buttons2' => array(), 'theme_advanced_buttons3' => array(), ); for ($i = 0; $i < count($init['buttons']); $i++) { $init['theme_advanced_buttons1'][] = $init['buttons'][$i]; } unset($init['buttons']); } 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 is passed from wysiwyg_editor_current_profile(), it may be FALSE, // and thus, the user is not allowed to use a wysiwyg editor. 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)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), 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)); } else { $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)))); if ($profile_name) { return wysiwyg_editor_profile_load($profile_name); } else { return FALSE; } } 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; }