Newer
Older
* Integrate Wysiwyg editors into Drupal.
*/
/**
* Implementation of hook_menu().
*/
$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>';
}
}
/**
* 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() {
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>');
$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'];
$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) {
// 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(
'#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) {
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';
$themepath = path_to_theme();
$base_path = base_path();
$settings = $profile->settings;
// Build a default list of Wysiwyg Editor settings.
$init['mode'] = 'none';
$init['theme'] = $theme;
$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') {
$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;
Daniel Kudwien
committed
$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 = '') {
if ($name === FALSE) {
return FALSE;
}
if (!isset($profiles)) {
$profiles = array();
$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];
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) {
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
$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))));
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);
$status = isset($settings['default']) ? $settings['default'] : FALSE;