Commit 47371be0 authored by webchick's avatar webchick

#414424 by sun, chx, Arancaytar, yched, et al: Introduce Form API #type...

#414424 by sun, chx, Arancaytar, yched, et al: Introduce Form API #type 'text_format' for additional DX/security around rich text fields.
parent c5bfbe7f
......@@ -5519,9 +5519,6 @@ function drupal_common_theme() {
'form_element_label' => array(
'render element' => 'element',
),
'text_format_wrapper' => array(
'render element' => 'element',
),
'vertical_tabs' => array(
'render element' => 'element',
),
......
......@@ -1435,7 +1435,15 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) {
}
}
}
form_set_value($element, $element['#value'], $form_state);
// Set the element's value in $form_state['values'], but only, if its key
// does not exist yet (a #value_callback may have already populated it).
$values = $form_state['values'];
foreach ($element['#parents'] as $key) {
$values = (isset($values[$key]) ? $values[$key] : NULL);
}
if (!isset($values)) {
form_set_value($element, $element['#value'], $form_state);
}
}
/**
......@@ -2205,110 +2213,6 @@ function form_process_radios($element) {
return $element;
}
/**
* Add text format selector to text elements with the #text_format property.
*
* The #text_format property should be the ID of an text format, found in
* {filter_format}.format, which gets passed to filter_form().
*
* If the property #text_format is set, the form element will be expanded into
* two separate form elements, one holding the content of the element, and the
* other holding the text format selector. The original element is shifted into
* a child element, but is otherwise unaltered, so that the format selector is
* at the same level as the text field which it affects.
*
* For example:
* @code
* // A simple textarea, such as a node body.
* $form['body'] = array(
* '#type' => 'textarea',
* '#title' => t('Body'),
* '#text_format' => isset($node->format) ? $node->format : filter_default_format(),
* );
* @endcode
*
* Becomes:
* @code
* $form['body'] = array(
* // Type switches to 'markup', as we're only interested in submitting the child elements.
* '#type' => 'markup',
* // 'value' holds the original element.
* 'value' => array(
* '#type' => 'textarea',
* '#title' => t('Body'),
* '#parents' => array('body'),
* ),
* // 'format' holds the text format selector.
* 'format' => array(
* '#parents' => array('body_format'),
* ...
* ),
* );
* @endcode
*
* And would result in:
* @code
* // Original, unaltered form element value.
* $form_state['values']['body'] = 'Example content';
* // Chosen text format.
* $form_state['values']['body_format'] = 1;
* @endcode
*
* @see system_element_info(), filter_form()
*/
function form_process_text_format($element) {
if (isset($element['#text_format'])) {
// Determine the form element parents and element name to use for the input
// format widget. This simulates the 'element' and 'element_format' pair of
// parents that filter_form() expects.
$element_parents = $element['#parents'];
$element_name = array_pop($element_parents);
$element_parents[] = $element_name . '_format';
// We need to break references, otherwise form_builder recurses infinitely.
$element['value'] = (array)$element;
$element['value']['#weight'] = 0;
unset($element['value']['#description']);
$element['#type'] = 'markup';
$element['#theme'] = NULL;
$element['#theme_wrappers'] = array('text_format_wrapper');
$element['format'] = filter_form($element['#text_format'], 1, $element_parents);
// We need to clear the #text_format from the new child otherwise we
// would get into an infinite loop.
unset($element['value']['#text_format']);
}
return $element;
}
/**
* Theme a text format form element.
*
* @param $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #children, #description
*
* @return
* A string representing the form element.
*
* @ingroup themeable
*/
function theme_text_format_wrapper($variables) {
$element = $variables['element'];
$output = '<div class="text-format-wrapper">' . "\n";
$output .= $element['#children'] . "\n";
if (!empty($element['#description'])) {
$output .= '<div class="description">' . $element['#description'] . "</div>\n";
}
$output .= "</div>\n";
return $output;
}
/**
* Theme a checkbox form element.
*
......
......@@ -58,23 +58,6 @@ Drupal.behaviors.formUpdated = {
}
};
/**
* Automatically display the guidelines of the selected text format.
*/
Drupal.behaviors.filterGuidelines = {
attach: function (context) {
$('.filter-guidelines', context).once('filter-guidelines')
.find('label').hide()
.parents('.filter-wrapper').find('select.filter-list')
.bind('change', function () {
$(this).parents('.filter-wrapper')
.find('.filter-guidelines-item').hide()
.siblings('#filter-guidelines-' + this.value).show();
})
.change();
}
};
/**
* Prepopulate form fields with information from the visitor cookie.
*/
......
......@@ -443,7 +443,7 @@ function block_add_block_form_submit($form, &$form_state) {
->fields(array(
'body' => $form_state['values']['body'],
'info' => $form_state['values']['info'],
'format' => $form_state['values']['body_format'],
'format' => $form_state['values']['format'],
))
->execute();
......
......@@ -420,10 +420,10 @@ function block_custom_block_form($edit = array()) {
);
$form['body_field']['#weight'] = -17;
$form['body_field']['body'] = array(
'#type' => 'textarea',
'#type' => 'text_format',
'#title' => t('Block body'),
'#default_value' => $edit['body'],
'#text_format' => isset($edit['format']) ? $edit['format'] : filter_default_format(),
'#format' => isset($edit['format']) ? $edit['format'] : NULL,
'#rows' => 15,
'#description' => t('The content of the block as shown to the user.'),
'#required' => TRUE,
......@@ -452,7 +452,7 @@ function block_custom_block_save($edit, $delta) {
->fields(array(
'body' => $edit['body'],
'info' => $edit['info'],
'format' => $edit['body_format'],
'format' => $edit['format'],
))
->condition('bid', $delta)
->execute();
......
......@@ -48,7 +48,7 @@ class BlockTestCase extends DrupalWebTestCase {
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $this->randomName(8);
$custom_block['body'] = $this->randomName(32);
$custom_block['body[value]'] = $this->randomName(32);
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
// Confirm that the custom block has been created, and then query the created bid.
......@@ -85,9 +85,9 @@ class BlockTestCase extends DrupalWebTestCase {
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $this->randomName(8);
$custom_block['body'] = '<h1>Full HTML</h1>';
$custom_block['body[value]'] = '<h1>Full HTML</h1>';
$full_html_format_id = db_query_range('SELECT format FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'Full HTML'))->fetchField();
$custom_block['body_format'] = $full_html_format_id;
$custom_block['body[format]'] = $full_html_format_id;
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
// Set the created custom block to a specific region.
......@@ -127,7 +127,7 @@ class BlockTestCase extends DrupalWebTestCase {
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $title;
$custom_block['body'] = $this->randomName(32);
$custom_block['body[value]'] = $this->randomName(32);
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $custom_block['info']))->fetchField();
......
......@@ -2051,7 +2051,6 @@ function comment_submit($comment) {
// 1) Filter it into HTML
// 2) Strip out all HTML tags
// 3) Convert entities back to plain-text.
$comment['subject'] = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment['comment_body'][LANGUAGE_NONE][0]['value'], $comment['comment_body'][LANGUAGE_NONE][0]['format'])))), 29, TRUE);
// Edge cases where the comment body is populated only by HTML tags will
// require a default subject.
......
......@@ -571,7 +571,7 @@ class CommentAnonymous extends CommentHelperCase {
$this->drupalGet('comment/reply/' . $this->node->nid);
$this->assertText('You are not authorized to view comments', t('Error attempting to post comment.'));
$this->assertNoFieldByName('subject', '', t('Subject field not found.'));
$this->assertNoFieldByName('comment', '', t('Comment field not found.'));
$this->assertNoFieldByName('comment[value]', '', t('Comment field not found.'));
user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array(
'access comments' => TRUE,
......
......@@ -512,10 +512,12 @@ function text_field_widget_settings_form($field, $instance) {
*/
function text_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) {
$element = $base;
$summary_widget = array();
$main_widget = array();
switch ($instance['widget']['type']) {
case 'text_textfield':
$element['value'] = $base + array(
$main_widget = $base + array(
'#type' => 'textfield',
'#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
'#size' => $instance['widget']['settings']['size'],
......@@ -526,7 +528,7 @@ function text_field_widget_form(&$form, &$form_state, $field, $instance, $langco
case 'text_textarea_with_summary':
$display = !empty($items[$delta]['summary']) || !empty($instance['settings']['display_summary']);
$element['summary'] = array(
$summary_widget = array(
'#type' => $display ? 'textarea' : 'value',
'#default_value' => isset($items[$delta]['summary']) ? $items[$delta]['summary'] : NULL,
'#title' => t('Summary'),
......@@ -543,7 +545,7 @@ function text_field_widget_form(&$form, &$form_state, $field, $instance, $langco
// Fall through to the next case.
case 'text_textarea':
$element['value'] = $base + array(
$main_widget = $base + array(
'#type' => 'textarea',
'#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
'#rows' => $instance['widget']['settings']['rows'],
......@@ -553,11 +555,20 @@ function text_field_widget_form(&$form, &$form_state, $field, $instance, $langco
break;
}
if ($instance['settings']['text_processing']) {
$element['value']['#text_format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : filter_default_format();
$element['#type'] = 'markup';
$element['#input'] = TRUE;
$element['#value_callback'] = 'text_field_widget_formatted_text_value';
if ($main_widget) {
// Conditionally alter the form element's type if text processing is enabled.
if ($instance['settings']['text_processing']) {
$element = $main_widget;
$element['#type'] = 'text_format';
$element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
$element['#base_type'] = $main_widget['#type'];
}
else {
$element['value'] = $main_widget;
}
}
if ($summary_widget) {
$element['summary'] = $summary_widget;
}
return $element;
......@@ -580,19 +591,3 @@ function text_field_widget_error($element, $error, $form, &$form_state) {
form_error($error_element, $error['message']);
}
/**
* Form element #value_callback to re-assign text format value for a formatted text widget.
*
* #text_format puts the format into 'value_format', while we need it in
* 'format'.
*/
function text_field_widget_formatted_text_value($element, $edit = FALSE, &$form_state) {
if ($edit !== FALSE) {
// The format selector uses #access = FALSE if only one format is
// available. In this case, we don't receive its value, and need to
// manually set it.
$edit['format'] = !empty($edit['value_format']) ? $edit['value_format'] : filter_default_format();
unset($edit['value_format']);
return $edit;
}
}
......@@ -166,7 +166,7 @@ class TextFieldTestCase extends DrupalWebTestCase {
// no format selector will be displayed.
$this->drupalGet('test-entity/add/test-bundle');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][value_format]", '', t('Format selector is not displayed'));
$this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is not displayed'));
// Submit with data that should be filtered.
$value = '<em>' . $this->randomName() . '</em>';
......@@ -202,11 +202,11 @@ class TextFieldTestCase extends DrupalWebTestCase {
// We should now have a 'text format' selector.
$this->drupalGet('test-entity/' . $id . '/edit');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', t('Widget is displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][value_format]", '', t('Format selector is displayed'));
$this->assertFieldByName("{$this->field_name}[$langcode][0][format]", '', t('Format selector is displayed'));
// Edit and change the text format to the new one that was created.
$edit = array(
"{$this->field_name}[$langcode][0][value_format]" => $format_id,
"{$this->field_name}[$langcode][0][format]" => $format_id,
);
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), t('Entity was updated'));
......
......@@ -1150,8 +1150,8 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase {
$langcode = LANGUAGE_NONE;
// Pretend the form has been built.
drupal_prepare_form('field_test_entity_form', $form, $form_state);
$form = form_builder('field_test_entity_form', $form, $form_state);
$form_state['values'] = array($this->field_name => array($langcode => $values));
drupal_process_form('field_test_entity_form', $form, $form_state);
$form_state['values'][$this->field_name][$langcode] = $values;
field_attach_submit($entity_type, $entity, $form, $form_state);
asort($weights);
......
......@@ -11,7 +11,6 @@
}
.filter-wrapper .form-item {
float: left;
margin: 0;
padding: 0 0 0.5em 1.5em;
}
.filter-wrapper .form-item label {
......
......@@ -56,6 +56,9 @@ function filter_theme() {
'variables' => array('tips' => NULL, 'long' => FALSE),
'file' => 'filter.pages.inc',
),
'text_format_wrapper' => array(
'render element' => 'element',
),
'filter_tips_more_info' => array(
'variables' => array(),
),
......@@ -65,6 +68,20 @@ function filter_theme() {
);
}
/**
* Implements hook_element_info().
*
* @see filter_process_format()
*/
function filter_element_info() {
$type['text_format'] = array(
'#process' => array('filter_process_format'),
'#base_type' => 'textarea',
'#theme_wrappers' => array('text_format_wrapper'),
);
return $type;
}
/**
* Implements hook_menu().
*/
......@@ -701,71 +718,194 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE)
}
/**
* Generates a selector for choosing a format in a form.
* Expands an element into a base element with text format selector attached.
*
* @param $selected_format
* The ID of the format that is currently selected; uses the default format
* for the current user if not provided.
* @param $weight
* The weight of the form element within the form.
* @param $parents
* The parents array of the element. Required when defining multiple text
* formats on a single form or having a different parent than 'format'.
* 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.
*
* @return
* Form API array for the form element.
* Since most modules expect the value of the new 'format' element *next* to the
* original element, filter_process_format() utilizes an #after_build to move
* the values of the children of the 'text_format' element so as to let the
* submitted form values appear as if they were located on the same level.
* For example, considering the input values:
* @code
* $form_state['input']['body']['value'] = 'foo';
* $form_state['input']['body']['format'] = 'foo';
* @endcode
* The #after_build will process them into:
* @code
* $form_state['values']['body'] = 'foo';
* $form_state['values']['format'] = 'foo';
* @endcode
*
* If multiple text format-enabled elements are required on the same level of
* the form structure, modules can set custom #parents on the original element.
* Alternatively, the #after_build may be unset through a subsequent #process
* callback. If no custom processing occurs, then the submitted form values will
* appear like in the $form_state['input'] array above.
*
* @ingroup forms
* @see filter_form_after_build()
*
* @param $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 0, NULL, or not
* set, the default format for the current user will be used.
*
* @return
* The expanded element.
*/
function filter_form($selected_format = NULL, $weight = NULL, $parents = array('format')) {
function filter_process_format($element) {
global $user;
// Use the default format for this user if none was selected.
if (empty($selected_format)) {
$selected_format = filter_default_format($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 as $key => $value) {
if ($key[0] === '#' && !in_array($key, $blacklist)) {
$element['value'][$key] = $value;
}
}
// Get a list of formats that the current user has access to.
$formats = filter_formats($user);
$element['value']['#type'] = $element['#base_type'];
$element['value'] += element_info($element['#base_type']);
// Turn original element into a text format wrapper.
$path = drupal_get_path('module', 'filter');
$element['#attached']['js'][] = $path . '/filter.js';
$element['#attached']['css'][] = $path . '/filter.css';
drupal_add_js('misc/form.js');
drupal_add_css(drupal_get_path('module', 'filter') . '/filter.css');
$element_id = drupal_html_id('edit-' . implode('-', $parents));
// Apply default #after_build behavior.
$element['#after_build'][] = 'filter_form_after_build';
$form = array(
// Setup child container for the text format widget.
$element['format'] = array(
'#type' => 'fieldset',
'#weight' => $weight,
'#attributes' => array('class' => array('filter-wrapper')),
);
$form['format_guidelines'] = array(
'#prefix' => '<div id="' . $element_id . '-guidelines" class="filter-guidelines">',
'#suffix' => '</div>',
'#weight' => 2,
// Prepare text format guidelines.
$element['format']['guidelines'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('filter-guidelines')),
'#weight' => 20,
);
// Get a list of formats that the current user has access to.
$formats = filter_formats($user);
foreach ($formats as $format) {
$options[$format->format] = $format->name;
$form['format_guidelines'][$format->format] = array(
'#markup' => theme('filter_guidelines', array('format' => $format)),
$element['format']['guidelines'][$format->format] = array(
'#theme' => 'filter_guidelines',
'#format' => $format,
);
}
$form['format'] = array(
// Use the default format for this user if none was selected.
if (empty($element['#format'])) {
$element['#format'] = filter_default_format($user);
}
$element['format']['format'] = array(
'#type' => 'select',
'#title' => t('Text format'),
'#options' => $options,
'#default_value' => $selected_format,
'#parents' => $parents,
'#default_value' => $element['#format'],
'#access' => count($formats) > 1,
'#id' => $element_id,
'#weight' => 10,
'#attributes' => array('class' => array('filter-list')),
'#parents' => array_merge($element['#parents'], array('format')),
'#value_callback' => 'filter_form_value_callback_format',
);
$form['format_help'] = array(
'#prefix' => '<div id="' . $element_id . '-help" class="filter-help">',
'#markup' => theme('filter_tips_more_info'),
'#suffix' => '</div>',
'#weight' => 1,
$element['format']['help'] = array(
'#type' => 'container',
'#theme' => 'filter_tips_more_info',
'#attributes' => array('class' => array('filter-help')),
'#weight' => 0,
);
return $form;
return $element;
}
/**
* After build, we need to move the values up in $form_state.
*/
function filter_form_after_build($element, &$form_state) {
$parents = $element['#parents'];
$original_key = array_pop($parents);
// For text fields, the additional subkeys map 1:1 to field schema columns.
if (isset($element['#columns'])) {
return $element;
}
foreach (element_children($element) as $key) {
$current_parents = $parents;
switch ($key) {
case 'value':
form_set_value(array('#parents' => $element['#parents']), $element[$key]['#value'], $form_state);
break;
case 'format':
$current_parents[] = $key;
form_set_value(array('#parents' => $current_parents), $element['format']['format']['#value'], $form_state);
break;
default:
$current_parents[] = $key;
form_set_value(array('#parents' => $current_parents), $element[$key]['#value'], $form_state);
}
}
return $element;
}
/**
* Render a text format-enabled form element.
*
* @param $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #children, #description
*
* @return
* A string representing the form element.
*
* @ingroup themeable
*/
function theme_text_format_wrapper($variables) {
$element = $variables['element'];
$output = '<div class="text-format-wrapper">';
$output .= $element['#children'];
if (!empty($element['#description'])) {
$output .= '<div class="description">' . $element['#description'] . '</div>';
}
$output .= "</div>\n";
return $output;
}
/**
......
......@@ -305,7 +305,7 @@ class FilterAdminTestCase extends DrupalWebTestCase {
$langcode = LANGUAGE_NONE;
$edit["title"] = $this->randomName();
$edit["body[$langcode][0][value]"] = $text;
$edit["body[$langcode][0][value_format]"] = $filtered;
$edit["body[$langcode][0][format]"] = $filtered;
$this->drupalPost('node/add/page', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $edit["title"])), t('Filtered node created.'));
......@@ -317,7 +317,7 @@ class FilterAdminTestCase extends DrupalWebTestCase {
// Use plain text and see if it escapes all tags, whether allowed or not.
$edit = array();
$edit["body[$langcode][0][value_format]"] = $plain;
$edit["body[$langcode][0][format]"] = $plain;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->drupalGet('node/' . $node->nid);
$this->assertText(check_plain($text), t('The "Plain text" text format escapes all HTML tags.'));
......@@ -1212,9 +1212,9 @@ class FilterHooksTestCase extends DrupalWebTestCase {
$custom_block = array();
$custom_block['info'] = $this->randomName(8);
$custom_block['title'] = $this->randomName(8);
$custom_block['body'] = $this->randomName(32);
$custom_block['body[value]'] = $this->randomName(32);
// Use the format created.
$custom_block['body_format'] = $format_id;
$custom_block['body[format]'] = $format_id;
$this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
$this->assertText(t('The block has been created.'), t('New block successfully created.'));
......
......@@ -190,15 +190,16 @@ class PathTaxonomyTermTestCase extends DrupalWebTestCase {
*/
function testTermAlias() {
// Create a term in the default 'Tags' vocabulary with URL alias.
$description = $this->randomName();;
$edit = array();
$edit['name'] = $this->randomName();
$edit['description'] = $this->randomName();
$edit['description[value]'] = $description;
$edit['path[alias]'] = $this->randomName();
$this->drupalPost('admin/structure/taxonomy/1/add', $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($edit['path[alias]']);
$this->assertText($edit['description'], 'Term can be accessed on URL alias.');
$this->assertText($description, 'Term can be accessed on URL alias.');
// Change the term's URL alias.
$tid = db_query("SELECT tid FROM {taxonomy_term_data} WHERE name = :name", array(':name' => $edit['name']))->fetchField();
......@@ -208,11 +209,11 @@ class PathTaxonomyTermTestCase extends DrupalWebTestCase {
// Confirm that the changed alias works.
$this->drupalGet($edit2['path[alias]']);
$this->assertText($edit['description'], 'Term can be accessed on changed URL alias.');
$this->assertText($description, 'Term can be accessed on changed URL alias.');
// Confirm that the old alias no longer works.
$this->drupalGet($edit['path[alias]']);
$this->assertNoText($edit['description'], 'Old URL alias has been removed after altering.');
$this->assertNoText($description, 'Old URL alias has been removed after altering.');
$this->assertResponse(404, 'Old URL alias returns 404.');
// Remove the term's URL alias.
......@@ -222,7 +223,7 @@ class PathTaxonomyTermTestCase extends DrupalWebTestCase {
// Confirm that the alias no longer works.
$this->drupalGet($edit2['path[alias]']);
$this->assertNoText($edit['description'], 'Old URL alias has been removed after altering.');
$this->assertNoText($description, 'Old URL alias has been removed after altering.');
$this->assertResponse(404, 'Old URL alias returns 404.');
}
}
......
......@@ -77,7 +77,7 @@ class PHPFilterTestCase extends PHPTestCase {
// Change filter to PHP filter and see that PHP code is evaluated.
$edit = array();
$langcode = LANGUAGE_NONE;