diff --git a/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php b/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php index dbd2f41..9e0066c 100644 --- a/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php +++ b/core/modules/path/lib/Drupal/path/Tests/PathAliasTest.php @@ -31,7 +31,7 @@ function setUp() { parent::setUp(); // Create test user and login. - $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'administer url aliases', 'create url aliases')); + $web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'administer url aliases', 'create url aliases', 'override url aliases')); $this->drupalLogin($web_user); } @@ -219,4 +219,25 @@ function testDuplicateNodeAlias() { $this->assertText(t('The alias is already in use.')); $this->assertFieldByXPath("//input[@name='path[alias]' and contains(@class, 'error')]", $edit['path[alias]'], 'Textfield exists and has the error class.'); } + + /** + * Tests that only valid aliases pass path_alias_is_override(). + */ + function testPathAliasIsOverride() { + $tests = array( + 'admin' => TRUE, + '/Admin/content/' => TRUE, + 'node/10000/edit' => TRUE, + 'User' => TRUE, + 'user/login' => TRUE, + 'users_are_people_too' => FALSE, + 'dries-sings-the-drupal-song' => FALSE, + ); + + foreach ($tests AS $alias => $expected) { + // Test the override functionality itself. + $result = path_alias_is_override($alias); + $this->assertIdentical($expected, $result); + } + } } diff --git a/core/modules/path/path.module b/core/modules/path/path.module index 03be41b..e9969bd 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -49,6 +49,9 @@ function path_permission() { 'create url aliases' => array( 'title' => t('Create and edit URL aliases'), ), + 'override url aliases' => array( + 'title' => t('Override system aliases'), + ), ); } @@ -163,24 +166,61 @@ function path_form_element_validate($element, &$form_state, $complete_form) { form_set_value($element['langcode'], $form_state['values']['langcode'], $form_state); } - $path = $form_state['values']['path']; + // If the alias has not changed since the last time it was changed, + // it should still be valid. + if ($element['alias']['#default_value'] != $alias) { + $path = $form_state['values']['path']; - // Ensure that the submitted alias does not exist yet. - $query = db_select('url_alias') - ->condition('alias', $path['alias']) - ->condition('langcode', $path['langcode']); - if (!empty($path['source'])) { - $query->condition('source', $path['source'], '<>'); - } - $query->addExpression('1'); - $query->range(0, 1); - if ($query->execute()->fetchField()) { - form_error($element, t('The alias is already in use.')); + // Ensure that the submitted alias does not exist yet. + $query = db_select('url_alias') + ->condition('alias', $path['alias']) + ->condition('langcode', $path['langcode']); + if (!empty($path['source'])) { + $query->condition('source', $path['source'], '<>'); + } + $query->addExpression('1'); + $query->range(0, 1); + if ($query->execute()->fetchField()) { + form_error($element, t('The alias is already in use.')); + } elseif (path_alias_is_override($path['alias']) && !user_access('override url aliases')) { + form_error($element, t('The alias overrides an existing URL.')); + } } } } /** + * Validates that a path alias is valid. + * + * @param $alias + * A string containing the alias in question. + * + * @return + * TRUE if the alias overrides an existing path, FALSE otherwise. + */ +function path_alias_is_override($alias) { + $alias = strtolower(trim($alias, '/ ')); + + // We could create an alias that would conflict with another node/user/taxonomy + // later on, so we need to take account of that. (e.g. Create node 7 + // with the alias 'node/9/delete'. It will make it impossible to delete + // node 9 later on without modifying node 7 first.) + $parts = explode('/', $alias, 2); + $restricted_paths = array('node', 'taxonomy', 'user'); + if (in_array($parts[0], $restricted_paths)) { + return TRUE; + } + + // Verify that no other module has reserved this address + $router_item = menu_get_item($alias); + if (!empty($router_item)) { + return TRUE; + } + + return FALSE; +} + +/** * Implements hook_node_insert(). */ function path_node_insert(EntityInterface $node) {