diff --git a/core/modules/filter/config/filter.format.plain_text.yml b/core/modules/filter/config/filter.format.plain_text.yml
index ff530c5..a7f72cd 100644
--- a/core/modules/filter/config/filter.format.plain_text.yml
+++ b/core/modules/filter/config/filter.format.plain_text.yml
@@ -1,27 +1,26 @@
+# Every site requires at least one text format as fallback format that
+# - is accessible to all users.
+# - is secure, using very basic formatting only.
+# - may be modified by installation profiles to have other properties.
format: plain_text
name: 'Plain text'
-cache: '1'
status: '1'
weight: '10'
roles:
- anonymous: anonymous
- authenticated: authenticated
- administrator: administrator
+ - anonymous
+ - authenticated
+cache: '1'
filters:
+ # Escape all HTML.
filter_html_escape:
module: filter
- settings: { }
status: '1'
- weight: '0'
+ # Convert URLs into links.
filter_url:
module: filter
- settings:
- filter_url_length: '72'
status: '1'
- weight: '1'
+ # Convert linebreaks into paragraphs.
filter_autop:
module: filter
- settings: { }
status: '1'
- weight: '2'
langcode: und
diff --git a/core/modules/filter/filter.admin.inc b/core/modules/filter/filter.admin.inc
index fa33501..b9fb91c 100644
--- a/core/modules/filter/filter.admin.inc
+++ b/core/modules/filter/filter.admin.inc
@@ -77,9 +77,9 @@ function filter_admin_overview($form) {
function filter_admin_overview_submit($form, &$form_state) {
$filter_formats = filter_formats();
foreach ($form_state['values']['formats'] as $id => $data) {
+ // Only update if this is a form element with weight.
if (is_array($data) && isset($data['weight'])) {
- // Only update if this is a form element with weight.
- $filter_formats[$id]->weight = $data['weight'];
+ $filter_formats[$id]->set('weight', $data['weight']);
$filter_formats[$id]->save();
}
}
@@ -209,6 +209,10 @@ function filter_admin_format_form($form, &$form_state, $format) {
'#title' => t('Enabled filters'),
'#prefix' => '
',
'#suffix' => '
',
+ // This item is used as a pure wrapping container with heading. Ignore its
+ // value, since 'filters' should only contain filter definitions.
+ // @see http://drupal.org/node/1829202
+ '#input' => FALSE,
);
foreach ($filter_info as $name => $filter) {
$form['filters']['status'][$name] = array(
@@ -226,6 +230,10 @@ function filter_admin_format_form($form, &$form_state, $format) {
'#type' => 'item',
'#title' => t('Filter processing order'),
'#theme' => 'filter_admin_format_filter_order',
+ // This item is used as a pure wrapping container with heading. Ignore its
+ // value, since 'filters' should only contain filter definitions.
+ // @see http://drupal.org/node/1829202
+ '#input' => FALSE,
);
foreach ($filter_info as $name => $filter) {
$form['filters']['order'][$name]['filter'] = array(
@@ -256,8 +264,7 @@ function filter_admin_format_form($form, &$form_state, $format) {
$function = $filter['settings callback'];
// Pass along stored filter settings and default settings, but also the
// format object and all filters to allow for complex implementations.
- $defaults = (isset($filter['default settings']) ? $filter['default settings'] : array());
- $settings_form = $function($form, $form_state, $filters[$name], $format, $defaults, $filters);
+ $settings_form = $function($form, $form_state, $filters[$name], $format, $filter['default settings'], $filters);
if (!empty($settings_form)) {
$form['filters']['settings'][$name] = array(
'#type' => 'details',
@@ -339,10 +346,13 @@ function filter_admin_format_form_submit($form, &$form_state) {
// Remove unnecessary values.
form_state_values_clean($form_state);
+ // Reduce roles to a simple list of enabled roles.
+ $form_state['values']['roles'] = array_keys(array_filter($form_state['values']['roles']));
+
// Add the submitted form values to the text format, and save it.
$format = $form['#format'];
foreach ($form_state['values'] as $key => $value) {
- $format->$key = $value;
+ $format->set($key, $value);
}
$status = $format->save();
diff --git a/core/modules/filter/filter.install b/core/modules/filter/filter.install
index 6f7432a..39ef59b 100644
--- a/core/modules/filter/filter.install
+++ b/core/modules/filter/filter.install
@@ -3,13 +3,6 @@
/**
* @file
* Install, update, and uninstall functions for the Filter module.
- *
- * All sites require at least one text format (the fallback format) that all
- * users have access to, so add it here. We initialize it as a simple, safe
- * plain text format with very basic formatting, but it can be modified by
- * installation profiles to have other properties.
- *
- * @see core/modules/filter/config/filter.format.plain_text.yml
*/
use Drupal\Component\Uuid\Uuid;
@@ -47,28 +40,32 @@ function filter_update_8000() {
*/
function filter_update_8001() {
$uuid = new Uuid();
- $result = db_query('SELECT * FROM {filter_format}', array(), array('fetch' => PDO::FETCH_ASSOC));
- foreach ($result as $filter_format) {
- // Find the settings for this format.
- $filters = array();
- $settings = db_query('SELECT * FROM {filter} WHERE format = :format', array(':format' => $filter_format['format']), array('fetch' => PDO::FETCH_ASSOC));
- foreach ($settings as $setting) {
- $setting['settings'] = unserialize($setting['settings']);
- $filters[$setting['name']] = $setting;
+ $result = db_query('SELECT format, name, cache, status, weight FROM {filter_format}', array(), array('fetch' => PDO::FETCH_ASSOC));
+ foreach ($result as $format) {
+ $id = $format['format'];
+
+ // Generate a UUID.
+ $format['uuid'] = $uuid->generate();
+
+ // Add user roles.
+ $format['roles'] = array_keys(user_roles(FALSE, 'use text format ' . $format['format']));
+
+ // Retrieve and prepare all filters.
+ $filters = db_query('SELECT name, module, status, weight, settings FROM {filter} WHERE format = :format ORDER BY weight, module, name', array(
+ ':format' => $id,
+ ), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('name');
+ foreach ($filters as $name => &$filter) {
+ // The filter name is used as key only.
+ unset($filter['name']);
+ $filter['settings'] = unserialize($filter['settings']);
}
+ $format['filters'] = $filters;
// Save the config object.
- $filter_format += array(
- 'filters' => $filters,
- 'roles' => drupal_map_assoc(array_keys(user_roles(FALSE, 'use text format ' . $filter_format['format']))),
- 'uuid' => $uuid->generate(),
- );
- $config = config('filter.format.' . $filter_format['format']);
- foreach ($filter_format as $key => $value) {
- $config->set($key, $value);
- }
+ $config = config('filter.format.' . $id);
+ $config->setData($format);
$config->save();
- update_config_manifest_add('filter.format', array($filter_format['format']));
+ update_config_manifest_add('filter.format', array($id));
}
}
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index b9c9b9d..b914dd9 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -171,56 +171,6 @@ function filter_menu() {
}
/**
- * Implements MODULE_config_import_create().
- */
-function filter_config_import_create($name, $new_config, $old_config) {
- if (strpos($name, 'filter.format.') !== 0) {
- return FALSE;
- }
-
- $filter_format = entity_create('filter_format', $new_config->get());
- $filter_format->save();
- return TRUE;
-}
-
-/**
- * Implements MODULE_config_import_change().
- */
-function filter_config_import_change($name, $new_config, $old_config) {
- if (strpos($name, 'filter.format.') !== 0) {
- return FALSE;
- }
-
- list(, , $id) = explode('.', $name);
- $filter_format = entity_load('filter_format', $id);
-
- $filter_format->original = clone $filter_format;
- foreach ($old_config->get() as $property => $value) {
- $filter_format->original->$property = $value;
- }
-
- foreach ($new_config->get() as $property => $value) {
- $filter_format->$property = $value;
- }
-
- $filter_format->save();
- return TRUE;
-}
-
-/**
- * Implements MODULE_config_import_delete().
- */
-function filter_config_import_delete($name, $new_config, $old_config) {
- if (strpos($name, 'filter.format.') !== 0) {
- return FALSE;
- }
-
- list(, , $id) = explode('.', $name);
- entity_delete_multiple('filter_format', array($id));
- return TRUE;
-}
-
-/**
* Access callback: Checks access for disabling text formats.
*
* @param $format
@@ -603,6 +553,7 @@ function filter_get_filters() {
$info[$name] += array(
'description' => '',
'weight' => 0,
+ 'default settings' => array(),
);
}
$filters = array_merge($filters, $info);
@@ -627,25 +578,6 @@ function _filter_list_cmp($a, $b) {
}
/**
- * Sorts an array of filters by filter status, weight, module, name.
- *
- * @see filter_list_format()
- * @see Drupal\filter\Plugin\Core\Entity\FilterFormat::save()
- */
-function _filter_format_filter_cmp($a, $b) {
- if ($a['status'] != $b['status']) {
- return !empty($a['status']) ? -1 : 1;
- }
- if ($a['weight'] != $b['weight']) {
- return ($a['weight'] < $b['weight']) ? -1 : 1;
- }
- elseif ($a['module'] != $b['module']) {
- return strcmp($a['module'], $b['module']);
- }
- return strcmp($a['name'], $b['name']);
-}
-
-/**
* Checks if the text in a certain text format is allowed to be cached.
*
* This function can be used to check whether the result of the filtering
@@ -722,12 +654,11 @@ function filter_list_format($format_id) {
$filter['name'] = $filter_name;
$filters['all'][$filter_format->format][$filter_name] = $filter;
}
- @uasort($filters['all'][$filter_format->format], '_filter_format_filter_cmp');
+ uasort($filters['all'][$filter_format->format], 'Drupal\filter\Plugin\Core\Entity\FilterFormat::sortFilters');
+ // Convert filters into objects.
+ // @todo Retain filters as arrays or convert to plugins.
foreach ($filters['all'][$filter_format->format] as $filter_name => $filter) {
- // Before Conversion to CMI, filter were objects, now they are arrays.
- // Convert filters back to objects to reduce the impact of changes.
- // @todo Follow-up: filters should be arrays instead of objects.
- $filters['all'][$filter_format->format][$filter_name] = (object)$filter;
+ $filters['all'][$filter_format->format][$filter_name] = (object) $filter;
}
}
cache()->set('filter_list_format', $filters['all']);
@@ -742,11 +673,8 @@ function filter_list_format($format_id) {
$filter->title = $filter_info[$name]['title'];
$filter->settings = isset($filter->settings) ? $filter->settings : array();
-
// Merge in default settings.
- if (isset($filter_info[$name]['default settings'])) {
- $filter->settings += $filter_info[$name]['default settings'];
- }
+ $filter->settings += $filter_info[$name]['default settings'];
$format_filters[$name] = $filter;
}
diff --git a/core/modules/filter/lib/Drupal/filter/FilterFormatStorageController.php b/core/modules/filter/lib/Drupal/filter/FilterFormatStorageController.php
index 7e5bbdb..8cffc23 100644
--- a/core/modules/filter/lib/Drupal/filter/FilterFormatStorageController.php
+++ b/core/modules/filter/lib/Drupal/filter/FilterFormatStorageController.php
@@ -19,55 +19,69 @@ class FilterFormatStorageController extends ConfigStorageController {
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::preSave().
*/
protected function preSave(EntityInterface $entity) {
- $entity->name = trim($entity->name);
+ parent::preSave($entity);
+
+ $entity->name = trim($entity->label());
$entity->cache = _filter_format_is_cacheable($entity);
$filter_info = filter_get_filters();
foreach ($filter_info as $name => $filter) {
- // If the format does not specify an explicit weight for a filter, assign
- // a default weight, either defined in hook_filter_info(), or the default of
- // 0 by filter_get_filters().
- if (!isset($entity->filters[$name]['weight'])) {
- $entity->filters[$name]['weight'] = $filter['weight'];
+ // Ensure a consistent definition and order of properties for all filters
+ // by merging the actual filter definition into defaults.
+ $defaults = array(
+ 'module' => $filter['module'],
+ // The filter ID has to be temporarily injected into the properties, in
+ // order to sort all filters below.
+ // @todo Rethink filter sorting to remove dependency on filter IDs.
+ 'name' => $name,
+ // Unless explicitly enabled, all filters are disabled by default.
+ 'status' => 0,
+ // If no explicit weight was defined for a filter, assign either the
+ // default weight defined in hook_filter_info() or the default of 0 by
+ // filter_get_filters().
+ 'weight' => $filter['weight'],
+ 'settings' => $filter['default settings'],
+ );
+ // All available filters are saved for each format, in order to retain all
+ // filter properties regardless of whether a filter is currently enabled
+ // or not, since some filters require extensive configuration.
+ // @todo Do not save disabled filters whose properties are identical to
+ // all default properties.
+ if (isset($entity->filters[$name])) {
+ $entity->filters[$name] = array_merge($defaults, $entity->filters[$name]);
}
- $entity->filters[$name]['status'] = isset($entity->filters[$name]['status']) ? $entity->filters[$name]['status'] : 0;
- $entity->filters[$name]['module'] = $filter['module'];
-
- // If settings were passed, only ensure default settings.
- if (isset($entity->filters[$name]['settings'])) {
- if (isset($filter['default settings'])) {
- $entity->filters[$name]['settings'] = array_merge($filter['default settings'], $entity->filters[$name]['settings']);
- }
- }
- // Otherwise, use default settings or fall back to an empty array.
else {
- $entity->filters[$name]['settings'] = isset($filter['default settings']) ? $filter['default settings'] : array();
+ $entity->filters[$name] = $defaults;
}
-
- // Sort filters properties by key, to minimize diff issues.
- ksort($entity->filters[$name]);
+ // The module definition from hook_filter_info() always takes precedence
+ // and needs to be updated in case it changes.
+ $entity->filters[$name]['module'] = $filter['module'];
}
- // Sort filters by enabled/disabled first then by weight
- @uasort($entity->filters, '_filter_format_filter_cmp');
+ // Sort all filters.
+ uasort($entity->filters, 'Drupal\filter\Plugin\Core\Entity\FilterFormat::sortFilters');
+ // Remove the 'name' property from all filters that was added above.
+ foreach ($entity->filters as &$filter) {
+ unset($filter['name']);
+ }
}
/**
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postSave().
*/
protected function postSave(EntityInterface $entity, $update) {
- if ($update == SAVED_UPDATED) {
+ parent::postSave($entity, $update);
+
+ if ($update) {
// Clear the filter cache whenever a text format is updated.
- cache('filter')->deleteTags(array('filter_format' => $entity->format));
+ cache('filter')->deleteTags(array('filter_format' => $entity->id()));
}
-
filter_formats_reset();
- if (!empty($entity->status)) {
- // Save user permissions.
- if ($permission = filter_permission_name($entity)) {
- foreach ($entity->roles as $rid => $enabled) {
- user_role_change_permissions($rid, array($permission => $enabled));
- }
+ // Save user role permissions.
+ if ($permission = filter_permission_name($entity)) {
+ foreach (user_roles() as $rid => $name) {
+ $enabled = in_array($rid, $entity->roles);
+ user_role_change_permissions($rid, array($permission => $enabled));
}
}
}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
index 8e47dbb..2f6e6d4 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
@@ -12,11 +12,11 @@
use Drupal\Core\Annotation\Translation;
/**
- * Defines the Filter Format entity.
+ * Represents a text format.
*
* @Plugin(
* id = "filter_format",
- * label = @Translation("Filter Format"),
+ * label = @Translation("Text format"),
* module = "filter",
* controller_class = "Drupal\filter\FilterFormatStorageController",
* config_prefix = "filter.format",
@@ -39,7 +39,11 @@ class FilterFormat extends ConfigEntityBase {
public $format;
/**
- * Name of the text format (Filtered HTML).
+ * Unique label of the text format.
+ *
+ * Since text formats impact a site's security, two formats with the same
+ * label but different filter configuration would impose a security risk.
+ * Therefore, each text format label must be unique.
*
* @todo Rename to $label.
*
@@ -55,51 +59,80 @@ class FilterFormat extends ConfigEntityBase {
public $uuid;
/**
- * Flag to indicate whether format is cacheable. (1 = cacheable, 0 = not
- * cacheable).
- *
- * @var int
- */
- public $cache = 0;
-
- /**
- * The status of the text format. (1 = enabled, 0 = disabled)
+ * Whether the text format is enabled or disabled.
*
- * @var int
+ * @var bool
*/
public $status = 1;
/**
- * Weight of text format to use when listing.
+ * Weight of this format in the text format selector.
+ *
+ * The first/lowest text format that is accessible for a user is used as
+ * default format.
*
* @var int
*/
public $weight = 0;
/**
- * An array of name value pairs of the roles that can use this format.
+ * List of user role IDs that are allowed to use this format.
*
* @var array
*/
public $roles = array();
/**
- * An array of name value pairs of the enabled filters for this text format.
+ * Whether processed text of this format can be cached.
*
- * Each element of this array must contain at least the following values:
- * - weight: Weight of filter within format.
- * - settings: An array of name value pairs that store the filter settings
- * for the specific format.
+ * @var bool
+ */
+ public $cache = 0;
+
+ /**
+ * Configured filters for this text format.
+ *
+ * An associative array of filters assigned to the text format, keyed by the
+ * ID of each filter (prefixed with module name) and using the properties:
+ * - module: The name of the module providing the filter.
+ * - status: (optional) A Boolean indicating whether the filter is
+ * enabled in the text format. Defaults to disabled.
+ * - weight: (optional) The weight of the filter in the text format. If
+ * omitted, the default value is determined in the following order:
+ * - if any, the currently stored weight is retained.
+ * - if any, the default weight from hook_filter_info() is taken over.
+ * - otherwise, a default weight of 10, which usually sorts it last.
+ * - settings: (optional) An array of configured settings for the filter.
+ * See hook_filter_info() for details.
*
* @var array
*/
public $filters = array();
/**
- * Implements \Drupal\Core\Entity\EntityInterface::id().
+ * Overrides \Drupal\Core\Entity\Entity::id().
*/
public function id() {
return $this->format;
}
+ /**
+ * Helper callback for uasort() to sort filters by status, weight, module, and name.
+ *
+ * @see Drupal\filter\FilterFormatStorageController::preSave()
+ * @see filter_list_format()
+ */
+ public static function sortFilters($a, $b) {
+ if ($a['status'] != $b['status']) {
+ return !empty($a['status']) ? -1 : 1;
+ }
+ if ($a['weight'] != $b['weight']) {
+ return ($a['weight'] < $b['weight']) ? -1 : 1;
+ }
+ if ($a['module'] != $b['module']) {
+ return strnatcasecmp($a['module'], $b['module']);
+ }
+ return strnatcasecmp($a['name'], $b['name']);
+ }
+
}
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
index 02031e3..cbeecd0 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
@@ -42,16 +42,9 @@ function testDefaultTextFormats() {
// Adjust the weights so that the first and second formats (in that order)
// are the two lowest weighted formats available to any user.
- $minimum_weight = NULL;
+ $minimum_weight = 0;
foreach (entity_load_multiple('filter_format') as $format) {
- if (is_null($minimum_weight)) {
- $minimum_weight = $format->weight;
- }
- else {
- if ($minimum_weight > $format->weight) {
- $minimum_weight = $format->weight;
- }
- }
+ $minimum_weight = min($minimum_weight, $format->weight);
}
$edit = array();
$edit['formats[' . $first_format->format . '][weight]'] = $minimum_weight - 2;
diff --git a/core/modules/php/config/filter.format.php_code.yml b/core/modules/php/config/filter.format.php_code.yml
index 504b970..cee6e9b 100644
--- a/core/modules/php/config/filter.format.php_code.yml
+++ b/core/modules/php/config/filter.format.php_code.yml
@@ -1,11 +1,10 @@
format: php_code
name: 'PHP code'
-cache: '0'
status: '1'
weight: '11'
+cache: '0'
filters:
php_code:
module: php
status: '1'
- weight: '0'
langcode: und
diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php
index f23204b..b7fd25c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleTestBase.php
@@ -99,7 +99,7 @@ function assertModuleConfig($module) {
return;
}
$module_file_storage = new FileStorage($module_config_dir);
- $names = $module_file_storage->listAll();
+ $names = $module_file_storage->listAll($module . '.');
// Verify that the config directory is not empty.
$this->assertTrue($names);
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index 41e7d2d..64275cc 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -71,7 +71,7 @@ function taxonomy_schema() {
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
- 'description' => 'The filter format of the description.',
+ 'description' => 'The filter format ID of the description.',
),
'weight' => array(
'type' => 'int',
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 6416654..d20b352 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -75,7 +75,7 @@ function user_schema() {
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
- 'description' => 'The Filter Format id of the signature.',
+ 'description' => 'The filter format ID of the signature.',
),
'created' => array(
'type' => 'int',
diff --git a/core/profiles/standard/config/filter.format.filtered_html.yml b/core/profiles/standard/config/filter.format.filtered_html.yml
index 3ba3ef8..11623fe 100644
--- a/core/profiles/standard/config/filter.format.filtered_html.yml
+++ b/core/profiles/standard/config/filter.format.filtered_html.yml
@@ -1,35 +1,22 @@
format: filtered_html
name: 'Filtered HTML'
-cache: '1'
status: '1'
weight: '0'
roles:
- anonymous: anonymous
- authenticated: authenticated
- administrator: administrator
+ - anonymous
+ - authenticated
+cache: '1'
filters:
filter_url:
module: filter
- settings:
- filter_url_length: '72'
status: '1'
- weight: '0'
filter_html:
module: filter
- settings:
- allowed_html: ' -
-
- '
- filter_html_help: '1'
- filter_html_nofollow: '0'
status: '1'
- weight: '1'
filter_autop:
module: filter
- settings: { }
status: '1'
- weight: '2'
filter_htmlcorrector:
module: filter
- settings: { }
status: '1'
- weight: '10'
langcode: und
diff --git a/core/profiles/standard/config/filter.format.full_html.yml b/core/profiles/standard/config/filter.format.full_html.yml
index 6fb754c..26ff727 100644
--- a/core/profiles/standard/config/filter.format.full_html.yml
+++ b/core/profiles/standard/config/filter.format.full_html.yml
@@ -1,27 +1,18 @@
format: full_html
name: 'Full HTML'
-cache: '1'
status: '1'
weight: '1'
roles:
- administrator: administrator
- anonymous: '0'
- authenticated: '0'
+ - administrator
+cache: '1'
filters:
filter_url:
module: filter
- settings:
- filter_url_length: '72'
status: '1'
- weight: '0'
filter_autop:
module: filter
- settings: { }
status: '1'
- weight: '1'
filter_htmlcorrector:
module: filter
- settings: { }
status: '1'
- weight: '10'
langcode: und