diff --git a/editors/ckeditor.inc b/editors/ckeditor.inc index 8ac3ceb..f796878 100644 --- a/editors/ckeditor.inc +++ b/editors/ckeditor.inc @@ -28,6 +28,11 @@ function wysiwyg_ckeditor_editor() { ), ), ), + 'toolbar support' => array( + 'multi groups' => TRUE, + 'multi rows' => TRUE, + 'separator' => TRUE, + ), 'version callback' => 'wysiwyg_ckeditor_version', 'themes callback' => 'wysiwyg_ckeditor_themes', 'settings callback' => 'wysiwyg_ckeditor_settings', @@ -189,52 +194,74 @@ function wysiwyg_ckeditor_settings($editor, $config, $theme) { $settings['toolbarLocation'] = $config['toolbar_loc']; } - if (!empty($config['buttons'])) { - $extra_plugins = array(); - $settings['toolbar'] = array(); - $plugins = wysiwyg_get_plugins($editor['name']); - foreach ($config['buttons'] as $plugin => $buttons) { - foreach ($buttons as $button => $enabled) { - // Iterate separately over buttons and extensions properties. - foreach (array('buttons', 'extensions') as $type) { - // Skip unavailable plugins. - if (!isset($plugins[$plugin][$type][$button])) { - continue; - } - // Add buttons. - if ($type == 'buttons') { - $settings['toolbar'][] = $button; - } - // Add external Drupal plugins to the list of extensions. - if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) { - $extra_plugins[] = $button; - } - // Add external plugins to the list of extensions. - elseif ($type == 'buttons' && empty($plugins[$plugin]['internal'])) { - $extra_plugins[] = $plugin; - } - // Add internal buttons that also need to be loaded as extension. - elseif ($type == 'buttons' && !empty($plugins[$plugin]['load'])) { - $extra_plugins[] = $plugin; - } - // Add plain extensions. - elseif ($type == 'extensions' && !empty($plugins[$plugin]['load'])) { - $extra_plugins[] = $plugin; - } - // Allow plugins to add or override global configuration settings. - if (!empty($plugins[$plugin]['options'])) { - $settings = array_merge($settings, $plugins[$plugin]['options']); - } - } + $extra_plugins = array(); + $toolbar = array(); + $plugins = wysiwyg_get_plugins($editor['name']); + + foreach ((array)$config['extensions'] as $plugin => $buttons) { + foreach ($buttons as $button => $enabled) { + if (!isset($plugins[$plugin]['extensions'][$button])) { + continue; + } + + // Add plain extensions. + if (!empty($plugins[$plugin]['load'])) { + $extra_plugins[] = $plugin; + } + + // Allow plugins to add or override global configuration settings. + if (!empty($plugins[$plugin]['options'])) { + $settings = array_merge($settings, $plugins[$plugin]['options']); } } - if (!empty($extra_plugins)) { - $settings['extraPlugins'] = implode(',', $extra_plugins); - } - // For now, all buttons are placed into one row. - if (!empty($settings['toolbar'])) { - $settings['toolbar'] = array($settings['toolbar']); + } + + foreach ((array)$config['toolbar'] as $row) { + foreach ($row as $buttons) { + $group = array(); + + foreach ($buttons as $button_config) { + $plugin = $button_config['plugin']; + $button = $button_config['button']; + + if ($button == WYSIWYG_SEPARATOR) { + $group[] = '-'; + continue; + } + if (!isset($plugins[$plugin]['buttons'][$button])) { + continue; + } + + $group[] = $button; + + // Add external Drupal plugins to the list of extensions. + if (!empty($plugins[$plugin]['proxy']) || empty($plugins[$plugin]['internal']) || !empty($plugins[$plugin]['load'])) { + $extra_plugins[] = $button; + } + + // Allow plugins to add or override global configuration settings. + if (!empty($plugins[$plugin]['options'])) { + $settings = array_merge($settings, $plugins[$plugin]['options']); + } + } + + $toolbar[] = $group; } + + $toolbar[] = '/'; // toolbar line break + } + + // Remove the last '/'. + if (end($toolbar) == '/') { + array_pop($toolbar); + } + + if (!empty($extra_plugins)) { + $settings['extraPlugins'] = implode(',', array_unique($extra_plugins)); + } + + if (!empty($toolbar)) { + $settings['toolbar'] = $toolbar; } return $settings; diff --git a/editors/fckeditor.inc b/editors/fckeditor.inc index 3da106c..f6d994b 100644 --- a/editors/fckeditor.inc +++ b/editors/fckeditor.inc @@ -20,6 +20,11 @@ function wysiwyg_fckeditor_editor() { 'files' => array('fckeditor.js'), ), ), + 'toolbar support' => array( + 'multi groups' => TRUE, + 'multi rows' => TRUE, + 'separator' => TRUE, + ), 'version callback' => 'wysiwyg_fckeditor_version', 'themes callback' => 'wysiwyg_fckeditor_themes', 'settings callback' => 'wysiwyg_fckeditor_settings', @@ -132,40 +137,66 @@ function wysiwyg_fckeditor_settings($editor, $config, $theme) { if ($config['css_setting'] == 'theme') { $settings['EditorAreaCSS'] = implode(',', wysiwyg_get_css()); } - else if ($config['css_setting'] == 'self' && isset($config['css_path'])) { + elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) { $settings['EditorAreaCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme())); } } - if (!empty($config['buttons'])) { - // Use our custom toolbar set. - $settings['ToolbarSet'] = 'Wysiwyg'; - // Populate our custom toolbar set for fckeditor.config.js. - $settings['buttons'] = array(); - $plugins = wysiwyg_get_plugins($editor['name']); - foreach ($config['buttons'] as $plugin => $buttons) { - foreach ($buttons as $button => $enabled) { - // Iterate separately over buttons and extensions properties. - foreach (array('buttons', 'extensions') as $type) { - // Skip unavailable plugins. - if (!isset($plugins[$plugin][$type][$button])) { - continue; - } - // Add buttons. - if ($type == 'buttons') { - $settings['buttons'][] = $button; - } - // Allow plugins to add or override global configuration settings. - if (!empty($plugins[$plugin]['options'])) { - $settings = array_merge($settings, $plugins[$plugin]['options']); - } - } + $plugins = wysiwyg_get_plugins($editor['name']); + $toolbar = array(); + + foreach ((array)$config['extensions'] as $plugin => $buttons) { + foreach ($buttons as $button => $enabled) { + if (!isset($plugins[$plugin]['extensions'][$button])) { + continue; + } + + // Allow plugins to add or override global configuration settings. + if (!empty($plugins[$plugin]['options'])) { + $settings = array_merge($settings, $plugins[$plugin]['options']); } } - // For now, all buttons are placed into one row. - if (!empty($settings['buttons'])) { - $settings['buttons'] = array($settings['buttons']); + } + + foreach ((array)$config['toolbar'] as $row) { + foreach ($row as $buttons) { + $group = array(); + + foreach ($buttons as $button_config) { + $plugin = $button_config['plugin']; + $button = $button_config['button']; + + if ($button == WYSIWYG_SEPARATOR) { + $group[] = '-'; + continue; + } + if (!isset($plugins[$plugin]['buttons'][$button])) { + continue; + } + + $group[] = $button; + + // Allow plugins to add or override global configuration settings. + if (!empty($plugins[$plugin]['options'])) { + $settings = array_merge($settings, $plugins[$plugin]['options']); + } + } + + $toolbar[] = $group; } + + $toolbar[] = '/'; + } + + // Remove the last '/'. + if (end($toolbar) == '/') { + array_pop($toolbar); + } + + if (!empty($toolbar)) { + // Use our custom toolbar set. + $settings['ToolbarSet'] = 'DrupalWysiwyg'; + $settings['ToolbarSets']['DrupalWysiwyg'] = $toolbar; } return $settings; diff --git a/editors/js/fckeditor.config.js b/editors/js/fckeditor.config.js index 2c3415f..77ca0eb 100644 --- a/editors/js/fckeditor.config.js +++ b/editors/js/fckeditor.config.js @@ -17,27 +17,18 @@ var pluginSettings = Drupal.settings.wysiwyg.plugins[wysiwygFormat]; * Apply format-specific settings. */ for (var setting in wysiwygSettings) { - if (setting == 'buttons') { - // Apply custom Wysiwyg toolbar for this format. - // FCKConfig.ToolbarSets['Wysiwyg'] = wysiwygSettings.buttons; + FCKConfig[setting] = wysiwygSettings[setting]; +} - // Temporarily stack buttons into multiple button groups and remove - // separators until #277954 is solved. - FCKConfig.ToolbarSets['Wysiwyg'] = []; - for (var i = 0; i < wysiwygSettings.buttons[0].length; i++) { - FCKConfig.ToolbarSets['Wysiwyg'].push([wysiwygSettings.buttons[0][i]]); - } - FCKTools.AppendStyleSheet(document, '#xToolbar .TB_Start { display:none; }'); - // Set valid height of select element in silver and office2003 skins. - if (FCKConfig.SkinPath.match(/\/office2003\/$/)) { - FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 24px; } #xToolbar .TB_End { display: none; }'); - } - else if (FCKConfig.SkinPath.match(/\/silver\/$/)) { - FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 27px; }'); - } +// Add custom stylesheet to editor toolbar. +FCKTools.AppendStyleSheet(document, '#xToolbar .TB_Start { display:none; }'); +if (FCKConfig.ToolbarSet == 'DrupalWysiwyg') { + // Set valid height of select element in silver and office2003 skins. + if (FCKConfig.SkinPath.match(/\/office2003\/$/)) { + FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 24px; } #xToolbar .TB_End { display: none; }'); } - else { - FCKConfig[setting] = wysiwygSettings[setting]; + else if (FCKConfig.SkinPath.match(/\/silver\/$/)) { + FCKTools.AppendStyleSheet(document, '#xToolbar .SC_FieldCaption { height: 27px; }'); } } diff --git a/editors/js/tinymce-3.js b/editors/js/tinymce-3.js index c90ffd5..833b1ec 100644 --- a/editors/js/tinymce-3.js +++ b/editors/js/tinymce-3.js @@ -55,15 +55,7 @@ Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) { ed.onEvent.add(function(ed, e) { Drupal.wysiwyg.activeId = ed.id; }); - // Make toolbar buttons wrappable (required for IE). - ed.onPostRender.add(function (ed) { - var $toolbar = $('
'); - $('#' + ed.editorContainer + ' table.mceToolbar > tbody > tr > td').each(function () { - $('
').addClass(this.className).append($(this).children()).appendTo($toolbar); - }); - $('#' + ed.editorContainer + ' table.mceLayout td.mceToolbar').append($toolbar); - $('#' + ed.editorContainer + ' table.mceToolbar').remove(); - }); + // Attach editor. ed.render(); }; diff --git a/editors/markitup.inc b/editors/markitup.inc index d4e6e5b..e0136a7 100644 --- a/editors/markitup.inc +++ b/editors/markitup.inc @@ -25,6 +25,7 @@ function wysiwyg_markitup_editor() { 'files' => array('jquery.markitup.pack.js'), ), ), + 'toolbar support' => TRUE, 'version callback' => 'wysiwyg_markitup_version', 'themes callback' => 'wysiwyg_markitup_themes', 'settings callback' => 'wysiwyg_markitup_settings', @@ -147,11 +148,13 @@ function wysiwyg_markitup_settings($editor, $config, $theme) { 'call' => 'preview', ), ); - if (!empty($config['buttons'])) { - foreach ($config['buttons'] as $plugin) { - foreach ($plugin as $button => $enabled) { - if (isset($default_buttons[$button])) { - $settings['markupSet'][$button] = $default_buttons[$button]; + if (!empty($config['toolbar'])) { + $settings['markupSet'] = array(); + + foreach ($config['toolbar'] as $row) { + foreach ($row as $group) { + foreach ($group as $button) { + $settings['markupSet'][$button['button']] = $default_buttons[$button['button']]; } } } diff --git a/editors/nicedit.inc b/editors/nicedit.inc index 30ce42d..2918125 100644 --- a/editors/nicedit.inc +++ b/editors/nicedit.inc @@ -20,6 +20,7 @@ function wysiwyg_nicedit_editor() { 'files' => array('nicEdit.js'), ), ), + 'toolbar support' => TRUE, 'version callback' => 'wysiwyg_nicedit_version', 'settings callback' => 'wysiwyg_nicedit_settings', 'plugin callback' => 'wysiwyg_nicedit_plugins', @@ -66,12 +67,16 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) { ); // Add configured buttons or all available. - if (!empty($config['buttons'])) { + if (!empty($config['toolbar'])) { $buttons = array(); - foreach ($config['buttons'] as $plugin) { - $buttons = array_merge($buttons, $plugin); + foreach ($config['toolbar'] as $row) { + foreach ($row as $group) { + foreach ($group as $button) { + $buttons[] = $button['button']; + } + } } - $settings['buttonList'] = array_keys($buttons); + $settings['buttonList'] = $buttons; } else { $settings['fullPanel'] = TRUE; @@ -85,7 +90,7 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) { $settings['externalCSS'] = base_path() . $css; } } - else if ($config['css_setting'] == 'self' && isset($config['css_path'])) { + elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) { $settings['externalCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme())); } } diff --git a/editors/openwysiwyg.inc b/editors/openwysiwyg.inc index 814332f..1a4e20c 100644 --- a/editors/openwysiwyg.inc +++ b/editors/openwysiwyg.inc @@ -21,6 +21,10 @@ function wysiwyg_openwysiwyg_editor() { 'files' => array('wysiwyg.js'), ), ), + 'toolbar support' => array( + 'multi rows' => TRUE, + 'separator' => TRUE, + ), 'version callback' => 'wysiwyg_openwysiwyg_version', 'themes callback' => 'wysiwyg_openwysiwyg_themes', 'settings callback' => 'wysiwyg_openwysiwyg_settings', @@ -107,25 +111,36 @@ function wysiwyg_openwysiwyg_settings($editor, $config, $theme) { } } - if (!empty($config['buttons'])) { + if (!empty($config['toolbar'])) { + $toolbar = array(); $plugins = wysiwyg_get_plugins($editor['name']); - foreach ($config['buttons'] as $plugin => $buttons) { - foreach ($buttons as $button => $enabled) { - foreach (array('buttons', 'extensions') as $type) { + foreach ($config['toolbar'] as $row) { + $toolbar_row = array(); + + foreach ($row as $buttons) { + foreach ($buttons as $button_config) { // Skip unavailable plugins. - if (!isset($plugins[$plugin][$type][$button])) { - continue; + $plugin = $button_config['plugin']; + $button = $button_config['button']; + + if ($button == WYSIWYG_SEPARATOR) { + $toolbar_row[] = 'seperator'; } - // Add buttons. - if ($type == 'buttons') { - $settings['Toolbar'][0][] = $button; + elseif (!isset($plugins[$plugin]['buttons'][$button])) { + continue; } + + $toolbar_row[] = $button; } } + + $toolbar[] = $toolbar_row; } + + $settings['Toolbar'] = $toolbar; } - // @todo + // @todo // if (isset($config['block_formats'])) { // $settings['DropDowns']['headings']['elements'] = explode(',', $config['block_formats']); // } diff --git a/editors/tinymce.inc b/editors/tinymce.inc index fad0c2d..7c21b93 100644 --- a/editors/tinymce.inc +++ b/editors/tinymce.inc @@ -27,6 +27,10 @@ function wysiwyg_tinymce_editor() { 'files' => array('tiny_mce_src.js'), ), ), + 'toolbar support' => array( + 'multi rows' => TRUE, + 'separator' => TRUE, + ), 'version callback' => 'wysiwyg_tinymce_version', 'themes callback' => 'wysiwyg_tinymce_themes', 'settings callback' => 'wysiwyg_tinymce_settings', @@ -191,75 +195,88 @@ function wysiwyg_tinymce_settings($editor, $config, $theme) { if ($config['css_setting'] == 'theme') { $settings['content_css'] = implode(',', wysiwyg_get_css()); } - else if ($config['css_setting'] == 'self' && isset($config['css_path'])) { + elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) { $settings['content_css'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => path_to_theme())); } } - // Find the enabled buttons and the button row they belong on. - // Also map the plugin metadata for each button. - // @todo What follows is a pain; needs a rewrite. - if (!empty($config['buttons']) && is_array($config['buttons'])) { - // $settings['buttons'] are stacked into - // $settings['theme_advanced_buttons1'] later. - // @todo Add a toolbar designer based on jQuery UI. - $settings['buttons'] = array(); - // Only array keys in $settings['extensions'] matter; added to - // $settings['plugins'] later. - $settings['extensions'] = array(); - // $settings['extended_valid_elements'] are just stacked, unique'd later, - // and transformed into a comma-separated string in - // wysiwyg_add_editor_settings(). - // @todo Needs a complete plugin API redesign using arrays for - // tag => attributes definitions and array_merge_recursive(). - $settings['extended_valid_elements'] = array(); + $plugins = wysiwyg_get_plugins($editor['name']); + $settings['extended_valid_elements'] = array(); + $toolbar = array(); + $extensions = array(); - $plugins = wysiwyg_get_plugins($editor['name']); - foreach ($config['buttons'] as $plugin => $buttons) { - foreach ($buttons as $button => $enabled) { - // Iterate separately over buttons and extensions properties. - foreach (array('buttons', 'extensions') as $type) { - // Skip unavailable plugins. - if (!isset($plugins[$plugin][$type][$button])) { - continue; - } - // Add buttons. - if ($type == 'buttons') { - $settings['buttons'][] = $button; - } - // Add external Drupal plugins to the list of extensions. - if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) { - $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $button)] = 1; - } - // Add external plugins to the list of extensions. - else if ($type == 'buttons' && empty($plugins[$plugin]['internal'])) { - $settings['extensions'][_wysiwyg_tinymce_plugin_name('add', $plugin)] = 1; - } - // Add internal buttons that also need to be loaded as extension. - else if ($type == 'buttons' && !empty($plugins[$plugin]['load'])) { - $settings['extensions'][$plugin] = 1; - } - // Add plain extensions. - else if ($type == 'extensions' && !empty($plugins[$plugin]['load'])) { - $settings['extensions'][$plugin] = 1; - } - // Allow plugins to add valid HTML elements. - if (!empty($plugins[$plugin]['extended_valid_elements'])) { - $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']); - } - // Allow plugins to add or override global configuration settings. - if (!empty($plugins[$plugin]['options'])) { - $settings = array_merge($settings, $plugins[$plugin]['options']); - } - } + foreach ((array)$config['extensions'] as $plugin => $buttons) { + foreach ($buttons as $button => $enabled) { + if (!isset($plugins[$plugin], $plugins[$plugin]['extensions'], $plugins[$plugin]['extensions'][$button])) { + continue; + } + + // Add plain extensions. + if (!empty($plugins[$plugin]['load'])) { + $extensions[$plugin] = 1; + } + + // Allow plugins to add valid HTML elements. + if (!empty($plugins[$plugin]['extended_valid_elements'])) { + $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']); + } + + // Allow plugins to add or override global configuration settings. + if (!empty($plugins[$plugin]['options'])) { + $settings = array_merge($settings, $plugins[$plugin]['options']); } } - // Clean-up. - $settings['extended_valid_elements'] = array_unique($settings['extended_valid_elements']); - if ($settings['extensions']) { - $settings['plugins'] = array_keys($settings['extensions']); + } + + foreach ((array)$config['toolbar'] as $row) { + $group = array(); + + foreach ($row as $buttons) { + foreach ($buttons as $button_config) { + $plugin = $button_config['plugin']; + $button = $button_config['button']; + + if ($button == WYSIWYG_SEPARATOR) { + $group[] = '|'; + continue; + } + if (!isset($plugins[$plugin]['buttons'][$button])) { + continue; + } + + $group[] = $button; + + // Add external Drupal plugins to the list of extensions. + if (!empty($plugins[$plugin]['proxy'])) { + $extensions[_wysiwyg_tinymce_plugin_name('add', $button)] = 1; + } + // Add external plugins to the list of extensions. + elseif (empty($plugins[$plugin]['internal'])) { + $extensions[_wysiwyg_tinymce_plugin_name('add', $plugin)] = 1; + } + // Add internal buttons that also need to be loaded as extension. + elseif (!empty($plugins[$plugin]['load'])) { + $extensions[$plugin] = 1; + } + + // Allow plugins to add or override global configuration settings. + if (!empty($plugins[$plugin]['options'])) { + $settings = array_merge($settings, $plugins[$plugin]['options']); + } + + // Allow plugins to add valid HTML elements. + if (!empty($plugins[$plugin]['extended_valid_elements'])) { + $settings['extended_valid_elements'] = array_merge($settings['extended_valid_elements'], $plugins[$plugin]['extended_valid_elements']); + } + } } - unset($settings['extensions']); + + $toolbar[] = $group; + } + + $settings['extended_valid_elements'] = array_unique($settings['extended_valid_elements']); + if ($extensions) { + $settings['plugins'] = array_keys($extensions); } // Add theme-specific settings. @@ -276,7 +293,7 @@ function wysiwyg_tinymce_settings($editor, $config, $theme) { if (isset($config['block_formats'])) { $settings['theme_advanced_blockformats'] = $config['block_formats']; } - if (isset($settings['buttons'])) { + if ($toolbar) { // These rows explicitly need to be set to be empty, otherwise TinyMCE // loads its default buttons of the advanced theme for each row. $settings += array( @@ -284,14 +301,17 @@ function wysiwyg_tinymce_settings($editor, $config, $theme) { 'theme_advanced_buttons2' => array(), 'theme_advanced_buttons3' => array(), ); - // @todo Allow to sort/arrange editor buttons. - for ($i = 0; $i < count($settings['buttons']); $i++) { - $settings['theme_advanced_buttons1'][] = $settings['buttons'][$i]; + + for ($i = 0;$i < count($toolbar);$i++) { + $group = $toolbar[$i]; + + foreach ($group as $button) { + $settings['theme_advanced_buttons' . ($i+1)][] = $button; + } } } break; } - unset($settings['buttons']); // Convert the config values into the form expected by TinyMCE. foreach ($settings as $key => $value) { diff --git a/images/add.png b/images/add.png new file mode 100644 index 0000000..6332fef Binary files /dev/null and b/images/add.png differ diff --git a/images/draggable.png b/images/draggable.png new file mode 100644 index 0000000..47e8a02 Binary files /dev/null and b/images/draggable.png differ diff --git a/images/remove.png b/images/remove.png new file mode 100644 index 0000000..1514d51 Binary files /dev/null and b/images/remove.png differ diff --git a/wysiwyg-toolbar-designer.tpl.php b/wysiwyg-toolbar-designer.tpl.php new file mode 100644 index 0000000..7ac92fb --- /dev/null +++ b/wysiwyg-toolbar-designer.tpl.php @@ -0,0 +1,37 @@ +
+

+
+ + + + + +
+ +

+ +
+ * +
+ +
+ +   + +
+ + +
 
+
+ +
+   +   +   +
+ +
+   +   +
+
diff --git a/wysiwyg.admin.inc b/wysiwyg.admin.inc index 26e3b6f..2d88055 100644 --- a/wysiwyg.admin.inc +++ b/wysiwyg.admin.inc @@ -7,9 +7,9 @@ */ /** - * Form builder for Wysiwyg profile form. + * Prepare default properties for profile. */ -function wysiwyg_profile_form($form_state, $profile) { +function wysiwyg_profile_default($profile) { // Merge in defaults. $profile = (array) $profile; $profile += array( @@ -46,10 +46,136 @@ function wysiwyg_profile_form($form_state, $profile) { ); $profile = (object) $profile; + return $profile; +} + +/** + * Profile designer callback. + */ +function wysiwyg_profile_toolbar($profile) { + $profile = wysiwyg_profile_default($profile); + $formats = filter_formats(); + $editor = wysiwyg_get_editor($profile->editor); + drupal_set_title(t('%editor profile for %format', array('%editor' => $editor['title'], '%format' => $formats[$profile->format]->name))); + + $toolbar_support = isset($editor['toolbar support']) ? $editor['toolbar support'] : FALSE; + + if ($toolbar_support === FALSE) { + drupal_set_message(t('This editor does not support custom toolbar feature.'), 'warning'); + + // Return a blank page. + return ''; + } + + if (!is_array($toolbar_support)) { + $toolbar_support = array(); + } + + // Default settings + $toolbar_support += array( + 'multi rows' => FALSE, + 'multi groups' => FALSE, + 'separator' => FALSE, + ); + + // Get all available buttons. + $plugins = wysiwyg_get_plugins($profile->editor); + $buttons = array(); + + if (isset($toolbar_support['separator']) && $toolbar_support['separator']) { + // Separator button. + $buttons[] = array( + 'plugin' => 'default', + 'button' => WYSIWYG_SEPARATOR, + 'title' => t('Separator'), + ); + } + + foreach ($plugins as $name => $meta) { + if (isset($meta['buttons']) && is_array($meta['buttons'])) { + foreach ($meta['buttons'] as $button => $title) { + $buttons[] = array( + 'plugin' => $name, + 'button' => $button, + 'title' => $title, + ); + } + } + } + + $toolbar = (array)$profile->settings['toolbar']; + + $toolbar_support = array_merge($toolbar_support, array( + 'callbackUrl' => url('admin/settings/wysiwyg/profile/' . $profile->format . '/toolbar/save'), + 'toolbar' => $toolbar, + )); + + // Add necessary javascripts and stylesheets + drupal_add_js(array('wysiwyg_toolbar' => $toolbar_support), 'setting'); + jquery_ui_add(array('ui.draggable', 'ui.droppable', 'ui.sortable')); + drupal_add_js(drupal_get_path('module', 'wysiwyg') . '/wysiwyg.admin_toolbar.js'); + drupal_add_css(drupal_get_path('module', 'wysiwyg') . '/wysiwyg.admin_toolbar.css'); + + $output = theme('wysiwyg_profile_toolbar', $buttons, $toolbar, $toolbar_support); + + return $output; +} + +function wysiwyg_profile_toolbar_save($profile) { + $profile = wysiwyg_profile_default($profile); + $plugins = wysiwyg_get_plugins($profile->editor); + $format = $profile->format; + + $raw_toolbar = explode("\n", trim($_POST['toolbar'])); + $toolbar = array(); + + foreach ($raw_toolbar as $raw_row) { + $row = array(); + $raw_groups = explode('|', rtrim($raw_row, '|')); + + foreach ($raw_groups as $raw_group) { + $buttons = explode(',', $raw_group); + + $group = array(); + foreach ($buttons as $button) { + list($plugin, $name) = explode('.', $button, 2); + + if ($name != WYSIWYG_SEPARATOR && !isset($plugins[$plugin], $plugins[$plugin]['buttons'][$name])) { + continue; + } + + $group[] = array( + 'button' => $name, + 'plugin' => $plugin, + ); + } + + $row[] = $group; + } + + + $toolbar[] = $row; + } + + $values = $profile->settings; + $values['toolbar'] = count($toolbar) ? $toolbar : NULL; + db_query("UPDATE {wysiwyg} SET settings = '%s' WHERE format = %d", serialize($values), $format); + + echo drupal_json(array('status' => 'ok', 'toolbar' => $toolbar)); +} + +/** + * Form builder for Wysiwyg profile form. + */ +function wysiwyg_profile_form($form_state, $profile) { + $profile = wysiwyg_profile_default($profile); + $formats = filter_formats(); $editor = wysiwyg_get_editor($profile->editor); drupal_set_title(t('%editor profile for %format', array('%editor' => $editor['title'], '%format' => $formats[$profile->format]->name))); + $form_state['storage']['profile'] = $profile; + $form['format'] = array('#type' => 'value', '#value' => $profile->format); $form['input_format'] = array('#type' => 'value', '#value' => $formats[$profile->format]->name); $form['editor'] = array('#type' => 'value', '#value' => $profile->editor); @@ -98,9 +224,9 @@ function wysiwyg_profile_form($form_state, $profile) { '#description' => t('The language to use for the editor interface. Language codes are based on the ISO-639-2 format.'), ); - $form['buttons'] = array( + $form['extensions'] = array( '#type' => 'fieldset', - '#title' => t('Buttons and plugins'), + '#title' => t('Extensions'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#tree' => TRUE, @@ -108,36 +234,14 @@ function wysiwyg_profile_form($form_state, $profile) { ); $plugins = wysiwyg_get_plugins($profile->editor); - // Generate the button list. + // Generate the extension list foreach ($plugins as $name => $meta) { - if (isset($meta['buttons']) && is_array($meta['buttons'])) { - foreach ($meta['buttons'] as $button => $title) { - $icon = ''; - if (!empty($meta['path'])) { - // @todo Button icon locations are different in editors, editor versions, - // and contrib/custom plugins (like Image Assist, f.e.). - $img_src = $meta['path'] . "/images/$name.gif"; - // Handle plugins that have more than one button. - if (!file_exists($img_src)) { - $img_src = $meta['path'] . "/images/$button.gif"; - } - $icon = file_exists($img_src) ? '' : ''; - } - $title = (isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title); - $title = (!empty($icon) ? $icon . ' ' . $title : $title); - $form['buttons'][$name][$button] = array( - '#type' => 'checkbox', - '#title' => $title, - '#default_value' => !empty($profile->settings['buttons'][$name][$button]) ? $profile->settings['buttons'][$name][$button] : FALSE, - ); - } - } - else if (isset($meta['extensions']) && is_array($meta['extensions'])) { + if (isset($meta['extensions']) && is_array($meta['extensions'])) { foreach ($meta['extensions'] as $extension => $title) { - $form['buttons'][$name][$extension] = array( + $form['extensions'][$name][$extension] = array( '#type' => 'checkbox', '#title' => isset($meta['url']) ? l($title, $meta['url'], array('target' => '_blank')) : $title, - '#default_value' => !empty($profile->settings['buttons'][$name][$extension]) ? $profile->settings['buttons'][$name][$extension] : FALSE, + '#default_value' => !empty($profile->settings['extensions'][$name][$extension]) ? $profile->settings['extensions'][$name][$extension] : FALSE, ); } } @@ -296,24 +400,25 @@ function wysiwyg_profile_form($form_state, $profile) { * @see wysiwyg_profile_form() */ function wysiwyg_profile_form_submit($form, &$form_state) { + $profile = $form_state['storage']['profile']; $values = $form_state['values']; - if (isset($values['buttons'])) { + if (isset($values['extensions'])) { // Store only enabled buttons for each plugin. - foreach ($values['buttons'] as $plugin => $buttons) { - $values['buttons'][$plugin] = array_filter($values['buttons'][$plugin]); + foreach ($values['extensions'] as $plugin => $buttons) { + $values['extensions'][$plugin] = array_filter($values['extensions'][$plugin]); } // Store only enabled plugins. - $values['buttons'] = array_filter($values['buttons']); + $values['extensions'] = array_filter($values['extensions']); } + + $values['extensions'] = (array)$values['extensions']; + // Remove input format name. $format = $values['format']; $input_format = $values['input_format']; $editor = $values['editor']; - unset($values['format'], $values['input_format'], $values['editor']); - // Remove FAPI values. - // @see system_settings_form_submit() - unset($values['submit'], $values['form_id'], $values['op'], $values['form_token'], $values['form_build_id']); + $values = array_merge($profile->settings, wysiwyg_profile_clean($values)); // Insert new profile data. db_query("UPDATE {wysiwyg} SET settings = '%s' WHERE format = %d", serialize($values), $format); @@ -351,7 +456,12 @@ function theme_wysiwyg_admin_button_table($form) { $rows[] = $row; } - $output = theme('table', array(), $rows, array('width' => '100%')); + if (count($rows)) { + $output = theme('table', array(), $rows, array('width' => '100%')); + } + else { + $output = ''; + } return $output; } @@ -529,3 +639,15 @@ function wysiwyg_profile_delete($format) { db_query("DELETE FROM {wysiwyg} WHERE format = %d", $format); } +/** + * Clean submitted setting values + */ +function wysiwyg_profile_clean($values) { + unset($values['format'], $values['input_format'], $values['editor']); + + // Remove FAPI values. + // @see system_settings_form_submit() + unset($values['submit'], $values['form_id'], $values['op'], $values['form_token'], $values['form_build_id']); + + return $values; +} diff --git a/wysiwyg.admin_toolbar.css b/wysiwyg.admin_toolbar.css new file mode 100644 index 0000000..61b6177 --- /dev/null +++ b/wysiwyg.admin_toolbar.css @@ -0,0 +1,118 @@ +/* $Id$ */ + +#toolbar-rows { + margin: 1em 0; +} + +#toolbar-rows .toolbar-row { + border: 1px solid gray; + padding: 0.2em 0.2em 0.2em 20px; + min-height: 2em; + position: relative; + margin-bottom: 0.5em; +} + +#toolbar-rows .single-group .toolbar-group { + border: 0; + margin: 0; + padding: 0; + width: 100%; +} + +#toolbar-rows .single-group .group-handler { + display: none; +} + +#toolbar-rows .toolbar-group { + border: 1px dotted silver; + display: inline-block; + min-width: 10em; + min-height: 27px; + padding: 0 1.5em 0 0.5em; + margin: 0 0.5em 0.1em 0; + position: relative; +} + +#toolbar-actions { + +} + +.toolbar-row-template, .toolbar-group-template { + display: none; +} + +#toolbar-rows .row-handler { + position: absolute; + top: 0.2em; + left: 0.2em; +} + +#toolbar-available-buttons { + border: 1px dashed silver; + padding: 0.5em; + margin-bottom: 1em; +} + +#wysiwyg-toolbar-designer .add-group { + display: inline-block; + background: url(images/add.png); + width: 16px; + height: 16px; + right: 1.6em; + top: 0.2em; + text-decoration: none; + position: absolute; +} + +#wysiwyg-toolbar-designer .add-toolbar-row { + display: inline-block; + background: url(images/add.png); + width: 16px; + height: 16px; + text-decoration: none; + outline: none; +} + +#wysiwyg-toolbar-designer .remove-row { + display: block; + background: url(images/remove.png); + width: 16px; + height: 16px; + position: absolute; + right: 0.2em; + top: 0.2em; + text-decoration: none; +} + +#wysiwyg-toolbar-designer .remove-group { + display: block; + background: url(images/remove.png); + width: 16px; + height: 16px; + position: absolute; + right: 0.2em; + top: 0.2em; + text-decoration: none; +} + +#wysiwyg-toolbar-designer .handler { + background: url(images/draggable.png) no-repeat 0 4px; + width: 16px; + height: 16px; + display: inline-block; + text-decoration: none; +} + +#wysiwyg-toolbar-designer .wysiwyg-button { + border: 1px solid silver; + display: inline-block; + margin: 0.2em 0.2em 0.2em 0; + padding: 0 0.5em; + height: 20px; + cursor: pointer; +} + +#wysiwyg-toolbar-designer .ahah-progress, #wysiwyg-toolbar-designer div.warning { + /* Hidden by default. */ + display: none; +} diff --git a/wysiwyg.admin_toolbar.js b/wysiwyg.admin_toolbar.js new file mode 100644 index 0000000..9f9ae19 --- /dev/null +++ b/wysiwyg.admin_toolbar.js @@ -0,0 +1,256 @@ +// $Id$ + +Drupal.behaviors.wysiwygToolbarDesigner = function(context) { + var settings = Drupal.settings.wysiwyg_toolbar; + var workspace = $('#wysiwyg-toolbar-designer'); + var designArea = $('#toolbar-rows'); + var changeNotification = $('#wysiwyg-toolbar-designer div.warning'); + var availableButtons = $('#toolbar-available-buttons'); + var separator = $('.wysiwyg-button-default-Separator',availableButtons); + + var createRow = function(noGroup) { + var row = $('.toolbar-row-template',workspace).clone().removeClass('toolbar-row-template'); + row.addClass('toolbar-row').sortable({ + handle: '.group-handler', + revert: true, + items: '.toolbar-group', + connectWith: '#toolbar-rows .toolbar-row', + stop: function(event,ui) { + changeNotification.fadeIn(); + } + }); + + row.find('.remove-row').click(function() { + if ($(this).parent().find('.wysiwyg-button').length == 0 || confirm(Drupal.t("Do you want to remove this row ?"))) { + // Enable buttons removed from this row. + $('.wysiwyg-button',$(this).parent()).each(function(i,button){ + var button_class = /wysiwyg-button-[^-]+-[^\s]+/.exec($(button).attr('class')); + $('.' + button_class[0],availableButtons).show(); + }); + separator.show(); + + $(this).parent().remove(); + changeNotification.fadeIn(); + } + }); + + if (settings['multi groups']) { + row.find('.add-group').click(function() { + var group = createGroup(); + row.append(group); + row.sortable('refresh'); + }); + } + else { + row.find('.add-group').hide(); + row.addClass('single-group'); + } + + if (!settings['multi rows']) { + row.find('.remove-row').hide(); + } + + // add required group + if (!noGroup) { + var group = createGroup(); + row.append(group); + } + + return row; + }; + + var createGroup = function() { + var group = $('.toolbar-group-template').clone().removeClass('toolbar-group-template'); + group.addClass('toolbar-group'); + + group.sortable({ + revert: true, + items: '.wysiwyg-button', + connectWith: '#toolbar-rows .toolbar-group', + stop: function(event,ui) { + changeNotification.fadeIn(); + } + }); + + group.droppable({ + accept: '.template-button', + drop: function(event,ui) { + var button = ui.draggable.clone(); + button.removeClass('template-button').addClass('toolbar-button'); + + // Disable this button in template area. + ui.draggable.hide(); + separator.show(); + + $(this).append(button).sortable('refresh'); + changeNotification.fadeIn(); + } + }); + + if (settings['multi groups']) { + group.find('.remove-group').click(function() { + if ($(this).parent().find('.wysiwyg-button').length == 0 || confirm(Drupal.t("Do you want to remove this group ?"))) { + // Enable buttons removed from this row. + $('.wysiwyg-button',$(this).parent()).each(function(i,button){ + var button_class = /wysiwyg-button-[^-]+-[^\s]+/.exec($(button).attr('class')); + $('.' + button_class[0],availableButtons).show(); + }); + separator.show(); + + $(this).parent().remove(); + changeNotification.fadeIn(); + } + }); + } + else { + group.find('.remove-group').hide(); + } + + return group; + } + + var reset = function() { + $('.toolbar-row',designArea).remove(); + + // Enable all buttons and then disable it later. + $('.wysiwyg-button',availableButtons).show(); + + + for (var i in settings.toolbar) { + var groups = settings.toolbar[i]; + var row = createRow(true); + + for (var j in groups) { + var group = createGroup(); + var buttons = groups[j]; + + for (var k in buttons) { + var buttonClass = '.wysiwyg-button-' + buttons[k].plugin + '-' + buttons[k].button; + var template_button = $(buttonClass,$('#toolbar-available-buttons')); + + if (template_button.length) { + + button = template_button.clone().show(); + button.removeClass('template-button').addClass('toolbar-button'); + + group.append(button); + + // Disable button in template area. + template_button.hide(); + } + } + + row.append(group); + } + + designArea.append(row); + } + + // Make sure we always have at least one row. + if (!settings['multi rows']) { + if ($('.toolbar-row',designArea).length <= 0) { + var row = createRow(); + designArea.append(row); + } + } + + separator.show(); + changeNotification.fadeOut(); + } + + $('.add-toolbar-row',workspace).click(function(){ + // clone from toolbar template + var row = createRow(row); + + // Append row to design area. + designArea.append(row).sortable('refresh'); + changeNotification.fadeIn(); + }); + + $('.wysiwyg-button',availableButtons).addClass('template-button').draggable({ + handle: '.handler', + helper: 'clone', + revert: 'invalid', + addClasses: false + }); + + availableButtons.droppable({ + accept: '.toolbar-button', + drop: function(event, ui) { + ui.draggable.remove(); + changeNotification.fadeIn(); + + // Enable button in template. + var button_id = /wysiwyg-button-([^-]+-[^\s]+)/.exec($(ui.draggable).attr('class')); + $('.wysiwyg-button-' + button_id[1]).show(); + } + }); + + $('#toolbar-rows').sortable({ + items: '.toolbar-row', + handle: '.row-handler', + stop: function(event, ui) { + changeNotification.fadeIn(); + } + }); + + /* Design actions buttons. */ + $('#reset-design').click(function() { + if (!changeNotification.is(':hidden') && confirm(Drupal.t('Do you want to reset the changes ?'))) + reset(); + }); + + + $('#save-design').click(function() { + // Prepare toolbar data to submit. + var toolbar = ""; + + designArea.find('.toolbar-row').each(function(key,rowDom){ + var row = ""; + + $('.toolbar-group',rowDom).each(function(key,groupDom){ + var group = ""; + + $('.wysiwyg-button',groupDom).each(function(key,button){ + var cls = /wysiwyg-button-([^-]+)-([^\s]+)/.exec($(button).attr('class')); + group += cls[1] + "." + cls[2] + ","; + }) + + row += group + "|"; + }); + + toolbar += row + "\n"; + }); + + var button = $(this); + var progress = $('.ahah-progress',workspace); + button.attr('disabled','disabled'); + progress.show(); + + $.ajax({ + type: 'POST', + dataType: 'json', + url: settings.callbackUrl, + data: {toolbar: toolbar}, + success: function(data){ + button.removeAttr('disabled'); + changeNotification.fadeOut(); + settings.toolbar = data.toolbar; + }, + complete: function(request,status) { + if (status == 'error' || status == 'parsererror') { + // TODO implement better error handler later. + alert("Error !!!"); + } + + progress.hide(); + } + }); + }); + + reset(); + + if (!settings['multi rows']) { + $('.add-toolbar-row').hide(); + } +}; diff --git a/wysiwyg.info b/wysiwyg.info index 245a370..9cdb3d8 100644 --- a/wysiwyg.info +++ b/wysiwyg.info @@ -5,6 +5,9 @@ package = User interface core = 6.x ; Information added by drupal.org packaging script on 2010-04-03 +dependencies[] = jquery_ui +dependencies[] = jquery_update + version = "6.x-2.x-dev" core = "6.x" project = "wysiwyg" diff --git a/wysiwyg.module b/wysiwyg.module index 660936b..59216a3 100644 --- a/wysiwyg.module +++ b/wysiwyg.module @@ -6,6 +6,8 @@ * Integrate client-side editors with Drupal. */ +define('WYSIWYG_SEPARATOR', 'Separator'); + /** * Implementation of hook_menu(). */ @@ -32,6 +34,24 @@ function wysiwyg_menu() { 'tab_parent' => 'admin/settings/wysiwyg/profile/%wysiwyg_profile', 'type' => MENU_LOCAL_TASK, ); + $items['admin/settings/wysiwyg/profile/%wysiwyg_profile/toolbar'] = array( + 'title' => 'Toolbar', + 'page callback' => 'wysiwyg_profile_toolbar', + 'page arguments' => array(4), + 'access arguments' => array('administer filters'), + 'file' => 'wysiwyg.admin.inc', + 'tab_root' => 'admin/settings/wysiwyg/profile', + 'tab_parent' => 'admin/settings/wysiwyg/profile/%wysiwyg_profile', + 'type' => MENU_LOCAL_TASK, + ); + $items['admin/settings/wysiwyg/profile/%wysiwyg_profile/toolbar/save'] = array( + 'title' => 'Toolbar', + 'page callback' => 'wysiwyg_profile_toolbar_save', + 'page arguments' => array(4), + 'access arguments' => array('administer filters'), + 'file' => 'wysiwyg.admin.inc', + 'type' => MENU_TASK, + ); $items['admin/settings/wysiwyg/profile/%wysiwyg_profile/delete'] = array( 'title' => 'Remove', 'page callback' => 'drupal_get_form', @@ -64,6 +84,10 @@ function wysiwyg_theme() { 'wysiwyg_profile_overview' => array( 'arguments' => array('form' => NULL), ), + 'wysiwyg_profile_toolbar' => array( + 'arguments' => array('buttons' => NULL, 'toolbar' => NULL, 'toolbar_support' => NULL), + 'template' => 'wysiwyg-toolbar-designer', + ), 'wysiwyg_admin_button_table' => array( 'arguments' => array('form' => NULL), ), @@ -414,16 +438,22 @@ function wysiwyg_add_plugin_settings($profile) { if (isset($editor['plugin settings callback'])) { // @todo Require PHP 5.1 in 3.x and use array_intersect_key(). $profile_plugins_native = array(); - foreach ($plugins[$editor['name']] as $plugin => $meta) { - // Skip Drupal plugins (handled below). - if ($plugin === $proxy) { - continue; - } - // Only keep native plugins that are enabled in this profile. - if (isset($profile->settings['buttons'][$plugin])) { - $profile_plugins_native[$plugin] = $meta; + + foreach ((array)$profile->settings['toolbar'] as $row) { + foreach ($row as $group) { + foreach ($group as $button) { + $plugin = $button['plugin']; + + // Skip Drupal plugins (handled below). + if ($plugin === $proxy) { + continue; + } + + $profile_plugins_native[$plugin] = $plugins[$plugin]; + } } } + // Invoke the editor's plugin settings callback, so it can populate the // settings for native external plugins with required values. $settings_native = call_user_func($editor['plugin settings callback'], $editor, $profile, $profile_plugins_native); @@ -433,26 +463,39 @@ function wysiwyg_add_plugin_settings($profile) { // Process Drupal plugins. if ($proxy && isset($editor['proxy plugin settings callback'])) { + $drupal_plugins = wysiwyg_get_all_plugins(); + $profile_plugins_drupal = array(); - foreach (wysiwyg_get_all_plugins() as $plugin => $meta) { - if (isset($profile->settings['buttons'][$proxy][$plugin])) { - // JavaScript and plugin-specific settings for Drupal plugins must be - // loaded and processed only once. Plugin information is cached - // statically to pass it to the editor's proxy plugin settings callback. - if (!isset($processed_plugins[$proxy][$plugin])) { - $profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin] = $meta; - // Load the Drupal plugin's JavaScript. - drupal_add_js($meta['js path'] . '/' . $meta['js file']); - // Add plugin-specific settings. - if (isset($meta['settings'])) { - drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($plugin => $meta['settings'])))), 'setting'); + + foreach ((array)$profile->settings['toolbar'] as $row) { + foreach ($row as $group) { + foreach ($group as $button) { + $plugin = $button['plugin']; + $button_id = $button['button']; + + if ($plugin == $proxy && isset($drupal_plugins[$button_id])) { + $meta = $drupal_plugins[$button_id]; + + // JavaScript and plugin-specific settings for Drupal plugins must be + // loaded and processed only once. Plugin information is cached + // statically to pass it to the editor's proxy plugin settings callback. + if (!isset($processed_plugins[$proxy][$button_id])) { + $profile_plugins_drupal[$button_id] = $processed_plugins[$proxy][$button_id] = $meta; + // Load the Drupal plugin's JavaScript. + drupal_add_js($meta['js path'] . '/' . $meta['js file']); + // Add plugin-specific settings. + if (isset($meta['settings'])) { + drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($button_id => $meta['settings'])))), 'setting'); + } + } + else { + $profile_plugins_drupal[$button_id] = $processed_plugins[$proxy][$button_id]; + } } } - else { - $profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin]; - } } } + // Invoke the editor's proxy plugin settings callback, so it can populate // the settings for Drupal plugins with custom, required values. $settings_drupal = call_user_func($editor['proxy plugin settings callback'], $editor, $profile, $profile_plugins_drupal); @@ -545,6 +588,7 @@ function wysiwyg_get_editor_config($profile, $theme) { $context = array('editor' => $editor, 'profile' => $profile, 'theme' => $theme); drupal_alter('wysiwyg_editor_settings', $settings, $context); } + return $settings; }