'CKEditor', 'vendor url' => 'http://ckeditor.com', 'download url' => 'http://ckeditor.com/download', 'libraries' => array( '' => array( 'title' => 'Default', 'files' => array( 'ckeditor.js' => array('preprocess' => FALSE), ), ), 'src' => array( 'title' => 'Source', 'files' => array( 'ckeditor_source.js' => array('preprocess' => FALSE), ), ), ), 'install note callback' => 'wysiwyg_ckeditor_install_note', 'verified version range' => array('3.0', '4.6.1.580bcaf'), 'migrate settings callback' => 'wysiwyg_ckeditor_migrate_settings', 'version callback' => 'wysiwyg_ckeditor_version', 'themes callback' => 'wysiwyg_ckeditor_themes', 'settings form callback' => 'wysiwyg_ckeditor_settings_form', 'init callback' => 'wysiwyg_ckeditor_init', 'settings callback' => 'wysiwyg_ckeditor_settings', 'plugin callback' => '_wysiwyg_ckeditor_plugins', 'plugin meta callback' => '_wysiwyg_ckeditor_plugin_meta', 'proxy plugin' => array( 'drupal' => array( 'load' => TRUE, 'proxy' => TRUE, ), ), 'proxy plugin settings callback' => '_wysiwyg_ckeditor_proxy_plugin_settings', 'versions' => array( '3.0.0.3665' => array( 'js files' => array('ckeditor-3.0.js'), ), ), ); return $editor; } /** * Profile migration callback for CKEditor. * * Applies known changes to the editor settings as needed when the installed * editor version is different from the one used to configure the profile. * This fixes problems caused by settings, plugins or buttons being renamed, * removed, or added between versions. * * Only changes needed up/down to and including the installed version from the * profile version may be applied, in case the user did not install the latest * supported version. * * @param $settings * The editor settings array as it was stored in the database. * @param $editor * The editor definition from wysiwyg_get_editor(). * @param $profile_version * The editor version string from when the profile was last saved. * @param $installed_version * The editor version currently installed on the system. * * @return * An editor version string telling Wysiwyg past which version the profile * could be migrated. If no changes were needed return TRUE. * Returning FALSE indicates migration failed and the profile is likely * unusable. Wysiwyg will recommend the user starts over with a new profile. */ function wysiwyg_ckeditor_migrate_settings(&$settings, $editor, $profile_version, $installed_version) { $version_diff = version_compare($installed_version, $profile_version); // Default to no changes needed. $migrated_version = TRUE; if ($version_diff === 1) { // Upgrading, starting at the profile version going up. // 3.x to 4.0. if (version_compare($profile_version, '4.0', '<') && version_compare($installed_version, '4.0', '>=')) { // The default skin changed from "kama" to "moono". if (isset($settings['skin']) && $settings['skin'] === 'kama') { $settings['skin'] = 'moono'; } $migrated_version = '4.0'; } // Version 4.6.0. if (version_compare($profile_version, '4.6.0', '<') && version_compare($installed_version, '4.6.0', '>=')) { // The default skin changed from "moono" to "moono-lisa". if (isset($settings['skin']) && $settings['skin'] === 'moono') { $settings['skin'] = 'moono-lisa'; } $migrated_version = '4.6.0'; } } elseif ($version_diff === 0) { // Same version. This function would never have been called. } // $version_diff === -1, an older version was installed. else { // Downgrading, starting at the profile version going down. // 4.6.0 down to 4.x. if (version_compare($profile_version, '4.6', '>=') && version_compare($installed_version, '4.6', '<')) { if (isset($settings['skin']) && $settings['skin'] === 'moono-lisa') { $settings['skin'] = 'moono'; } // Going down directly to 4.0 since no changes need to run anyway. $migrated_version = '4.6'; } // 4.x to 3.x. if (version_compare($profile_version, '4.0', '>=') && version_compare($installed_version, '4.0', '<')) { // Change the default skin back "moono" to "kama". if (isset($settings['skin']) && $settings['skin'] === 'moono') { $settings['skin'] = 'kama'; } $migrated_version = '4.0'; } } // Return the version which was possible to migrate to, or FALSE on fail. Must // be within the verified range, but not necessarily match the exact version // which is currently installed. return $migrated_version; } /** * Return an install note. */ function wysiwyg_ckeditor_install_note() { $output = '
' . t('Do NOT download the "CKEditor for Drupal" edition.') . ''; $output .= t('Make sure you install the full package as not all plugins work with the standard package.') . '
'; return $output; } /** * Detect editor version. * * @param $editor * An array containing editor properties as returned from hook_editor(). * * @return * The installed editor version. */ function wysiwyg_ckeditor_version($editor) { $library = $editor['library path'] . '/ckeditor.js'; if (!file_exists($library)) { return; } $library = fopen($library, 'r'); $max_lines = 8; while ($max_lines && $line = fgets($library, 500)) { // version:'CKEditor 3.0 SVN',revision:'3665' // version:'3.0 RC',revision:'3753' // version:'3.0.1',revision:'4391' // version:"4.0",revision:"769d96134b" if (preg_match('@version:[\'"](?:CKEditor )?([\d\.]+)(?:.+revision:[\'"]([[:xdigit:]]+))?@', $line, $version)) { fclose($library); // Version numbers need to have three parts since 3.0.1. $version[1] = preg_replace('/^(\d+)\.(\d+)$/', '${1}.${2}.0', $version[1]); return $version[1] . '.' . $version[2]; } $max_lines--; } fclose($library); } /** * Determine available editor themes or check/reset a given one. * * @param $editor * A processed hook_editor() array of editor properties. * @param $profile * A wysiwyg editor profile. * * @return * An array of theme names. The first returned name should be the default * theme name. */ function wysiwyg_ckeditor_themes($editor, $profile) { // @todo Skins are not themes but this will do for now. $path = $editor['library path'] . '/skins/'; if (file_exists($path) && ($dir_handle = opendir($path))) { $themes = array(); while ($file = readdir($dir_handle)) { if (is_dir($path . $file) && substr($file, 0, 1) != '.' && $file != 'CVS') { $themes[] = $file; } } closedir($dir_handle); natcasesort($themes); $themes = array_values($themes); return !empty($themes) ? $themes : array('default'); } else { return array('default'); } } /** * Enhances the editor profile settings form for CKEditor. * * @see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html */ function wysiwyg_ckeditor_settings_form(&$form, &$form_state) { $profile = $form_state['wysiwyg_profile']; $settings = $profile->settings; $installed_version = $form_state['wysiwyg']['editor']['installed version']; $ckeditor_defaults = array( 'block_formats' => 'p,address,pre,h2,h3,h4,h5,h6,div', // Custom setting. 'default_toolbar_grouping' => FALSE, 'forcePasteAsPlainText' => FALSE, 'resize_enabled' => TRUE, 'simple_source_formatting' => FALSE, 'toolbarLocation' => 'top', 'allowedContent' => TRUE, ); if (version_compare($installed_version, '3.1.0', '>=')) { // Enabled by default. $ckeditor_defaults['pasteFromWordRemoveFontStyles'] = TRUE; $ckeditor_defaults['pasteFromWordRemoveStyles'] = TRUE; } if (version_compare($installed_version, '4.6.0', '>=')) { // Disabled by default, deprecated. $ckeditor_defaults['pasteFromWordRemoveFontStyles'] = FALSE; // Dropped, no effect. unset($ckeditor_defaults['pasteFromWordNumberedHeadingToList'], $ckeditor_defaults['pasteFromWordRemoveStyles']); } if (version_compare($installed_version, '3.2.1', '>=')) { $ckeditor_defaults['stylesSet'] = ''; } $settings += $ckeditor_defaults; $form['appearance']['toolbarLocation'] = array( '#type' => 'select', '#title' => t('Toolbar location'), '#default_value' => $settings['toolbarLocation'], '#options' => array('bottom' => t('Bottom'), 'top' => t('Top')), '#description' => t('This option controls whether the editor toolbar is displayed above or below the editing area.') . ' ' . t('Uses the @setting setting internally.', array('@setting' => 'toolbarLocation', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-toolbarLocation'))), ); $form['appearance']['resize_enabled'] = array( '#type' => 'checkbox', '#title' => t('Enable resizing button'), '#default_value' => $settings['resize_enabled'], '#return_value' => 1, '#description' => t('This option gives you the ability to enable/disable the editor resizing feature.') . ' ' . t('Uses the @setting setting internally.', array('@setting' => 'resize_enabled', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-resize_enabled'))), ); $form['output']['simple_source_formatting'] = array( '#type' => 'checkbox', '#title' => t('Apply simple source formatting'), '#default_value' => $settings['simple_source_formatting'], '#return_value' => 1, '#description' => t('If enabled, the editor will re-format the HTML source code using a simple set of predefined rules. Disabling this option could avoid conflicts with other input filters.') . ' ' . t('Uses the @setting setting internally.', array('@setting' => 'dataProcessor.write.setRules()', '@url' => url('http://docs.cksource.com/ckeditor_api/symbols/src/plugins_htmlwriter_plugin.js.html'))), ); $form['paste'] = array( '#type' => 'fieldset', '#title' => t('Paste plugin'), '#description' => t('Settings for the @plugin plugin.', array('@plugin' => 'paste', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-forcePasteAsPlainText'))), '#collapsible' => TRUE, '#collapsed' => TRUE, '#group' => 'advanced', ); $form['paste']['forcePasteAsPlainText'] = array( '#type' => 'checkbox', '#title' => t('Force paste as plain text'), '#default_value' => !empty($settings['forcePasteAsPlainText']), '#return_value' => 1, '#description' => t('If enabled, all pasting operations insert plain text into the editor, losing any formatting information possibly available in the source text. Note: Paste from Word is not affected by this setting.') . ' ' . t('Uses the @setting setting internally.', array('@setting' => 'forcePasteAsPlainText', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-forcePasteAsPlainText'))), ); if (version_compare($installed_version, '3.1.0', '>=')) { $form['paste']['pasteFromWord'] = array( '#type' => 'fieldset', '#title' => t('Paste from Word'), ); $form['paste']['pasteFromWord']['pasteFromWordNumberedHeadingToList'] = array( '#type' => 'checkbox', '#title' => t('Numbered heading to list'), '#default_value' => !empty($settings['pasteFromWordNumberedHeadingToList']), '#return_value' => 1, '#description' => t('If enabled, transforms MS Word outline numbered headings into lists.'), ); $form['paste']['pasteFromWord']['pasteFromWordPromptCleanup'] = array( '#type' => 'checkbox', '#title' => t('Prompt on cleanup'), '#default_value' => !empty($settings['pasteFromWordPromptCleanup']), '#return_value' => 1, '#description' => t('If enabled, prompts the user about the clean up of content being pasted from MS Word.'), ); $form['paste']['pasteFromWord']['pasteFromWordRemoveFontStyles'] = array( '#type' => 'checkbox', '#title' => t('Remove font styles'), '#default_value' => !empty($settings['pasteFromWordRemoveFontStyles']), '#return_value' => 1, '#description' => t('If enabled, removes all font related formatting styles, including font size, font family, font foreground/background color.'), ); $form['paste']['pasteFromWord']['pasteFromWordRemoveStyles'] = array( '#type' => 'checkbox', '#title' => t('Remove styles'), '#default_value' => !empty($settings['pasteFromWordRemoveStyles']), '#return_value' => 1, '#description' => t('If enabled, removes element styles that can not be managed with the editor, other than font specific styles.'), ); } if (version_compare($installed_version, '4.1.0', '>=')) { $form['output']['acf'] = array( '#type' => 'fieldset', '#title' => t('Advanced Content Filter'), '#description' => t('ACF limits and adapts input data (HTML code added in source mode or by the editor.setData method, pasted HTML code, etc.) so it matches the editor configuration in the best possible way. It may also deactivate features which generate HTML code that is not allowed by the configuration. See @url for details.', array('@url' => url('http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter'))), ); $form['output']['acf']['acf_mode'] = array( '#type' => 'select', '#title' => t('Mode'), '#options' => array( WYSIWYG_CKEDITOR_ACF_AUTOMATIC => t('Automatic'), WYSIWYG_CKEDITOR_ACF_CUSTOM => t('Custom'), WYSIWYG_CKEDITOR_ACF_DISABLED => t('Disabled'), ), '#default_value' => isset($profile->settings['acf_mode']) ? $profile->settings['acf_mode'] : WYSIWYG_CKEDITOR_ACF_DISABLED, '#description' => t('If set to Automatic or Custom, the editor will strip out any content not explicitely allowed when the editor loads.'), ); $form['output']['acf']['acf_allowed_content'] = array( '#type' => 'textarea', '#title' => t('Content Rules'), '#default_value' => isset($profile->settings['acf_allowed_content']) ? $profile->settings['acf_allowed_content'] : '', '#description' => t('Rules for whitelisting content for the advanced content filter. Both string and object formats accepted. Uses the allowedContent setting in Custom mode or the extraAllowedContent settings in Automatic mode internally. See @info_url for details.', array('@info_url' => url('http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules'), '@allowed_url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent'), '@allowed_extra_url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-extraAllowedContent'))), '#states' => array( 'visible' => array( ':input[name="acf_mode"]' => array( array('value' => WYSIWYG_CKEDITOR_ACF_AUTOMATIC), array('value' => WYSIWYG_CKEDITOR_ACF_CUSTOM), ), ), ), '#element_validate' => array('wysiwyg_ckeditor_settings_form_validate_allowed_content'), ); } if (version_compare($installed_version, '3.6.0', '>=')) { $form['appearance']['default_toolbar_grouping'] = array( '#type' => 'checkbox', '#title' => t('Use default toolbar button grouping'), '#default_value' => !empty($settings['default_toolbar_grouping']), '#return_value' => 1, '#description' => t('This option gives you the ability to enable/disable the usage of default groupings for toolbar buttons. If enabled, toolbar buttons will be placed into predetermined groups instead of all in a single group.'), ); } if (version_compare($installed_version, '3.2.1', '>=')) { // Versions below 3.2.1 do not support Font styles at all. $form['css']['stylesSet'] = array( '#type' => 'textarea', '#title' => t('CSS classes'), '#description' => t('Optionally define CSS classes for the "Font style" dropdown list.[label]=[element].[class]
',
'!example' => 'Title=h1.title
',
)) . ' ' . t('Uses the @setting setting internally.', array('@setting' => 'stylesSet', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-stylesSet'))),
'#default_value' => $settings['stylesSet'],
'#element_validate' => array('wysiwyg_ckeditor_settings_form_validate_stylessets'),
);
}
if (version_compare($installed_version, '4.6.0', '>=')) {
$form['paste']['pasteFromWord']['pasteFromWordRemoveFontStyles']['#description'] .= '@format-list
.', array('@format-list' => 'p,h1,h2,h3,h4,h5,h6,div,blockquote,address,pre,code,dt,dd and other block elements')) . ' ' . t('Uses the @setting setting internally.', array('@setting' => 'block_formats', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-format_tags'))),
);
}
/**
* #element_validate handler for ACF Allowed Content element altered by wysiwyg_ckeditor_settings_form().
*/
function wysiwyg_ckeditor_settings_form_validate_allowed_content($element, &$form_state) {
if (_wysiwyg_ckeditor_settings_acf_is_obj($element['#value']) && json_decode($element['#value']) === NULL) {
form_error($element, t('Allowed content is not valid JSON.'));
}
}
/**
* #element_validate handler for CSS classes element altered by wysiwyg_ckeditor_settings_form().
*/
function wysiwyg_ckeditor_settings_form_validate_stylessets($element, &$form_state) {
if (wysiwyg_ckeditor_settings_parse_styles($element['#value']) === FALSE) {
form_error($element, t('The specified CSS classes are syntactically incorrect.'));
}
}
/**
* Returns an initialization JavaScript for this editor library.
*
* @param array $editor
* The editor library definition.
* @param string $library
* The library variant key from $editor['libraries'].
* @param object $profile
* The (first) wysiwyg editor profile.
*
* @return string
* A string containing inline JavaScript to execute before the editor library
* script is loaded.
*/
function wysiwyg_ckeditor_init($editor) {
// CKEditor unconditionally searches for its library filename in SCRIPT tags
// on the page upon loading the library in order to determine the base path to
// itself. When JavaScript aggregation is enabled, this search fails and all
// relative constructed paths within CKEditor are broken. The library has a
// CKEditor.basePath property, but it is not publicly documented and thus not
// reliable. The official documentation suggests to solve the issue through
// the global window variable.
// @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Specifying_the_Editor_Path
$library_path = base_path() . $editor['library path'] . '/';
return <<