You are here

editor_ckeditor.module in Editor 7

Adds CKEditor as a supported editor.

File

modules/editor_ckeditor/editor_ckeditor.module
View source
<?php

/**
 * @file
 * Adds CKEditor as a supported editor.
 */
define('CKEDITOR_VERSION', '4.5.10');

/**
 * Implements hook_init().
 */
function editor_ckeditor_init() {
  $path = drupal_get_path('module', 'editor_ckeditor');

  // Add the CSS for this module. These aren't in editor_ckeditor.info, because
  // they need to be in the CSS_SYSTEM group rather than the CSS_DEFAULT group.
  drupal_add_css($path . '/css/plugins/drupalimagecaption/editor_ckeditor.drupalimagecaption.css', array(
    'group' => CSS_SYSTEM,
    'every_page' => TRUE,
  ));
}

/**
 * Implements hook_hook_info().
 */
function editor_ckeditor_hook_info() {
  $hooks = array(
    'editor_ckeditor_plugins',
    'editor_ckeditor_plugins_alter',
    'editor_ckeditor_settings',
    'editor_ckeditor_css_alter',
  );
  return array_fill_keys($hooks, array(
    'group' => 'editor_ckeditor',
  ));
}

/**
 * Implements hook_menu().
 */
function editor_ckeditor_menu() {
  $items['editor-ckeditor/dialog/link/%filter_format'] = array(
    'title' => 'Edit image',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'editor_ckeditor_link_dialog_form',
      3,
    ),
    'access callback' => 'editor_format_access',
    'access arguments' => array(
      3,
    ),
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file' => 'includes/editor_ckeditor.pages.inc',
  );
  $items['editor-ckeditor/dialog/image/%filter_format'] = array(
    'title' => 'Edit image',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'editor_ckeditor_image_dialog_form',
      3,
    ),
    'access callback' => 'editor_format_access',
    'access arguments' => array(
      3,
    ),
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
    'file' => 'includes/editor_ckeditor.pages.inc',
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function editor_ckeditor_theme() {
  return array(
    'editor_ckeditor_settings_toolbar' => array(
      'variables' => array(
        'format' => NULL,
        'plugins' => NULL,
      ),
      'file' => 'includes/editor_ckeditor.theme.inc',
    ),
  );
}

/**
 * Implements hook_library().
 */
function editor_ckeditor_library() {
  $path = drupal_get_path('module', 'editor_ckeditor');
  $libraries['drupal.editor_ckeditor.plugins.language'] = array(
    'title' => 'Language plugin.',
    'version' => VERSION,
    'css' => array(
      $path . '/css/plugins/language/editor_ckeditor.language.css' => array(),
    ),
  );
  $libraries['drupal.editor_ckeditor.language.admin'] = array(
    'title' => 'Administrative library for configuring the language plugin.',
    'version' => VERSION,
    'js' => array(
      $path . '/js/editor_ckeditor.language.admin.js' => array(),
    ),
    'dependencies' => array(
      array(
        'system',
        'drupal.vertical-tabs',
      ),
    ),
  );
  $libraries['drupal.editor_ckeditor.admin'] = array(
    'title' => 'Administrative library for configuring CKEditor.',
    'version' => VERSION,
    'js' => array(
      $path . '/js/editor_ckeditor.admin.js' => array(),
    ),
    'css' => array(
      $path . '/css/editor_ckeditor.admin.css' => array(),
    ),
    'dependencies' => array(
      array(
        'system',
        'ui.sortable',
      ),
      array(
        'system',
        'ui.draggable',
      ),
    ),
  );
  $libraries['drupal.ckeditor'] = array(
    'title' => 'Drupal behavior to enable CKEditor on textareas.',
    'version' => VERSION,
    'js' => array(
      $path . '/js/ckeditor.js' => array(
        // Core libraries cannot depend on contrib libraries, so we give the
        // CKEditor initialization JS a low weight to ensure it comes after the
        // CKEditor library itself.
        'weight' => 2,
      ),
    ),
    'css' => array(
      $path . '/css/ckeditor.css' => array(),
    ),
    'dependencies' => array(
      array(
        'filter',
        'filter',
      ),
      array(
        'system',
        'drupal.ajax',
      ),
      array(
        'editor_ckeditor',
        'ckeditor',
      ),
      array(
        'editor',
        'drupal.editor',
      ),
      array(
        'editor',
        'drupal.editor.dialog',
      ),
    ),
  );
  $libraries['ckeditor'] = array(
    'title' => 'Loads the main CKEditor library.',
    'version' => CKEDITOR_VERSION,
    'js' => array(
      // CKEditor is both a large library and needs to be able to identify its
      // installation path, so we disable aggregation on it.
      $path . '/lib/ckeditor/ckeditor.js' => array(
        'preprocess' => FALSE,
        'group' => JS_LIBRARY,
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_library_alter().
 */
function editor_ckeditor_library_alter(&$libraries, $module) {
  global $theme;

  // Pass Drupal's JS cache-busting string via settings along to CKEditor.
  // @see http://docs.ckeditor.com/#!/api/CKEDITOR-property-timestamp
  if ($module === 'editor_ckeditor' && isset($libraries['drupal.ckeditor'])) {
    $query_string = variable_get('css_js_query_string') ?: '0';
    $libraries['drupal.ckeditor']['js'][] = array(
      'type' => 'setting',
      'data' => array(
        'ckeditor' => array(
          'timestamp' => $query_string,
        ),
      ),
    );
  }

  // A number of core themes have styling issues with the toolbar configurator.
  // Add an appropriate 'override' stylesheet to the default administration
  // library if one of these themes is enabled.
  if ($module == 'editor_ckeditor' && isset($libraries['drupal.editor_ckeditor.admin'])) {
    foreach (array(
      'garland',
      'seven',
      'bartik',
    ) as $core_theme) {

      // Only add the override CSS if the theme is available to use.
      if ($theme == $core_theme) {
        $libraries['drupal.editor_ckeditor.admin']['css'][drupal_get_path('module', 'editor_ckeditor') . "/css/editor_ckeditor.{$core_theme}.admin.css"] = array();
      }
    }
  }
}

/**
 * Retrieves the full list of installed CKEditor plugins.
 *
 * @return array
 *   An associative array of CKEditor plugins keyed by the internal name of the
 *   plugin. Each plugin may contain the following elements (all are optional
 *   except as noted):
 *   - location: Required for all external plugins. String path to the plugin
 *     directory relative to the Drupal installation root.
 *   - file: Required for all external plugins. String file name of the plugin
 *     in the "location" directory.
 *   - internal: Boolean value indicating if the plugin is part of the
 *     compressed CKEditor library package and already loaded on all instances.
 *   - css: An array of CSS files that should be added by CKEditor. These files
 *     are used only when CKEditor is using an iframe wrapper around its
 *     content.
 *   - enabled callback: String containing a function name that can determine if
 *     this plugin should be enabled based on the current editor configuration.
 *   - buttons: An array of buttons that are provided by this plugin. Each
 *     button is keyed by its CKEditor button name, and contains an array of
 *     button properties, including:
 *     - label: A human-readable, translated button name.
 *     - image: An image for the button to be used in the toolbar.
 *     - image_rtl: If the image needs to have a right-to-left version,
 *       specifies an alternative file that will be used in RTL editors.
 *     - image_alternative: If the button does not render as an image, specifies
 *       an HTML string representing the contents of the button.
 *     - attributes: An array of HTML attributes which are added to the button
 *       when it is rendered in the administrative section for assembling the
 *       toolbar.
 *     - multiple: Boolean value indicating if the button may be added multiple
 *       times to the toolbar.
 *     - required_html: If the button requires certain HTML tags to be allowed,
 *       specifies an array of tags.
 *
 * @see hook_editor_ckeditor_plugins()
 * @see hook_editor_ckeditor_plugins_alter()
 * @see hook_editor_ckeditor_PLUGIN_plugin_check()
 */
function editor_ckeditor_plugins() {
  $plugins = module_invoke_all('editor_ckeditor_plugins');
  drupal_alter('editor_ckeditor_plugins', $plugins);
  return $plugins;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Manipulate the image insert form to describe CKEditor-integration.
 */
function editor_ckeditor_form_filter_format_editor_image_form_alter(&$form, $form_state) {
  $format = $form_state['format'];
  if ($format->editor === 'ckeditor') {
    $form['caption']['#description'] = t('If checked, a caption area will appear in the editor.');
  }
}

/**
 * Enabled callback for hook_editor_ckeditor_plugins().
 *
 * Checks if the Caption plugin should be enabled based on the configuration of
 * a text format and editor.
 *
 * @param object $format
 *   The filter format object for which to check the settings of.
 *
 * @return bool
 *   TRUE if the image plugin is enabled, FALSE otherwise.
 */
function editor_ckeditor_image_plugin_check($format) {

  // Automatically enable caption support if the DrupalImage button is enabled.
  foreach ($format->editor_settings['toolbar'] as $row) {
    foreach ($row as $button_group) {
      if (in_array('DrupalImage', $button_group['items'])) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

/**
 * Editor JS settings callback; Add CKEditor settings to the page for a format.
 *
 * @param object $format
 *   The filter format object for which CKEditor is adding its settings.
 * @param $existing_settings
 *   Settings that have already been added to the page by filters.
 *
 * @return array
 *   An associative array of CKEditor settings. For a complete list of settings
 *   see http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-skin.
 */
function editor_ckeditor_get_settings($format, $existing_settings) {
  global $language;
  editor_format_ensure_additional_properties($format);

  // Loop through all available plugins and check to see if it has been
  // explicitly enabled. At the same time, associate each plugin with its
  // buttons (if any) so we can check if the plugin should be enabled implicitly
  // based on the toolbar.
  $plugin_info = editor_ckeditor_plugins();
  $external_plugins = array();
  $external_css = array();
  $all_buttons = array();
  foreach ($plugin_info as $plugin_name => $plugin) {

    // Check if this plugin should be enabled.
    if (isset($plugin['enabled callback'])) {
      if ($plugin['enabled callback'] === TRUE || $plugin['enabled callback']($format, $plugin_name) && !empty($plugin['path'])) {
        $external_plugins[$plugin_name]['file'] = $plugin['file'];
        $external_plugins[$plugin_name]['path'] = $plugin['path'];
        if (isset($plugin['css'])) {
          $external_css = array_merge($external_css, $plugin['css']);
        }
      }
    }

    // Associate each plugin with its button.
    if (isset($plugin['buttons'])) {
      if (empty($plugin['internal'])) {
        foreach ($plugin['buttons'] as $button_name => &$button) {
          $button['plugin'] = $plugin;
          $button['plugin']['name'] = $plugin_name;
          unset($button['plugin']['buttons']);
        }
      }
      $all_buttons = array_merge($all_buttons, $plugin['buttons']);
    }
  }

  // Change the toolbar separators into groups and record needed plugins based
  // on use in the toolbar.
  $toolbar = array();
  foreach ($format->editor_settings['toolbar'] as $row) {
    foreach ($row as $button_group) {
      $checked_group = array();

      // Check that the group configuration is valid.
      $checked_group['name'] = empty($button_group['name']) ? '' : $button_group['name'];
      if (empty($button_group['items'])) {
        continue;
      }
      foreach ($button_group['items'] as $button_name) {

        // Sanity check that the button exists in our installation.
        if (isset($all_buttons[$button_name])) {
          $checked_group['items'][] = $button_name;

          // Keep track of the needed plugin for this button, if any.
          if (isset($all_buttons[$button_name]['plugin']['path'])) {
            $plugin_name = $all_buttons[$button_name]['plugin']['name'];
            $external_plugin = $all_buttons[$button_name]['plugin'];
            $external_plugins[$plugin_name]['file'] = $external_plugin['file'];
            $external_plugins[$plugin_name]['path'] = $external_plugin['path'];
            if (isset($external_plugin['css'])) {
              $external_css = array_merge($external_css, $external_plugin['css']);
            }
          }
        }
      }
      $toolbar[] = $checked_group;
    }
    $toolbar[] = '/';
  }

  // Remove the trailing slash (end row) from the toolbar.
  if ($toolbar) {
    array_pop($toolbar);
  }

  // Add the style list if configured.
  $style_list = array();
  if (!empty($format->editor_settings['plugins']['style']['style_list'])) {
    $style_list = $format->editor_settings['plugins']['style']['style_list'];
  }
  $css = array(
    drupal_get_path('module', 'editor_ckeditor') . '/css/ckeditor-iframe.css',
    drupal_get_path('module', 'editor') . '/css/components/align.module.css',
  );

  // Collect a list of CSS files to be added to the editor instance.
  $css = array_merge($css, $external_css, _editor_ckeditor_theme_css());
  drupal_alter('editor_ckeditor_css', $css, $format);

  // Convert all paths to be relative to root.
  foreach ($css as $key => $css_path) {
    $css[$key] = base_path() . $css_path;
  }

  // Initialize reasonable defaults that provide expected basic behavior.
  $settings = array(
    'toolbar' => $toolbar,
    'extraPlugins' => implode(',', array_keys($external_plugins)),
    'removePlugins' => 'image',
    'removeButtons' => '',
    // Empty custom config must be a string.
    // See http://docs.ckeditor.com/#!/guide/dev_configuration.
    'customConfig' => '',
    // Empty styles must be an array.
    // See http://docs.ckeditor.com/#!/guide/dev_styles.
    'stylesSet' => $style_list,
    'contentsCss' => array_values($css),
    'pasteFromWordPromptCleanup' => TRUE,
    'entities' => FALSE,
    'disableNativeSpellChecker' => FALSE,
    'indentClasses' => array(
      'indent1',
      'indent2',
      'indent3',
    ),
    'justifyClasses' => array(
      'text-align-left',
      'text-align-center',
      'text-align-right',
      'text-align-justify',
    ),
    'coreStyles_underline' => array(
      'element' => 'span',
      'attributes' => array(
        'class' => 'underline',
      ),
    ),
    //'format_tags' => implode(';', $format->editor_settings['format_list']),
    'language' => isset($language->language) ? $language->language : '',
    'resize_dir' => 'vertical',
  );

  // Add the language list if configured.
  $language_list = array();
  $config = array(
    'language_list' => 'un',
  );
  if (isset($format->editor_settings['plugins']['language'])) {
    $config = $format->editor_settings['plugins']['language'];
  }
  $predefined_languages = $config['language_list'] === 'all' ? editor_ckeditor_standard_language_list() : editor_ckeditor_united_nations_language_list();

  // Generate the language_list setting as expected by the CKEditor Language
  // plugin, but key the values by the full language name so that we can sort
  // them later on.
  foreach ($predefined_languages as $langcode => $lang) {
    $english_name = $lang[0];
    $direction = empty($lang[2]) ? NULL : $lang[2];
    if ($direction === 'rtl') {
      $language_list[$english_name] = $langcode . ':' . $english_name . ':rtl';
    }
    else {
      $language_list[$english_name] = $langcode . ':' . $english_name;
    }
  }

  // Sort on full language name.
  ksort($language_list);
  $settings['language_list'] = array_values($language_list);

  // Add the allowedContent setting, which ensures CKEditor only allows tags
  // and attributes that are allowed by the text format for this text editor.
  list($settings['allowedContent'], $settings['disallowedContent']) = editor_ckeditor_get_acf_settings($format);

  // These settings are used specifically by Drupal.
  $settings['drupal'] = array(
    'externalPlugins' => $external_plugins,
    'format' => $format->format,
  );
  if (isset($external_plugins['drupallink'])) {
    $settings['drupal']['linkDialogTitleAdd'] = t('Add Link');
    $settings['drupal']['linkDialogTitleEdit'] = t('Edit Link');
    $settings['drupal']['linkDialogUrl'] = url('editor-ckeditor/dialog/link');
  }
  if (isset($external_plugins['drupalimage'])) {
    $settings['drupal']['imageDialogTitleAdd'] = t('Insert Image');
    $settings['drupal']['imageDialogTitleEdit'] = t('Edit Image');
    $settings['drupal']['imageDialogUrl'] = url('editor-ckeditor/dialog/image');
  }
  if (isset($external_plugins['drupalimagecaption'])) {
    $settings['drupal']['captionFilterEnabled'] = !empty($format->filters['editor_caption']->status);
    $settings['drupal']['alignFilterEnabled'] = !empty($format->filters['editor_align']->status);
    $settings['drupal']['imageCaptionPlaceholderText'] = t('Enter caption text here.');
    $settings['image2_captionedClass'] = 'caption caption-img';
    $settings['image2_alignClasses'] = array(
      'align-left',
      'align-center',
      'align-right',
    );
  }
  drupal_alter('editor_ckeditor_settings', $settings, $format);
  return $settings;
}

/**
 * Builds the ACF part of the CKEditor JS settings.
 *
 * This ensures that CKEditor obeys the HTML restrictions defined by Drupal's
 * filter system, by enabling CKEditor's Advanced Content Filter (ACF)
 * functionality: http://ckeditor.com/blog/CKEditor-4.1-RC-Released.
 *
 * @param object $format
 *   The text format object.
 *
 * @return array
 *   An array with two values:
 *   - the first value is the "allowedContent" setting: a well-formatted array
 *     or TRUE. The latter indicates that anything is allowed.
 *   - the second value is the "disallowedContent" setting: a well-formatted
 *     array or FALSE. The latter indicates that nothing is disallowed.
 */
function editor_ckeditor_get_acf_settings($format) {
  $html_restrictions = editor_format_allowed_html($format);

  // When all HTML is allowed, also set allowedContent to true and
  // disallowedContent to false.
  if ($html_restrictions === TRUE) {
    return array(
      TRUE,
      FALSE,
    );
  }

  // Converts Drupal-stored attribute values to CKEditor attribute lists.
  $get_attribute_values = function ($attribute_values, $allowed_values) {
    $values = array_keys(array_filter($attribute_values, function ($value) use ($allowed_values) {
      if ($allowed_values) {
        return $value !== FALSE;
      }
      else {
        return $value === FALSE;
      }
    }));
    if (count($values)) {
      return implode(',', $values);
    }
    else {
      return NULL;
    }
  };
  $allowed = array();
  $disallowed = array();
  if (isset($html_restrictions['forbidden'])) {
    foreach ($html_restrictions['forbidden'] as $tag) {
      $disallowed[$tag] = TRUE;
    }
  }
  foreach ($html_restrictions['allowed'] as $tag => $attributes) {

    // Tell CKEditor the tag is allowed, but no attributes.
    if ($attributes === FALSE) {
      $allowed[$tag] = array(
        'attributes' => FALSE,
        'styles' => FALSE,
        'classes' => FALSE,
      );
    }
    elseif ($attributes === TRUE) {
      $allowed[$tag] = array(
        'attributes' => TRUE,
        'styles' => TRUE,
        'classes' => TRUE,
      );

      // We've just marked that any value for the "style" and "class"
      // attributes is allowed. However, that may not be the case: the "*"
      // tag may still apply restrictions.
      // Since CKEditor's ACF follows the following principle:
      // "Once validated, an element or its property cannot be invalidated by
      // another rule."
      // That means that the most permissive setting wins. Which means that
      // it will still be allowed by CKEditor to e.g. define any style, no
      // matter what the "*" tag's restrictions may be. If there's a setting
      // for either the "style" or "class" attribute, it cannot possibly be
      // more permissive than what was set above. Hence: inherit from the
      // "*" tag where possible.
      if (isset($html_restrictions['allowed']['*'])) {
        $wildcard = $html_restrictions['allowed']['*'];
        if (isset($wildcard['style'])) {
          if (!is_array($wildcard['style'])) {
            $allowed[$tag]['styles'] = $wildcard['style'];
          }
          else {
            $allowed_styles = $get_attribute_values($wildcard['style'], TRUE);
            if (isset($allowed_styles)) {
              $allowed[$tag]['styles'] = $allowed_styles;
            }
            else {
              unset($allowed[$tag]['styles']);
            }
          }
        }
        if (isset($wildcard['class'])) {
          if (!is_array($wildcard['class'])) {
            $allowed[$tag]['classes'] = $wildcard['class'];
          }
          else {
            $allowed_classes = $get_attribute_values($wildcard['class'], TRUE);
            if (isset($allowed_classes)) {
              $allowed[$tag]['classes'] = $allowed_classes;
            }
            else {
              unset($allowed[$tag]['classes']);
            }
          }
        }
      }
    }
    elseif (is_array($attributes)) {

      // Configure allowed attributes, allowed "style" attribute values and
      // allowed "class" attribute values.
      // CKEditor only allows specific values for the "class" and "style"
      // attributes; so ignore restrictions on other attributes, which
      // Drupal filters may provide.
      // NOTE: A Drupal contrib module can subclass this class, override the
      // getConfig() method, and override the JavaScript at
      // Drupal.editors.ckeditor to somehow make validation of values for
      // attributes other than "class" and "style" work.
      $allowed_attributes = array_filter($attributes, function ($value) {
        return $value !== FALSE;
      });
      if (count($allowed_attributes)) {
        $allowed[$tag]['attributes'] = implode(',', array_keys($allowed_attributes));
      }
      if (isset($allowed_attributes['style']) && is_array($allowed_attributes['style'])) {
        $allowed_styles = $get_attribute_values($allowed_attributes['style'], TRUE);
        if (isset($allowed_styles)) {
          $allowed[$tag]['styles'] = $allowed_styles;
        }
      }
      if (isset($allowed_attributes['class']) && is_array($allowed_attributes['class'])) {
        $allowed_classes = $get_attribute_values($allowed_attributes['class'], TRUE);
        if (isset($allowed_classes)) {
          $allowed[$tag]['classes'] = $allowed_classes;
        }
      }

      // Handle disallowed attributes analogously. However, to handle *dis-
      // allowed* attribute values, we must look at *allowed* attributes'
      // disallowed attribute values! After all, a disallowed attribute
      // implies that all of its possible attribute values are disallowed,
      // thus we must look at the disallowed attribute values on allowed
      // attributes.
      $disallowed_attributes = array_filter($attributes, function ($value) {
        return $value === FALSE;
      });
      if (count($disallowed_attributes)) {

        // No need to blacklist the 'class' or 'style' attributes; CKEditor
        // handles them separately (if no specific class or style attribute
        // values are allowed, then those attributes are disallowed).
        if (isset($disallowed_attributes['class'])) {
          unset($disallowed_attributes['class']);
        }
        if (isset($disallowed_attributes['style'])) {
          unset($disallowed_attributes['style']);
        }
        $disallowed[$tag]['attributes'] = implode(',', array_keys($disallowed_attributes));
      }
      if (isset($allowed_attributes['style']) && is_array($allowed_attributes['style'])) {
        $disallowed_styles = $get_attribute_values($allowed_attributes['style'], FALSE);
        if (isset($disallowed_styles)) {
          $disallowed[$tag]['styles'] = $disallowed_styles;
        }
      }
      if (isset($allowed_attributes['class']) && is_array($allowed_attributes['class'])) {
        $disallowed_classes = $get_attribute_values($allowed_attributes['class'], FALSE);
        if (isset($disallowed_classes)) {
          $disallowed[$tag]['classes'] = $disallowed_classes;
        }
      }
    }
  }
  return array(
    $allowed,
    $disallowed,
  );
}

/**
 * Retrieves the default theme's CKEditor stylesheets defined in the .info file.
 *
 * Themes may specify iframe-specific CSS files for use with CKEditor by
 * including a "editor_ckeditor_stylesheets" key in the theme .info file.
 *
 * @code
 * editor_ckeditor_stylesheets[] = css/ckeditor-iframe.css
 * @endcode
 *
 * @param string $theme
 *   The theme name from which the "editor_ckeditor_stylesheets" property should
 *   be read in the .info files. This theme and all its parent themes will be
 *   checked. Defaults to the current front-end theme.
 *
 * @return array
 *   An array of all CSS to be added by the theme within the CKEditor.
 */
function _editor_ckeditor_theme_css($theme = NULL) {
  $css = array();
  if (!isset($theme)) {
    $theme = variable_get('theme_default', 'bartik');
  }
  if ($theme_path = drupal_get_path('theme', $theme)) {
    $info = system_get_info('theme', $theme);
    if (isset($info['editor_ckeditor_stylesheets'])) {
      $css = $info['editor_ckeditor_stylesheets'];
      foreach ($css as $key => $path) {
        $css[$key] = $theme_path . '/' . $path;
      }
    }
    if (isset($info['base theme'])) {
      $css = array_merge($css, _editor_ckeditor_theme_css($info['base theme']));
    }
  }
  return $css;
}

/**
 * Some common languages with their English and native names.
 *
 * Language codes are defined by the W3C language tags document for
 * interoperability. Language codes typically have a language and, optionally,
 * a script or regional variant name. See:
 * http://www.w3.org/International/articles/language-tags/ for more
 * information.
 *
 * @return array
 *   An array of language code to language name information. Language name
 *   information itself is an array of English and native names.
 */
function editor_ckeditor_standard_language_list() {

  // This list is based on languages available from localize.drupal.org. See
  // http://localize.drupal.org/issues for information on how to add languages
  // there.
  //
  // The "Left-to-right marker" comments and the enclosed UTF-8 markers are to
  // make otherwise strange looking PHP syntax natural (to not be displayed in
  // right to left). See https://www.drupal.org/node/128866#comment-528929.
  return array(
    'af' => array(
      'Afrikaans',
      'Afrikaans',
    ),
    'am' => array(
      'Amharic',
      'አማርኛ',
    ),
    'ar' => array(
      'Arabic',
      /* Left-to-right marker "‭" */
      'العربية',
      'rtl',
    ),
    'ast' => array(
      'Asturian',
      'Asturianu',
    ),
    'az' => array(
      'Azerbaijani',
      'Azərbaycanca',
    ),
    'be' => array(
      'Belarusian',
      'Беларуская',
    ),
    'bg' => array(
      'Bulgarian',
      'Български',
    ),
    'bn' => array(
      'Bengali',
      'বাংলা',
    ),
    'bo' => array(
      'Tibetan',
      'བོད་སྐད་',
    ),
    'bs' => array(
      'Bosnian',
      'Bosanski',
    ),
    'ca' => array(
      'Catalan',
      'Català',
    ),
    'cs' => array(
      'Czech',
      'Čeština',
    ),
    'cy' => array(
      'Welsh',
      'Cymraeg',
    ),
    'da' => array(
      'Danish',
      'Dansk',
    ),
    'de' => array(
      'German',
      'Deutsch',
    ),
    'dz' => array(
      'Dzongkha',
      'རྫོང་ཁ',
    ),
    'el' => array(
      'Greek',
      'Ελληνικά',
    ),
    'en' => array(
      'English',
      'English',
    ),
    'en-x-simple' => array(
      'Simple English',
      'Simple English',
    ),
    'eo' => array(
      'Esperanto',
      'Esperanto',
    ),
    'es' => array(
      'Spanish',
      'Español',
    ),
    'et' => array(
      'Estonian',
      'Eesti',
    ),
    'eu' => array(
      'Basque',
      'Euskera',
    ),
    'fa' => array(
      'Persian, Farsi',
      /* Left-to-right marker "‭" */
      'فارسی',
      'rtl',
    ),
    'fi' => array(
      'Finnish',
      'Suomi',
    ),
    'fil' => array(
      'Filipino',
      'Filipino',
    ),
    'fo' => array(
      'Faeroese',
      'Føroyskt',
    ),
    'fr' => array(
      'French',
      'Français',
    ),
    'fy' => array(
      'Frisian, Western',
      'Frysk',
    ),
    'ga' => array(
      'Irish',
      'Gaeilge',
    ),
    'gd' => array(
      'Scots Gaelic',
      'Gàidhlig',
    ),
    'gl' => array(
      'Galician',
      'Galego',
    ),
    'gsw-berne' => array(
      'Swiss German',
      'Schwyzerdütsch',
    ),
    'gu' => array(
      'Gujarati',
      'ગુજરાતી',
    ),
    'he' => array(
      'Hebrew',
      /* Left-to-right marker "‭" */
      'עברית',
      'rtl',
    ),
    'hi' => array(
      'Hindi',
      'हिन्दी',
    ),
    'hr' => array(
      'Croatian',
      'Hrvatski',
    ),
    'ht' => array(
      'Haitian Creole',
      'Kreyòl ayisyen',
    ),
    'hu' => array(
      'Hungarian',
      'Magyar',
    ),
    'hy' => array(
      'Armenian',
      'Հայերեն',
    ),
    'id' => array(
      'Indonesian',
      'Bahasa Indonesia',
    ),
    'is' => array(
      'Icelandic',
      'Íslenska',
    ),
    'it' => array(
      'Italian',
      'Italiano',
    ),
    'ja' => array(
      'Japanese',
      '日本語',
    ),
    'jv' => array(
      'Javanese',
      'Basa Java',
    ),
    'ka' => array(
      'Georgian',
      'ქართული ენა',
    ),
    'kk' => array(
      'Kazakh',
      'Қазақ',
    ),
    'km' => array(
      'Khmer',
      'ភាសាខ្មែរ',
    ),
    'kn' => array(
      'Kannada',
      'ಕನ್ನಡ',
    ),
    'ko' => array(
      'Korean',
      '한국어',
    ),
    'ku' => array(
      'Kurdish',
      'Kurdî',
    ),
    'ky' => array(
      'Kyrgyz',
      'Кыргызча',
    ),
    'lo' => array(
      'Lao',
      'ພາສາລາວ',
    ),
    'lt' => array(
      'Lithuanian',
      'Lietuvių',
    ),
    'lv' => array(
      'Latvian',
      'Latviešu',
    ),
    'mg' => array(
      'Malagasy',
      'Malagasy',
    ),
    'mk' => array(
      'Macedonian',
      'Македонски',
    ),
    'ml' => array(
      'Malayalam',
      'മലയാളം',
    ),
    'mn' => array(
      'Mongolian',
      'монгол',
    ),
    'mr' => array(
      'Marathi',
      'मराठी',
    ),
    'ms' => array(
      'Bahasa Malaysia',
      'بهاس ملايو',
    ),
    'my' => array(
      'Burmese',
      'ဗမာစကား',
    ),
    'ne' => array(
      'Nepali',
      'नेपाली',
    ),
    'nl' => array(
      'Dutch',
      'Nederlands',
    ),
    'nb' => array(
      'Norwegian Bokmål',
      'Norsk, bokmål',
    ),
    'nn' => array(
      'Norwegian Nynorsk',
      'Norsk, nynorsk',
    ),
    'oc' => array(
      'Occitan',
      'Occitan',
    ),
    'pa' => array(
      'Punjabi',
      'ਪੰਜਾਬੀ',
    ),
    'pl' => array(
      'Polish',
      'Polski',
    ),
    'pt-pt' => array(
      'Portuguese, Portugal',
      'Português, Portugal',
    ),
    'pt-br' => array(
      'Portuguese, Brazil',
      'Português, Brasil',
    ),
    'ro' => array(
      'Romanian',
      'Română',
    ),
    'ru' => array(
      'Russian',
      'Русский',
    ),
    'sco' => array(
      'Scots',
      'Scots',
    ),
    'se' => array(
      'Northern Sami',
      'Sámi',
    ),
    'si' => array(
      'Sinhala',
      'සිංහල',
    ),
    'sk' => array(
      'Slovak',
      'Slovenčina',
    ),
    'sl' => array(
      'Slovenian',
      'Slovenščina',
    ),
    'sq' => array(
      'Albanian',
      'Shqip',
    ),
    'sr' => array(
      'Serbian',
      'Српски',
    ),
    'sv' => array(
      'Swedish',
      'Svenska',
    ),
    'sw' => array(
      'Swahili',
      'Kiswahili',
    ),
    'ta' => array(
      'Tamil',
      'தமிழ்',
    ),
    'ta-lk' => array(
      'Tamil, Sri Lanka',
      'தமிழ், இலங்கை',
    ),
    'te' => array(
      'Telugu',
      'తెలుగు',
    ),
    'th' => array(
      'Thai',
      'ภาษาไทย',
    ),
    'tr' => array(
      'Turkish',
      'Türkçe',
    ),
    'tyv' => array(
      'Tuvan',
      'Тыва дыл',
    ),
    'ug' => array(
      'Uyghur',
      'Уйғур',
    ),
    'uk' => array(
      'Ukrainian',
      'Українська',
    ),
    'ur' => array(
      'Urdu',
      /* Left-to-right marker "‭" */
      'اردو',
      'rtl',
    ),
    'vi' => array(
      'Vietnamese',
      'Tiếng Việt',
    ),
    'xx-lolspeak' => array(
      'Lolspeak',
      'Lolspeak',
    ),
    'zh-hans' => array(
      'Chinese, Simplified',
      '简体中文',
    ),
    'zh-hant' => array(
      'Chinese, Traditional',
      '繁體中文',
    ),
  );
}

/**
 * The 6 official languages used at the United Nations.
 *
 * This list is based on http://www.un.org/en/aboutun/languages.shtml/ and it
 * uses the same format as getStandardLanguageList().
 *
 * @return array
 *   An array with language codes as keys, and English and native language
 *   names as values.
 */
function editor_ckeditor_united_nations_language_list() {
  return array(
    'ar' => array(
      'Arabic',
      /* Left-to-right marker "‭" */
      'العربية',
      'rtl',
    ),
    'zh-hans' => array(
      'Chinese, Simplified',
      '简体中文',
    ),
    'en' => array(
      'English',
      'English',
    ),
    'fr' => array(
      'French',
      'Français',
    ),
    'ru' => array(
      'Russian',
      'Русский',
    ),
    'es' => array(
      'Spanish',
      'Español',
    ),
  );
}

Functions

Namesort descending Description
editor_ckeditor_form_filter_format_editor_image_form_alter Implements hook_form_FORM_ID_alter().
editor_ckeditor_get_acf_settings Builds the ACF part of the CKEditor JS settings.
editor_ckeditor_get_settings Editor JS settings callback; Add CKEditor settings to the page for a format.
editor_ckeditor_hook_info Implements hook_hook_info().
editor_ckeditor_image_plugin_check Enabled callback for hook_editor_ckeditor_plugins().
editor_ckeditor_init Implements hook_init().
editor_ckeditor_library Implements hook_library().
editor_ckeditor_library_alter Implements hook_library_alter().
editor_ckeditor_menu Implements hook_menu().
editor_ckeditor_plugins Retrieves the full list of installed CKEditor plugins.
editor_ckeditor_standard_language_list Some common languages with their English and native names.
editor_ckeditor_theme Implements hook_theme().
editor_ckeditor_united_nations_language_list The 6 official languages used at the United Nations.
_editor_ckeditor_theme_css Retrieves the default theme's CKEditor stylesheets defined in the .info file.

Constants

Namesort descending Description
CKEDITOR_VERSION @file Adds CKEditor as a supported editor.