Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redirect module to core. #1317

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 23 additions & 1 deletion core/includes/database/select.inc
Expand Up @@ -22,12 +22,21 @@ interface QueryExtendableInterface {
/**
* Enhance this object by wrapping it in an extender object.
*
* The core implementations of QueryExtendableInterface include:
* - TableSort: Extend a query to sort based on query strings.
* - PagerDefault: Extend a query to limit results based on query strings.
* - SearchQuery: Extend a query to join on the search index tables.
*
* Other implementations may be added by other modules, but for the sake of
* code validation, these core extenders are specified as possible return
* values of this interface.
*
* @param $extender_name
* The base name of the extending class. The base name will be checked
* against the current database connection to allow driver-specific subclasses
* as well, using the same logic as the query objects themselves. For example,
* PagerDefault_mysql is the MySQL-specific override for PagerDefault.
* @return QueryExtendableInterface
* @return QueryExtendableInterface|SelectQueryInterface|TableSort|PagerDefault|SearchQuery
* The extender object, which now contains a reference to this object.
*/
public function extend($extender_name);
Expand Down Expand Up @@ -1291,6 +1300,19 @@ class SelectQuery extends Query implements SelectQueryInterface {
return $alias;
}

/**
* Add columns to be retrieved from a database table.
*
* @param $table_alias
* The table alias used when specifying a table to be added to the query,
* either through SelectQueryInterface::select(),
* SelectQueryInterface::join(), or other joining methods.
* @param array $fields
* An array of column names to be selected from this table.
*
* @return SelectQueryInterface
* The current select object.
*/
public function fields($table_alias, array $fields = array()) {

if ($fields) {
Expand Down
12 changes: 5 additions & 7 deletions core/includes/path.inc
Expand Up @@ -460,14 +460,12 @@ function path_delete($criteria) {
if (!is_array($criteria)) {
$criteria = array('pid' => $criteria);
}
$path = path_load($criteria);
$query = db_delete('url_alias');
foreach ($criteria as $field => $value) {
$query->condition($field, $value);
// Load and then delete each path until there are no more aliases left.
while ($path = path_load($criteria)) {
module_invoke_all('path_delete', $path);
backdrop_clear_path_cache($path['source']);
db_delete('url_alias')->condition('pid', $path['pid'])->execute();
}
$query->execute();
module_invoke_all('path_delete', $path);
backdrop_clear_path_cache($path['source']);
}

/**
Expand Down
7 changes: 7 additions & 0 deletions core/misc/tableresponsive.js
Expand Up @@ -66,6 +66,13 @@ $.extend(TableResponsive.prototype, {
eventhandlerEvaluateColumnVisibility: function (e) {
var pegged = parseInt(this.$link.data('pegged'), 10);
var hiddenLength = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden').length;

// If the table is not at all visible, do not manipulate the link.
var tableVisible = this.$table.is(':visible');
if (!tableVisible) {
return;
}

// If the table has hidden columns, associate an action link with the table
// to show the columns.
if (hiddenLength > 0) {
Expand Down
1 change: 1 addition & 0 deletions core/modules/node/node.module
Expand Up @@ -127,6 +127,7 @@ function node_entity_info() {
'base table' => 'node',
'revision table' => 'node_revision',
'fieldable' => TRUE,
'redirect support' => TRUE,
'entity keys' => array(
'id' => 'nid',
'revision' => 'vid',
Expand Down
6 changes: 6 additions & 0 deletions core/modules/node/tests/node.test
Expand Up @@ -2516,6 +2516,7 @@ class NodeEntityViewModeAlterTest extends NodeWebTestCase {
* Tests the cache invalidation of node operations.
*/
class NodePageCacheTest extends NodeWebTestCase {
protected $profile = 'minimal';

/**
* An admin user with administrative permissions for nodes.
Expand All @@ -2528,6 +2529,11 @@ class NodePageCacheTest extends NodeWebTestCase {
config_set('system.core', 'cache', 1);
config_set('system.core', 'page_cache_maximum_age', 300);

$this->backdropCreateContentType(array(
'type' => 'page',
'name' => 'Page',
));

$this->admin_user = $this->backdropCreateUser(array(
'bypass node access',
'access content overview',
Expand Down
19 changes: 15 additions & 4 deletions core/modules/path/path.admin.inc
Expand Up @@ -180,8 +180,14 @@ function path_admin_form($form, &$form_state, $path = array('source' => '', 'ali
'#type' => 'submit',
'#value' => t('Delete'),
'#submit' => array('path_admin_form_delete_submit'),
'#limit_validation_errors' => array(array('actions'), array('pid')),
);
}
$form['actions']['cancel'] = array(
'#type' => 'link',
'#title' => t('Cancel'),
'#href' => isset($_GET['destination']) ? $_GET['destination'] : 'admin/config/search/path',
);

return $form;
}
Expand Down Expand Up @@ -500,21 +506,26 @@ function path_patterns_settings_form($form) {
'#description' => t('Maximum text length of any component in the alias (e.g., [title]). 100 is the recommended length. @max is the maximum possible length.', array('@max' => $max_length)),
);

$description = t('What should be done when updating an existing content item which already has an alias?');
$description = t('The action taken when an item already has an alias.');
$delete_label = t('Create a new alias. Delete the old alias.');
if (module_exists('redirect')) {
$auto_redirect = config_get('redirect.settings', 'auto_redirect');
$description .= ' ' . t('The <a href="!url">Redirect module settings</a> affect whether a redirect is created when an alias is deleted.', array('!url' => url('admin/config/search/redirect/settings')));
if ($auto_redirect) {
$delete_label = t('Create a new alias. Replace the old alias with a redirect.');
}
}
else {
$description .= ' ' . t('Considering installing the <a href="!url">Redirect module</a> to get redirects when your aliases change.', array('!url' => 'https://github.com/backdrop-contrib/redirect'));
$description .= ' ' . t('Considering enabling the Redirect module on the <a href="!url">modules page</a> to create redirects when your aliases change.', array('admin/modules'));
}
$form['update_action'] = array(
'#type' => 'radios',
'#title' => t('Update action'),
'#default_value' => $config->get('update_action'),
'#options' => array(
PATH_UPDATE_ACTION_NO_NEW => t('Do nothing. Leave the old alias intact.'),
PATH_UPDATE_ACTION_DELETE => $delete_label,
PATH_UPDATE_ACTION_LEAVE => t('Create a new alias. Leave the existing alias functioning.'),
PATH_UPDATE_ACTION_DELETE => t('Create a new alias. Delete the old alias.'),
PATH_UPDATE_ACTION_NO_NEW => t('Do nothing. Leave the old alias intact.'),
),
'#description' => $description,
);
Expand Down
5 changes: 5 additions & 0 deletions core/modules/path/path.module
Expand Up @@ -789,6 +789,11 @@ function path_form_element(Entity $entity) {
'#type' => 'value',
'#value' => $path['source'],
);
$fieldset['original'] = array(
'#type' => 'value',
'#value' => path_load($path['pid'])
);

$fieldset['langcode'] = array(
'#type' => 'value',
'#value' => $path['langcode'],
Expand Down
107 changes: 95 additions & 12 deletions core/modules/path/tests/path.test
Expand Up @@ -8,11 +8,19 @@
* Provides a base class for testing the Path module.
*/
class PathTestCase extends BackdropWebTestCase {
function setUp() {
parent::setUp('path');
protected $profile = 'minimal';

function setUp($modules = array()) {
$modules[] = 'path';
parent::setUp($modules);

$this->backdropCreateContentType(array(
'type' => 'page',
'name' => 'Page',
));

// Create test user and login.
$web_user = $this->backdropCreateUser(array('create page content', 'edit own page content', 'create post content', 'edit own post content', 'administer url aliases', 'create url aliases'));
$web_user = $this->backdropCreateUser(array('access content', 'create page content', 'edit own page content', 'administer url aliases', 'create url aliases'));
$this->backdropLogin($web_user);
}

Expand Down Expand Up @@ -107,9 +115,7 @@ class PathTestCase extends BackdropWebTestCase {
*/
function testNodeAlias() {
// Create test node.
$node1 = $this->backdropCreateNode(array(
'type' => 'post',
));
$node1 = $this->backdropCreateNode();

// Create alias.
$edit = array();
Expand Down Expand Up @@ -178,7 +184,6 @@ class PathTestCase extends BackdropWebTestCase {
$node_one = $this->backdropCreateNode();
$edit = array();
$edit['path[alias]'] = $this->randomName();
$edit['path[auto]'] = FALSE;
$this->backdropPost('node/' . $node_one->nid . '/edit', $edit, t('Save'));

// Now create another node and try to set the same alias.
Expand All @@ -196,6 +201,11 @@ class PathTaxonomyTermTestCase extends BackdropWebTestCase {
function setUp() {
parent::setUp('path', 'taxonomy');

// Redirect module is not tested with aliases in these tests. Redirect does
// its own testing in RedirectFunctionalTest.
module_disable(array('redirect'));
menu_rebuild();

// Create and login user.
$web_user = $this->backdropCreateUser(array('administer url aliases', 'administer taxonomy', 'access administration pages'));
$this->backdropLogin($web_user);
Expand Down Expand Up @@ -251,11 +261,29 @@ class PathTaxonomyTermTestCase extends BackdropWebTestCase {
* Tests URL aliases for translated nodes.
*/
class PathLanguageTestCase extends BackdropWebTestCase {
function setUp() {
parent::setUp('path', 'locale', 'translation');
protected $profile = 'testing';

/**
* The user account of the logged in user for these tests.
*
* @var User
*/
protected $web_user;

function setUp($modules = array()) {
$modules[] = 'node';
$modules[] = 'path';
$modules[] = 'locale';
$modules[] = 'translation';
parent::setUp($modules);

$this->backdropCreateContentType(array(
'type' => 'page',
'name' => 'Page',
));

// Create and login user.
$this->web_user = $this->backdropCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages'));
$this->web_user = $this->backdropCreateUser(array('access content', 'edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages'));
$this->backdropLogin($this->web_user);

// Enable French language.
Expand Down Expand Up @@ -284,7 +312,6 @@ class PathLanguageTestCase extends BackdropWebTestCase {
// Edit the node to set language and path.
$edit = array();
$edit['langcode'] = 'en';
$edit['path[auto]'] = FALSE;
$edit['path[alias]'] = $english_alias;
$this->backdropPost('node/' . $english_node->nid . '/edit', $edit, t('Save'));

Expand All @@ -300,7 +327,6 @@ class PathLanguageTestCase extends BackdropWebTestCase {
$edit["title"] = $this->randomName();
$edit["body[$langcode][0][value]"] = $this->randomName();
$french_alias = $this->randomName();
$edit['path[auto]'] = FALSE;
$edit['path[alias]'] = $french_alias;
$this->backdropPost(NULL, $edit, t('Save'));

Expand Down Expand Up @@ -518,3 +544,60 @@ class PathMonolingualTestCase extends BackdropWebTestCase {
$this->assertText(t('Add language'), 'Page contains the add language text');
}
}

/**
* Tests that path hooks are invoked.
*/
class PathHooksTestCase extends BackdropWebTestCase {
protected $profile = 'testing';

function setUp() {
parent::setUp(array('node', 'path', 'path_test'));
$this->backdropCreateContentType(array(
'type' => 'page',
'name' => 'Page',
));
$web_user = $this->backdropCreateUser(array('access content', 'create page content', 'delete own page content', 'administer url aliases', 'create url aliases'));
$this->backdropLogin($web_user);
}

function testPathHooks() {
// Create test node.
$node1 = $this->backdropCreateNode();

// Generate two test aliases.
$alias1 = $this->randomName(8);
$alias2 = $this->randomName(8);

// Insert aliases and test that hook_path_insert() is called.
$edit = array();
$edit['source'] = 'node/' . $node1->nid;
$edit['alias'] = $alias1;
$this->backdropPost('admin/config/search/path/add', $edit, t('Save URL alias'));
$this->assertRaw('path_test_path_insert(): ' . $edit['alias'] . ' => ' . $edit['source'], t('hook_path_insert() was called.'));

$edit['alias'] = $alias2;
$this->backdropPost('admin/config/search/path/add', $edit, t('Save URL alias'));

// Extract the path ID from the second path alias.
$inserted_path = state_get('path_test_inserted_path', array());
$pid2 = $inserted_path['pid'];

// Update the second path alias and test that hook_path_update() is called.
$edit['alias'] = $alias2 = $this->randomName(8);
$this->backdropPost('admin/config/search/path/edit/' . $pid2, $edit, t('Save URL alias'));
$this->assertRaw('path_test_path_update(): ' . $edit['alias'] . ' => ' . $edit['source'], t('hook_path_update() was called.'));

// Delete the node and test that hook_path_delete() is called once for each
// path alias.
$this->backdropPost('node/' . $node1->nid . '/delete', array(), t('Delete'));
$this->assertRaw('path_test_path_delete(): ' . $alias1 . ' => node/' . $node1->nid, t('hook_path_delete() was called for the first path alias.'));
$this->assertRaw('path_test_path_delete(): ' . $alias2 . ' => node/' . $node1->nid, t('hook_path_delete() was called for the second path alias.'));

// Test that hook_path_delete() is not called if the node has no path
// aliases.
$node2 = $this->backdropCreateNode();
$this->backdropPost('node/' . $node2->nid . '/delete', array(), t('Delete'));
$this->assertNoRaw('path_test_path_delete()', t('hook_path_delete() was not called.'));
}
}
6 changes: 6 additions & 0 deletions core/modules/path/tests/path.tests.info
Expand Up @@ -28,6 +28,12 @@ description = Confirm that paths are not changed on monolingual non-English site
group = Path
file = path.test

[PathHooksTestCase]
name = Path hooks
description = Test the hooks invoked when a path is inserted, updated, or deleted.
group = Path
file = path.test

[PathPatternUnitTestCase]
name = Path pattern unit tests
description = Unit tests for Path automatic alias functions.
Expand Down