You are here

editor.module in Editor 7

Same filename and directory in other branches
  1. 5 editor.module
  2. 6 editor.module

Allows rich text fields to be edited using WYSIWYG client-side editors.

File

editor.module
View source
<?php

/**
 * @file
 * Allows rich text fields to be edited using WYSIWYG client-side editors.
 */

// AJAX commands.
require_once dirname(__FILE__) . '/includes/editor.commands.inc';

// File usage tracking.
require_once dirname(__FILE__) . '/includes/editor.file_usage.inc';

// Hooks and functions missing from the core filter.module.
require_once dirname(__FILE__) . '/includes/editor.filter.inc';

// Filter hooks and callbacks.
require_once dirname(__FILE__) . '/includes/editor.filters.inc';

// Replace the core filter.module administration pages.
require_once dirname(__FILE__) . '/includes/editor.admin.inc';

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

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

/**
 * Implements hook_css_alter().
 */
function editor_css_alter(&$css) {
  $bartik_style_css = drupal_get_path('theme', 'bartik') . '/css/style.css';

  // Add caption CSS to the Bartik theme.
  if (isset($css[$bartik_style_css])) {
    $bartik_caption_css = drupal_get_path('module', 'editor') . '/css/components/bartik.captions.css';
    $css[$bartik_caption_css] = $css[$bartik_style_css];
    $css[$bartik_caption_css]['data'] = $bartik_caption_css;
  }
}

/**
 * Implements hook_module_implements_alter().
 */
function editor_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'element_info_alter') {

    // Move the editor.module implementation of hook_element_info_alter() to the
    // end of the list so that other modules can work with the default
    // filter.module text_format #process function before it is replaced.
    // module_implements() iterates through $implementations with a foreach loop
    // which PHP iterates in the order that the items were added, so to move an
    // item to the end of the array, we remove it and then add it.
    $group = $implementations['editor'];
    unset($implementations['editor']);
    $implementations['editor'] = $group;
  }
}

/**
 * Implements hook_menu_alter().
 */
function editor_menu_alter(&$items) {

  // Modify the formats page title and description to reflect the addition of
  // WYSIWYG editors.
  if (!empty($items['admin/config/content/formats'])) {
    $items['admin/config/content/formats']['title'] = 'Text editors and formats';
    $items['admin/config/content/formats']['description'] = 'Configure WYSIWYG and text editors on the site. Restrict or allow certain HTML tags to be used in content.';
  }

  // Disable tips for individual filter formats.
  if (!empty($items['filter/tips/%filter_format'])) {
    $items['filter/tips/%filter_format']['access callback'] = FALSE;
  }
}

/**
 * Implements hook_theme_registry_alter().
 */
function editor_theme_registry_alter(&$theme_registry) {

  // Drop textarea.js in favor of CSS3 resize.
  // @see https://www.drupal.org/node/1465840
  if (isset($theme_registry['textarea'])) {
    $path = drupal_get_path('module', 'editor');
    $theme_registry['textarea']['file'] = 'editor.theme.inc';
    $theme_registry['textarea']['theme path'] = $path . '/includes';
    $theme_registry['textarea']['function'] = 'editor_textarea';
    $theme_registry['textarea']['includes'][] = $theme_registry['textarea']['theme path'] . '/' . $theme_registry['textarea']['file'];
  }

  // Add an 'editor' column to the text format overview form.
  if (isset($theme_registry['filter_admin_overview'])) {
    $path = drupal_get_path('module', 'editor');
    $theme_registry['filter_admin_overview']['file'] = 'editor.theme.inc';
    $theme_registry['filter_admin_overview']['theme path'] = $path . '/includes';
    $theme_registry['filter_admin_overview']['function'] = 'editor_admin_overview';

    // Locate the default filter.module include.
    $filter_include = array_search('modules/filter/filter.admin.inc', $theme_registry['filter_admin_overview']['includes']);

    // Replace it with the editor.module include.
    array_splice($theme_registry['filter_admin_overview']['includes'], $filter_include, 1, $theme_registry['filter_admin_overview']['theme path'] . '/' . $theme_registry['filter_admin_overview']['file']);
  }
}

/**
 * Implements hook_hook_info().
 */
function editor_hook_info() {
  $hooks = array(
    'editor_info',
    'editor_info_alter',
  );
  return array_fill_keys($hooks, array(
    'group' => 'editor',
  ));
}

/**
 * Implements hook_theme().
 */
function editor_theme() {
  return array(
    'editor_caption' => array(
      'variables' => array(
        'item' => NULL,
        'caption' => '',
        'attributes' => array(),
      ),
      'file' => 'includes/editor.theme.inc',
    ),
  );
}

/**
 * Implements hook_library().
 */
function editor_library() {
  $libraries['drupal.editor'] = array(
    'title' => 'Drupal Editor',
    'website' => 'http://www.drupal.org',
    'version' => VERSION,
    'js' => array(
      drupal_get_path('module', 'editor') . '/js/editor.js' => array(
        'weight' => 2,
      ),
    ),
    'dependencies' => array(
      array(
        'system',
        'jquery',
      ),
      array(
        'dialog',
        'drupal.dialog',
      ),
    ),
  );
  $libraries['drupal.editor.dialog'] = array(
    'title' => 'Drupal Editor Dialog',
    'website' => 'http://www.drupal.org',
    'version' => VERSION,
    'js' => array(
      drupal_get_path('module', 'editor') . '/js/editor.dialog.js' => array(
        'weight' => 2,
      ),
    ),
    'dependencies' => array(
      array(
        'system',
        'jquery',
      ),
      array(
        'system',
        'drupal.ajax',
      ),
      array(
        'dialog',
        'drupal.dialog',
      ),
    ),
  );
  $libraries['quickedit.inPlaceEditor.formattedText'] = array(
    'title' => 'CKEditor formatted text in-place editor',
    'version' => VERSION,
    'js' => array(
      drupal_get_path('module', 'editor') . '/js/editor.formattedTextEditor.js' => array(
        'weight' => 3,
        'scope' => 'footer',
      ),
      array(
        'type' => 'setting',
        'data' => array(
          'quickedit' => array(
            'ckeditor' => array(
              'getUntransformedTextURL' => url('quickedit/ckeditor/!entity_type/!id/!field_name/!langcode/!view_mode'),
            ),
          ),
        ),
      ),
    ),
    'dependencies' => array(
      array(
        'quickedit',
        'quickedit',
      ),
      array(
        'editor',
        'drupal.editor',
      ),
      array(
        'system',
        'drupal.ajax',
      ),
    ),
  );
  $libraries['caption'] = array(
    'title' => 'Caption',
    'website' => 'http://www.drupal.org',
    'version' => VERSION,
    'css' => array(
      drupal_get_path('module', 'editor') . '/css/filter.caption.css' => array(),
    ),
  );
  $libraries['align'] = array(
    'title' => 'Align',
    'website' => 'http://www.drupal.org',
    'version' => VERSION,
    'css' => array(
      drupal_get_path('module', 'editor_ckeditor') . '/css/plugins/drupalimagecaption/editor_ckeditor.drupalimagecaption.css' => array(),
    ),
    'dependencies' => array(
      array(
        'editor',
        'caption',
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_element_info_alter().
 */
function editor_element_info_alter(&$type) {

  // Replace the default text_format #process function in order to add support
  // for editors.
  if (isset($type['text_format'])) {

    // Locate the default file.module #process function.
    $filter_process_format_location = array_search('filter_process_format', $type['text_format']['#process']);

    // Replace it with the editor.module #process function.
    array_splice($type['text_format']['#process'], $filter_process_format_location, 1, 'editor_process_format');
  }

  // Editor drops textarea.js in favor of CSS3 resizing of textareas.
  // Change the default value of #resizeable for textarea accordingly.
  if (isset($type['textarea'])) {
    $type['textarea']['#resizable'] = 'vertical';
  }
}

/**
 * A copy of filter_process_format() which adds support for editors.
 *
 * Expands an element into a base element with text format selector attached.
 *
 * The form element will be expanded into two separate form elements, one
 * holding the original element, and the other holding the text format selector:
 * - value: Holds the original element, having its #type changed to the value of
 *   #base_type or 'textarea' by default.
 * - format: Holds the text format fieldset and the text format selection, using
 *   the text format id specified in #format or the user's default format by
 *   default, if NULL.
 *
 * The resulting value for the element will be an array holding the value and
 * the format. For example, the value for the body element will be:
 * @code
 *   $form_state['values']['body']['value'] = 'foo';
 *   $form_state['values']['body']['format'] = 'foo';
 * @endcode
 *
 * @param array $element
 *   The form element to process. Properties used:
 *   - #base_type: The form element #type to use for the 'value' element.
 *     'textarea' by default.
 *   - #format: (optional) The text format ID to preselect. If NULL or not set,
 *     the default format for the current user will be used.
 *
 * @return array
 *   The expanded element.
 *
 * @see filter_process_format()
 */
function editor_process_format($element) {
  global $user;

  // Ensure that children appear as subkeys of this element.
  $element['#tree'] = TRUE;
  $blacklist = array(
    // Make form_builder() regenerate child properties.
    '#parents',
    '#id',
    '#name',
    // Do not copy this #process function to prevent form_builder() from
    // recursing infinitely.
    '#process',
    // Description is handled by theme_text_format_wrapper().
    '#description',
    // Ensure proper ordering of children.
    '#weight',
    // Properties already processed for the parent element.
    '#prefix',
    '#suffix',
    '#attached',
    '#processed',
    '#theme_wrappers',
  );

  // Move this element into sub-element 'value'.
  unset($element['value']);
  foreach (element_properties($element) as $key) {
    if (!in_array($key, $blacklist)) {
      $element['value'][$key] = $element[$key];
    }
  }
  $element['value']['#type'] = $element['#base_type'];
  $element['value'] += element_info($element['#base_type']);

  // Get a list of formats to which the current user has access.
  $formats = filter_formats($user);

  // JavaScript settings are not idempotent in Drupal 7 which causes editor
  // configuration information to be added once per text area, resulting in
  // duplicate toolbars, plugins, etc.
  // @see https://www.drupal.org/node/1911578
  static $has_run = FALSE;

  // Ensure that editor attachments are only added once by tracking whether or
  // not they have already been attached.
  if (!$has_run) {

    // Turn original element into a text format wrapper.
    $element['#attached'] = editor_get_attached($formats);
    $has_run = TRUE;
  }

  // Attach Editor module's (this module) library.
  $element['#attached']['library'][] = array(
    'filter',
    'filter',
  );
  $element['#attached']['library'][] = array(
    'editor',
    'drupal.editor',
  );
  $element['#attached']['library'][] = array(
    'editor',
    'drupal.editor.dialog',
  );

  // Use the default format for this user if none was selected.
  if (!isset($element['#format'])) {
    $element['#format'] = filter_default_format($user);
  }

  // Remove the Plain text format if not set and other options are available.
  $fallback_format = variable_get('filter_fallback_format');
  if ($element['#format'] != $fallback_format && count($formats) > 1 && array_key_exists($fallback_format, $formats)) {
    unset($formats[$fallback_format]);
  }

  // Setup child container for the text format widget.
  $element['format'] = array(
    '#type' => 'fieldset',
    '#attributes' => array(
      'class' => array(
        'filter-wrapper',
      ),
    ),
  );

  // Prepare text format guidelines.
  $element['format']['guidelines'] = array(
    '#type' => 'container',
    '#attributes' => array(
      'class' => array(
        'filter-guidelines',
      ),
    ),
    '#weight' => 20,
  );
  foreach ($formats as $format) {
    $options[$format->format] = $format->name;
    $element['format']['guidelines'][$format->format] = array(
      '#theme' => 'filter_guidelines',
      '#format' => $format,
    );
  }

  // If there are multiple options OR if the current text format is no
  // longer available to the current user, then show as a select.
  if (count($options) > 1 || !array_key_exists($element['#format'], $options)) {
    $element['format']['format'] = array(
      '#type' => 'select',
      '#title' => t('Editor'),
      '#options' => $options,
      '#default_value' => $element['#format'],
      '#weight' => 10,
      '#attributes' => array(
        'class' => array(
          'filter-list',
        ),
      ),
      '#parents' => array_merge($element['#parents'], array(
        'format',
      )),
    );
  }
  else {
    $element['format']['format'] = array(
      '#type' => 'hidden',
      '#value' => $element['#format'],
      '#default_value' => $element['#format'],
      '#attributes' => array(
        'class' => array(
          'filter-list',
        ),
      ),
      '#parents' => array_merge($element['#parents'], array(
        'format',
      )),
    );
  }
  $all_formats = filter_formats();
  $format_exists = isset($all_formats[$element['#format']]);
  $user_has_access = isset($formats[$element['#format']]);
  $user_is_admin = user_access('administer filters');

  // If the stored format does not exist, administrators have to assign a new
  // format.
  if (!$format_exists && $user_is_admin) {
    $element['format']['format']['#required'] = TRUE;
    $element['format']['format']['#default_value'] = NULL;

    // Force access to the format selector (it may have been denied above if
    // the user only has access to a single format).
    $element['format']['format']['#access'] = TRUE;
  }
  elseif (!$user_has_access || !$format_exists) {

    // Overload default values into #value to make them unalterable.
    $element['value']['#value'] = $element['value']['#default_value'];
    $element['format']['format']['#value'] = $element['format']['format']['#default_value'];

    // Prepend #pre_render callback to replace field value with user notice
    // prior to rendering.
    $element['value'] += array(
      '#pre_render' => array(),
    );
    array_unshift($element['value']['#pre_render'], 'filter_form_access_denied');

    // Cosmetic adjustments.
    if (isset($element['value']['#rows'])) {
      $element['value']['#rows'] = 3;
    }
    $element['value']['#disabled'] = TRUE;
    $element['value']['#resizable'] = 'none';

    // Hide the text format selector and any other child element (such as text
    // field's summary).
    foreach (element_children($element) as $key) {
      if ($key != 'value') {
        $element[$key]['#access'] = FALSE;
      }
    }
  }
  return $element;
}

/**
 * Adds filter configuration information to the page for access by JavaScript.
 *
 * @param array $formats
 *   An array of formats as returned by filter_formats(), whose settings should
 *   be added to the page.
 *
 * @return array
 *   An array of attached libraries, CSS, and JS that can be set to an element's
 *   #attached property.
 */
function editor_get_attached($formats) {
  $attached = array();
  foreach ($formats as $format_name => $format_info) {
    if (!isset($format_info->editor)) {
      $format_info->editor = NULL;
    }
    if (isset($added[$format_name])) {
      unset($formats[$format_name]);
    }
    else {

      // Add the library associated with a format's editor if needed.
      if ($format_info->editor && ($editor = editor_load($format_info->editor))) {
        $attached['library'][] = $editor['library'];
      }
    }
  }
  if (!empty($formats)) {
    $settings = editor_get_js_settings($formats);
    $attached['js'][] = array(
      'type' => 'setting',
      'data' => array(
        'editor' => array(
          'formats' => $settings,
        ),
      ),
    );
  }
  return $attached;
}

/**
 * Get a complete list of allowed and forbidden tags for a text format.
 *
 * @param object $format
 *   The text format object for which the list will be generated.
 *
 * @return array|TRUE
 *   An array of allowed HTML with the following keys:
 *   - allowed: A list of allowed tags keyed by tag name. The value is an array
 *     of attributes.
 *   - forbidden: An unindexed array of tags that are not allowed.
 *   For the full documentation on the return values of these two properties,
 *   see callback_filter_allowed_html().
 *   If TRUE is returned, then there are no restrictions on this format's HTML
 *   content.
 *
 * @see callback_filter_allowed_html()
 */
function editor_format_allowed_html($format) {
  $all_filter_info = filter_get_filters();
  $all_html_allowed = TRUE;
  $restrictions = array(
    'allowed' => array(),
    'forbidden' => array(),
  );
  foreach ($format->filters as $filter_name => $filter) {

    // Skip disabled filters.
    if (!$filter->status) {
      continue;
    }

    // Skip non-existent filters.
    if (!isset($all_filter_info[$filter_name])) {
      continue;
    }

    // We're only concerned with filters that specify an allowed HTML callback.
    $filter_info = $all_filter_info[$filter_name];
    if (!isset($filter_info['allowed html callback'])) {
      continue;
    }
    $allowed_html_callback = $filter_info['allowed html callback'];
    $filter_restrictions = $allowed_html_callback($filter, $format);
    if ($filter_restrictions) {
      $all_html_allowed = FALSE;
    }
    else {
      continue;
    }

    // Forbidden tags are simple in that they have no attributes to track, it's
    // just a list of tags that are not allowed.
    if (isset($filter_restrictions['forbidden'])) {
      $restrictions['forbidden'] = array_unique(array_merge($restrictions['forbidden'], $filter_restrictions['forbidden']));
    }

    // Add any allowed tags that have not yet been specified and build a list
    // of any that need to be intersected.
    $intersected_tags = array();
    foreach ($filter_restrictions['allowed'] as $tag => $attributes) {
      if (!isset($restrictions['allowed'][$tag])) {
        $restrictions['allowed'][$tag] = $attributes;
      }
      else {
        $intersected_tags[$tag] = $attributes;
      }
    }

    // Allowed tags are more complicated as different filters may allow
    // different individual attributes. Track the intersection of all allowed
    // attributes for each tag.
    foreach ($intersected_tags as $tag => $attributes) {
      $intersection = NULL;
      $current_attributes = isset($restrictions['allowed'][$tag]) ? $restrictions['allowed'][$tag] : array();
      $new_attributes = $filter_restrictions['allowed'][$tag];

      // The current intersection does not allow any attributes, never allow.
      if (!is_array($current_attributes) && $current_attributes == FALSE) {
        continue;
      }
      elseif (!is_array($current_attributes) && $current_attributes == TRUE && ($new_attributes == FALSE || is_array($new_attributes))) {
        $intersection = $new_attributes;
      }
      elseif (is_array($current_attributes) && $new_attributes == FALSE) {
        $intersection = $new_attributes;
      }
      elseif (is_array($current_attributes) && $new_attributes == TRUE) {
        continue;
      }
      elseif ($current_attributes == $new_attributes) {
        continue;
      }
      else {
        $intersection = array_intersect_key($current_attributes, $new_attributes);
        foreach (array_keys($intersection) as $attribute_value) {
          $intersection[$attribute_value] = $intersection[$attribute_value] && $new_attributes[$attribute_value];
        }
      }
      if (isset($intersection)) {
        $restrictions['allowed'][$tag] = $intersection;
      }
    }
  }

  // Simplification: if we have both a (intersected) whitelist and a (unioned)
  // blacklist, then remove any tags from the whitelist that also exist in the
  // blacklist. Now the whitelist alone expresses all tag-level restrictions,
  // and we can delete the blacklist.
  if (isset($restrictions['allowed']) && isset($restrictions['forbidden'])) {
    foreach ($restrictions['forbidden'] as $tag) {
      if (isset($restrictions['allowed'][$tag])) {
        unset($restrictions['allowed'][$tag]);
      }
    }
    $restrictions['forbidden'] = array();
  }

  // Simplification: if the only remaining allowed tag is the asterisk (which
  // contains attribute restrictions that apply to all tags), and only
  // whitelisting filters were used, then effectively nothing is allowed.
  if (isset($restrictions['allowed'])) {
    if (count($restrictions['allowed']) === 1 && array_key_exists('*', $restrictions['allowed']) && !isset($restrictions['forbidden'])) {
      $restrictions['allowed'] = array();
    }
  }

  // If no filters specified restrictions, change the allowed values to be
  // a Boolean.
  if ($all_html_allowed) {
    $restrictions = TRUE;
  }
  return $restrictions;
}

/**
 * Checks a user's access to a particular text format.
 *
 * @param object $format
 *   A text format object.
 *
 * @return bool
 *   TRUE if the text format can be used by the current user, FALSE otherwise.
 */
function editor_format_access($format) {
  $permission = filter_permission_name($format);
  return $format->format === filter_fallback_format() || user_access($permission);
}

/**
 * Loads an individual editor's information.
 *
 * @param string|FALSE $editor_name
 *   The internal editor name of the editor to load.
 */
function editor_load($editor_name) {
  $editors = editor_get_editors();
  return isset($editors[$editor_name]) ? $editors[$editor_name] : FALSE;
}

/**
 * Returns a list of text editors that are used with 'text_format' elements.
 *
 * @return array
 *   An associative array of editors keyed by the internal name of the editor.
 *   Each editor may contain the following elements (all are optional except as
 *   noted):
 *   - title: (required) A human readable name for the editor.
 *   - settings callback: The name of a function that returns configuration
 *     form elements for the editor. See hook_editor_EDITOR_settings() for
 *     details.
 *   - default settings: An associative array containing default settings for
 *     the editor, to be applied when the editor has not been configured yet.
 *   - js settings callback: The name of a function that returns configuration
 *     options that should be added to the page via JavaScript for use on the
 *     client side. See hook_editor_EDITOR_js_settings() for details.
 *
 * @see filter_example.module
 * @see hook_filter_info()
 * @see hook_filter_info_alter()
 */
function editor_get_editors() {
  $editors =& drupal_static(__FUNCTION__, NULL);
  if (!isset($editors)) {
    $editors = array();
    $modules = module_implements('editor_info');
    foreach ($modules as $module) {
      $module_editors = module_invoke($module, 'editor_info');
      foreach ($module_editors as $editor_name => $editor) {
        $editor['module'] = $module;
        $editors[$editor_name] = $editor;
      }
    }
    drupal_alter('editor_info', $editors);
  }
  return $editors;
}

/**
 * Retrieve JavaScript settings that should be added by each filter.
 *
 * @param array $formats
 *   An array of formats as returned by filter_formats().
 *
 * @return array
 *   An array of JavaScript settings representing the configuration of the
 *   filters.
 */
function editor_get_js_settings($formats) {
  $settings = array();
  $filter_info = filter_get_filters();
  $editor_info = editor_get_editors();
  foreach ($formats as $format_name => $format) {
    editor_format_ensure_additional_properties($format);

    // Don't add settings for formats that don't have associated editors.
    if (!$format->editor) {
      continue;
    }
    $filter_settings = array();
    foreach ($format->filters as $filter_name => $filter) {
      if ($filter->status && isset($filter_info[$filter_name]['js settings callback'])) {
        $function = $filter_info[$filter_name]['js settings callback'];
        $filter_settings += $function($filter, $format);
      }
    }
    $settings[$format_name] = array(
      'format' => $format,
      'filterSettings' => $filter_settings,
      'editor' => $format->editor,
      'editorSettings' => array(),
    );
    if ($format->editor && isset($editor_info[$format->editor]['js settings callback'])) {
      $function = $editor_info[$format->editor]['js settings callback'];
      $settings[$format_name]['editorSettings'] = $function($format, $settings);
    }
  }
  drupal_alter('filter_js_settings', $settings, $formats);
  return $settings;
}

/**
 * Ensures that a text format has the additional properties added by Editor.
 *
 * This is required when interacting with a format freshly loaded with
 * filter_format_load() because filter.module does not provide a way to load
 * additional data when a format is retrieved from the database.
 *
 * @param object $format
 *   An object representing the text format.
 *
 * @todo Remove this when we can alter filter_format_load() or filter_formats().
 */
function editor_format_ensure_additional_properties($format) {
  if (empty($format->editor)) {
    $format->editor = NULL;
  }
  if (empty($format->editor_settings)) {
    $format->editor_settings = array();
  }
  else {

    // Unserialize the editor settings when necessary.
    $editor_settings = $format->editor_settings;
    if ($editor_settings == serialize(false) || @unserialize($editor_settings) !== false) {
      $format->editor_settings = unserialize($editor_settings);
    }
  }
  if (empty($format->filters)) {
    $format->filters = array();
    $filters = filter_list_format($format->format);
    foreach ($filters as $name => $filter) {
      $format->filters[$name] = $filter;
    }
  }
}

Functions

Namesort descending Description
editor_css_alter Implements hook_css_alter().
editor_element_info_alter Implements hook_element_info_alter().
editor_format_access Checks a user's access to a particular text format.
editor_format_allowed_html Get a complete list of allowed and forbidden tags for a text format.
editor_format_ensure_additional_properties Ensures that a text format has the additional properties added by Editor.
editor_get_attached Adds filter configuration information to the page for access by JavaScript.
editor_get_editors Returns a list of text editors that are used with 'text_format' elements.
editor_get_js_settings Retrieve JavaScript settings that should be added by each filter.
editor_hook_info Implements hook_hook_info().
editor_init Implements hook_init().
editor_library Implements hook_library().
editor_load Loads an individual editor's information.
editor_menu_alter Implements hook_menu_alter().
editor_module_implements_alter Implements hook_module_implements_alter().
editor_process_format A copy of filter_process_format() which adds support for editors.
editor_theme Implements hook_theme().
editor_theme_registry_alter Implements hook_theme_registry_alter().