? DELETE ? MISC-FIELD-CHANGES-REAPPLY.patch ? Makefile ? article.macro ? d6-50-nodes.sql.gz ? d7-50-nodes-new.sql.gz ? d7-50-nodes.sql.gz ? head.kpf ? patches ? test.php ? modules/field/field.bulk.update.inc ? scripts/OLD-generate-autoload.pl ? scripts/generate-autoload.pl ? sites/all/cck ? sites/all/modules/admin_menu ? sites/all/modules/color ? sites/all/modules/combofield ? sites/all/modules/devel ? sites/all/modules/macro ? sites/all/modules/pbs ? sites/all/modules/pipedream ? sites/all/modules/taint ? sites/default/files ? sites/default/private ? sites/default/settings.php Index: modules/field/modules/text/text.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/modules/text/text.module,v retrieving revision 1.62 diff -u -F '^[fc]' -r1.62 text.module --- modules/field/modules/text/text.module 22 Aug 2010 12:55:04 -0000 1.62 +++ modules/field/modules/text/text.module 22 Aug 2010 16:31:33 -0000 @@ -660,9 +660,15 @@ function text_filter_format_update() { /** * Implements hook_filter_format_delete(). * + * When a text format is deleted, the Filter API remembers the old format's + * replacement format and uses the replacement when asked to render text in the + * old format. Thus, all we really need to do here is invalidate the field data + * cache. We could try to be more clever and only invalid selected cache + * elements, but input formats are not deleted that often. + * * @todo D8: Properly update filter format references in all fields. See * http://drupal.org/node/556022 for details. */ -function text_filter_format_delete() { +function text_filter_format_delete($format, $replacement) { field_cache_clear(); } Index: modules/filter/filter.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.api.php,v retrieving revision 1.20 diff -u -F '^[fc]' -r1.20 filter.api.php --- modules/filter/filter.api.php 26 Jun 2010 01:55:29 -0000 1.20 +++ modules/filter/filter.api.php 22 Aug 2010 16:31:33 -0000 @@ -238,20 +238,29 @@ function hook_filter_format_update($form * All modules storing references to text formats have to implement this hook. * * When a text format is deleted, all content that previously had that format - * assigned needs to be switched to the passed fallback format. + * assigned should to be switched to the passed replacement format. + * + * In some cases this is not possible to do immediately. Therefore, the Filter + * API maintains a history of all deleted formats and their corresponding + * replacement formats. When the Filter API is asked to operate on a deleted + * format, it instead uses the corresponding replacement format. This feature + * of Filter API may be deprecated someday, so modules should update their + * data if possible. + * + * @see filter_format_delete() * * @param $format * The format object of the format being deleted. - * @param $fallback + * @param $replacement * The format object of the format to use as replacement. * * @see hook_filter_format_insert() * @see hook_filter_format_update() */ -function hook_filter_format_delete($format, $fallback) { - // Replace the deleted format with the fallback format. +function hook_filter_format_delete($format, $replacement) { + // Replace the deleted format with the replacement format. db_update('my_module_table') - ->fields(array('format' => $fallback->format)) + ->fields(array('format' => $replacement->format)) ->condition('format', $format->format) ->execute(); } Index: modules/filter/filter.module =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.module,v retrieving revision 1.340 diff -u -F '^[fc]' -r1.340 filter.module --- modules/filter/filter.module 22 Aug 2010 12:55:04 -0000 1.340 +++ modules/filter/filter.module 22 Aug 2010 16:31:33 -0000 @@ -258,14 +258,31 @@ function filter_format_save(&$format) { /** * Delete a text format. * + * Modules that manage data associated with a text format should update their + * data to refer to the replacement text format by implementing + * hook_filter_format_delete(). + * + * In some cases this is not possible to do immediately. Therefore, the Filter + * API maintains a history of all deleted formats and their corresponding + * replacement formats. When the Filter API is asked to operate on a deleted + * format, it instead uses the corresponding replacement format. This feature + * of Filter API may be deprecated someday, so modules should update their + * data if possible. + * + * The history of deleted formats is available from + * filter_get_replacement_formats(). + * + * @todo: This method of handling deleted formats may not be + * ideal. See http://drupal.org/node/556022 for a lengthy discussion. + * * @param $format * The text format object to be deleted. - * @param $fallback_id + * @param $replacement_id * (optional) The ID of the text format to use to reassign content that is * currently using $format. If omitted, the currently stored * filter_fallback_format() is used. */ -function filter_format_delete($format, $fallback_id = NULL) { +function filter_format_delete($format, $replacement_id = NULL) { db_delete('filter_format') ->condition('format', $format->format) ->execute(); @@ -273,12 +290,17 @@ function filter_format_delete($format, $ ->condition('format', $format->format) ->execute(); - // Allow modules to react on text format deletion. - if (empty($fallback_id)) { - $fallback_id = filter_fallback_format(); + // Determine the replacement format. + if (empty($replacement_id)) { + $replacement_id = filter_fallback_format(); } - $fallback = filter_format_load($fallback_id); - module_invoke_all('filter_format_delete', $format, $fallback); + $replacement = filter_format_load($replacement_id); + + // Update the deleted-formats replacement list. + filter_format_set_replacement($format->format, $replacement->format); + + // Allow modules to react on text format deletion. + module_invoke_all('filter_format_delete', $format, $replacement); // Clear the filter cache whenever a text format is deleted. filter_formats_reset(); @@ -286,6 +308,52 @@ function filter_format_delete($format, $ } /** + * Set the replacement format for a deleted format. + * + * @param $format_id + * A format that is deleted. + * @param $replacement_id + * The replacement format for $format_id. + */ +function filter_format_set_replacement($format_id, $replacement_id) { + $deleted = variable_get('filter_deleted_formats', array()); + // Add the new entry. + $deleted[$format_id] = $replacement_id; + // Update any previously deleted format whose replacement is $format_id + // to fall back to $replacement_id. + foreach ($deleted as $original_id => $old_replacement_id) { + if ($old_replacement_id == $format_id) { + $deleted[$original_id] = $replacement_id; + } + } + variable_set('filter_deleted_formats', $deleted); +} + +/** + * Get a format's replacement format if it has been deleted. + * + * @param $format_id + * A format that is deleted. + * @return + * The replacement format for $format_id if $format_id has been deleted, or + * $format_id if it has not been deleted. + */ +function filter_format_get_replacement($format_id) { + $deleted = variable_get('filter_deleted_formats', array()); + return isset($deleted[$format_id]) ? $deleted[$format_id] : $format_id; +} + +/** + * Retrieve the mapping of deleted formats to their replacement format. + * + * @return + * An associative array mapping deleted format id to replacement format id. + */ +function filter_get_replacement_formats() { + return variable_get('filter_deleted_formats', array()); +} + +/** * Display a text format form title. */ function filter_admin_format_title($format) { @@ -615,7 +683,8 @@ function _filter_format_is_cacheable($fo * before performing actions with the filter. * * @param $format_id - * The format ID to retrieve filters for. + * The format ID to retrieve filters for. If the format has been deleted, + * the filters for the replacement format are automatically returned. * * @return * An array of filter objects associated to the given text format, keyed by @@ -632,6 +701,10 @@ function filter_list_format($format_id) } } + // If we're being asked about a format that was previously deleted, + // use its replacement format instead. + $format_id = filter_format_get_replacement($format_id); + if (!isset($filters[$format_id])) { $format_filters = array(); foreach ($filters['all'][$format_id] as $name => $filter) { @@ -682,6 +755,12 @@ function check_markup($text, $format_id if (empty($format_id)) { $format_id = filter_fallback_format(); } + else { + // If we're being asked to use a format that was previously + // deleted, load its replacement format instead. + $format_id = filter_format_get_replacement($format_id); + } + // If the requested text format does not exist, the text cannot be filtered. if (!$format = filter_format_load($format_id)) { watchdog('filter', 'Missing text format: %format.', array('%format' => $format_id), WATCHDOG_ALERT); @@ -824,6 +903,10 @@ function filter_process_format($element) if (empty($element['#format'])) { $element['#format'] = filter_default_format($user); } + else { + // If the format has been deleted, use its replacement format. + $element['#format'] = filter_format_get_replacement($element['#format']); + } $element['format']['format'] = array( '#type' => 'select', '#title' => t('Text format'), Index: modules/filter/filter.test =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v retrieving revision 1.72 diff -u -F '^[fc]' -r1.72 filter.test --- modules/filter/filter.test 22 Aug 2010 12:55:04 -0000 1.72 +++ modules/filter/filter.test 22 Aug 2010 16:31:33 -0000 @@ -68,6 +68,54 @@ class FilterCRUDTestCase extends DrupalW } /** + * Test that when a text format is deleted, calls to check_markup() + * using a deleted format use the correct replacement format. + */ + function testTextFormatReplacements() { + $plaintext_format = db_query_range('SELECT * FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'Plain text'))->fetchObject(); + $filtered_html_format = db_query_range('SELECT * FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'Filtered HTML'))->fetchObject(); + $full_html_format = db_query_range('SELECT * FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'Full HTML'))->fetchObject(); + $langcode = LANGUAGE_NONE; + + $this->web_user = $this->drupalCreateUser(array( + 'edit any article content', + filter_permission_name($filtered_html_format), + filter_permission_name($full_html_format), + )); + $this->drupalLogin($this->web_user); + + // Create a node using Full HTML. + $html_text = '
looks different under Full HTML, Filtered HTML, and default replacement format
'; + $node = array(); + $node['type'] = 'article'; + $node['uid'] = 1; + $node['body'][$langcode][0]['value'] = $html_text; + $node['body'][$langcode][0]['format'] = $full_html_format->format; + $node = $this->drupalCreateNode($node); + + // Verify Full HTML is used. + $this->drupalGet('node/' . $node->nid); + $this->assertRaw($html_text, 'Content is rendered in Full HTML.'); + $this->drupalGet('node/' . $node->nid . '/edit'); + $this->assertFieldByName("body[$langcode][0][format]", $full_html_format->format, 'Text format widget defaults to Full HTML.'); + + // Delete Full HTML, replacing it with Filtered HTML. + filter_format_delete($full_html_format, $filtered_html_format->format); + $this->drupalGet('node/' . $node->nid); + $this->assertNoRaw($html_text, 'Content is not rendered in Full HTML.'); + $this->assertRaw(strip_tags($html_text), 'Content was filtered with replacement format.'); + $this->drupalGet('node/' . $node->nid . '/edit'); + $this->assertFieldByName("body[$langcode][0][format]", $filtered_html_format->format, 'Text format widget defaults to Filtered HTML.'); + + // Delete Filtered HTML, verify default format is used. Note that the + // format selector is not rendered if there is only one format, so there + // is no parallel test for the format selector in this case. + filter_format_delete($filtered_html_format); + $this->drupalGet('node/' . $node->nid); + $this->assertRaw(check_markup($html_text, $plaintext_format->format), 'Content was rendered in Plain text.'); + } + + /** * Verify that a text format is properly stored. */ function verifyTextFormat($format) { @@ -682,6 +730,15 @@ class FilterSecurityTestCase extends Dru // Delete the text format entirely. $this->drupalPost('admin/config/content/formats/' . $format_id . '/delete', array(), t('Delete')); + // For this test we need content in a text format that the filter + // system knows nothing about. This means we have to explicitly + // remove it from deleted formats replacement list; otherwise, the + // next text will fail because the text will be formatted in the + // fallback format instead of as the empty string. + $deleted = variable_get('filter_deleted_formats', array()); + unset($deleted[$format_id]); + variable_set('filter_deleted_formats', $deleted); + // Verify that the content is empty, because the text format does not exist. $this->drupalGet('node/' . $node->nid); $this->assertNoText($body_raw, t('Node body not found.'));