First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

314
modules/path/path.admin.inc Normal file
View file

@ -0,0 +1,314 @@
<?php
/**
* @file
* Administrative page callbacks for the path module.
*/
/**
* Returns a listing of all defined URL aliases.
*
* When filter key passed, perform a standard search on the given key,
* and return the list of matching URL aliases.
*/
function path_admin_overview($keys = NULL) {
// Add the filter form above the overview table.
$build['path_admin_filter_form'] = drupal_get_form('path_admin_filter_form', $keys);
// Enable language column if locale is enabled or if we have any alias with language
$alias_exists = (bool) db_query_range('SELECT 1 FROM {url_alias} WHERE language <> :language', 0, 1, array(':language' => LANGUAGE_NONE))->fetchField();
$multilanguage = (module_exists('locale') || $alias_exists);
$header = array();
$header[] = array('data' => t('Alias'), 'field' => 'alias', 'sort' => 'asc');
$header[] = array('data' => t('System'), 'field' => 'source');
if ($multilanguage) {
$header[] = array('data' => t('Language'), 'field' => 'language');
}
$header[] = array('data' => t('Operations'));
$query = db_select('url_alias')->extend('PagerDefault')->extend('TableSort');
if ($keys) {
// Replace wildcards with PDO wildcards.
$query->condition('alias', '%' . preg_replace('!\*+!', '%', $keys) . '%', 'LIKE');
}
$result = $query
->fields('url_alias')
->orderByHeader($header)
->limit(50)
->execute();
$rows = array();
$destination = drupal_get_destination();
foreach ($result as $data) {
$row = array();
$row['data']['alias'] = l($data->alias, $data->source);
$row['data']['source'] = l($data->source, $data->source, array('alias' => TRUE));
if ($multilanguage) {
$row['data']['language'] = module_invoke('locale', 'language_name', $data->language);
}
$operations = array();
$operations['edit'] = array(
'title' => t('edit'),
'href' => "admin/config/search/path/edit/$data->pid",
'query' => $destination,
);
$operations['delete'] = array(
'title' => t('delete'),
'href' => "admin/config/search/path/delete/$data->pid",
'query' => $destination,
);
$row['data']['operations'] = array(
'data' => array(
'#theme' => 'links',
'#links' => $operations,
'#attributes' => array('class' => array('links', 'inline', 'nowrap')),
),
);
// If the system path maps to a different URL alias, highlight this table
// row to let the user know of old aliases.
if ($data->alias != drupal_get_path_alias($data->source, $data->language)) {
$row['class'] = array('warning');
}
$rows[] = $row;
}
$build['path_table'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => t('No URL aliases available. <a href="@link">Add URL alias</a>.', array('@link' => url('admin/config/search/path/add'))),
);
$build['path_pager'] = array('#theme' => 'pager');
return $build;
}
/**
* Page callback: Returns a form creating or editing a path alias.
*
* @param $path
* An array containing the path ID, source, alias, and language code.
*
* @return
* A form for adding or editing a URL alias.
*
* @see path_menu()
*/
function path_admin_edit($path = array()) {
if ($path) {
drupal_set_title($path['alias']);
$output = drupal_get_form('path_admin_form', $path);
}
else {
$output = drupal_get_form('path_admin_form');
}
return $output;
}
/**
* Form constructor for the path administration form.
*
* @param $path
* An array containing the path ID, source, alias, and language code.
*
* @ingroup forms
* @see path_admin_form_validate()
* @see path_admin_form_submit()
* @see path_admin_form_delete_submit()
*/
function path_admin_form($form, &$form_state, $path = array('source' => '', 'alias' => '', 'language' => LANGUAGE_NONE, 'pid' => NULL)) {
$form['source'] = array(
'#type' => 'textfield',
'#title' => t('Existing system path'),
'#default_value' => $path['source'],
'#maxlength' => 255,
'#size' => 45,
'#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1.'),
'#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
'#required' => TRUE,
);
$form['alias'] = array(
'#type' => 'textfield',
'#title' => t('Path alias'),
'#default_value' => $path['alias'],
'#maxlength' => 255,
'#size' => 45,
'#description' => t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
'#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
'#required' => TRUE,
);
// This will be a hidden value unless locale module is enabled.
$form['language'] = array(
'#type' => 'value',
'#value' => $path['language']
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
if ($path['pid']) {
$form['pid'] = array(
'#type' => 'hidden',
'#value' => $path['pid'],
);
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#submit' => array('path_admin_form_delete_submit'),
);
}
return $form;
}
/**
* Form submission handler for the 'Delete' button on path_admin_form().
*
* @see path_admin_form_validate()
* @see path_admin_form_submit()
*/
function path_admin_form_delete_submit($form, &$form_state) {
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
unset($_GET['destination']);
}
$form_state['redirect'] = array('admin/config/search/path/delete/' . $form_state['values']['pid'], array('query' => $destination));
}
/**
* Form validation handler for path_admin_form().
*
* @see path_admin_form_submit()
* @see path_admin_form_delete_submit()
*/
function path_admin_form_validate($form, &$form_state) {
$source = &$form_state['values']['source'];
$source = drupal_get_normal_path($source);
$alias = $form_state['values']['alias'];
$pid = isset($form_state['values']['pid']) ? $form_state['values']['pid'] : 0;
// Language is only set if locale module is enabled, otherwise save for all languages.
$language = isset($form_state['values']['language']) ? $form_state['values']['language'] : LANGUAGE_NONE;
$has_alias = db_query("SELECT COUNT(alias) FROM {url_alias} WHERE pid <> :pid AND alias = :alias AND language = :language", array(
':pid' => $pid,
':alias' => $alias,
':language' => $language,
))
->fetchField();
if ($has_alias) {
form_set_error('alias', t('The alias %alias is already in use in this language.', array('%alias' => $alias)));
}
if (!drupal_valid_path($source)) {
form_set_error('source', t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $source)));
}
}
/**
* Form submission handler for path_admin_form().
*
* @see path_admin_form_validate()
* @see path_admin_form_delete_submit()
*/
function path_admin_form_submit($form, &$form_state) {
// Remove unnecessary values.
form_state_values_clean($form_state);
path_save($form_state['values']);
drupal_set_message(t('The alias has been saved.'));
$form_state['redirect'] = 'admin/config/search/path';
}
/**
* Form constructor for the path deletion form.
*
* @param $path
* The path alias that will be deleted.
*
* @see path_admin_delete_confirm_submit()
*/
function path_admin_delete_confirm($form, &$form_state, $path) {
if (user_access('administer url aliases')) {
$form_state['path'] = $path;
return confirm_form(
$form,
t('Are you sure you want to delete path alias %title?',
array('%title' => $path['alias'])),
'admin/config/search/path'
);
}
return array();
}
/**
* Form submission handler for path_admin_delete_confirm().
*/
function path_admin_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
path_delete($form_state['path']['pid']);
$form_state['redirect'] = 'admin/config/search/path';
}
}
/**
* Form constructor for the path admin overview filter form.
*
* @ingroup forms
* @see path_admin_filter_form_submit_filter()
* @see path_admin_filter_form_submit_reset()
*/
function path_admin_filter_form($form, &$form_state, $keys = '') {
$form['#attributes'] = array('class' => array('search-form'));
$form['basic'] = array('#type' => 'fieldset',
'#title' => t('Filter aliases'),
'#attributes' => array('class' => array('container-inline')),
);
$form['basic']['filter'] = array(
'#type' => 'textfield',
'#title' => 'Path alias',
'#title_display' => 'invisible',
'#default_value' => $keys,
'#maxlength' => 128,
'#size' => 25,
);
$form['basic']['submit'] = array(
'#type' => 'submit',
'#value' => t('Filter'),
'#submit' => array('path_admin_filter_form_submit_filter'),
);
if ($keys) {
$form['basic']['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset'),
'#submit' => array('path_admin_filter_form_submit_reset'),
);
}
return $form;
}
/**
* Form submission handler for the path_admin_filter_form() Filter button.
*
* @see path_admin_filter_form_submit_reset()
*/
function path_admin_filter_form_submit_filter($form, &$form_state) {
$form_state['redirect'] = 'admin/config/search/path/list/' . trim($form_state['values']['filter']);
}
/**
* Form submission handler for the path_admin_filter_form() Reset button.
*
* @see path_admin_filter_form_submit_filter()
*/
function path_admin_filter_form_submit_reset($form, &$form_state) {
$form_state['redirect'] = 'admin/config/search/path/list';
}

73
modules/path/path.api.php Normal file
View file

@ -0,0 +1,73 @@
<?php
/**
* @file
* Hooks provided by the Path module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Respond to a path being inserted.
*
* @param $path
* An associative array containing the following keys:
* - source: The internal system path.
* - alias: The URL alias.
* - pid: Unique path alias identifier.
* - language: The language of the alias.
*
* @see path_save()
*/
function hook_path_insert($path) {
db_insert('mytable')
->fields(array(
'alias' => $path['alias'],
'pid' => $path['pid'],
))
->execute();
}
/**
* Respond to a path being updated.
*
* @param $path
* An associative array containing the following keys:
* - source: The internal system path.
* - alias: The URL alias.
* - pid: Unique path alias identifier.
* - language: The language of the alias.
*
* @see path_save()
*/
function hook_path_update($path) {
db_update('mytable')
->fields(array('alias' => $path['alias']))
->condition('pid', $path['pid'])
->execute();
}
/**
* Respond to a path being deleted.
*
* @param $path
* An associative array containing the following keys:
* - source: The internal system path.
* - alias: The URL alias.
* - pid: Unique path alias identifier.
* - language: The language of the alias.
*
* @see path_delete()
*/
function hook_path_delete($path) {
db_delete('mytable')
->condition('pid', $path['pid'])
->execute();
}
/**
* @} End of "addtogroup hooks".
*/

13
modules/path/path.info Normal file
View file

@ -0,0 +1,13 @@
name = Path
description = Allows users to rename URLs.
package = Core
version = VERSION
core = 7.x
files[] = path.test
configure = admin/config/search/path
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

21
modules/path/path.js Normal file
View file

@ -0,0 +1,21 @@
/**
* @file
* Attaches behaviors for the Path module.
*/
(function ($) {
Drupal.behaviors.pathFieldsetSummaries = {
attach: function (context) {
$('fieldset.path-form', context).drupalSetSummary(function (context) {
var path = $('.form-item-path-alias input').val();
return path ?
Drupal.t('Alias: @alias', { '@alias': path }) :
Drupal.t('No alias');
});
}
};
})(jQuery);

313
modules/path/path.module Normal file
View file

@ -0,0 +1,313 @@
<?php
/**
* @file
* Enables users to rename URLs.
*/
/**
* Implements hook_help().
*/
function path_help($path, $arg) {
switch ($path) {
case 'admin/help#path':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Path module allows you to specify an alias, or custom URL, for any existing internal system path. Aliases should not be confused with URL redirects, which allow you to forward a changed or inactive URL to a new URL. In addition to making URLs more readable, aliases also help search engines index content more effectively. Multiple aliases may be used for a single internal system path. To automate the aliasing of paths, you can install the contributed module <a href="@pathauto">Pathauto</a>. For more information, see the online handbook entry for the <a href="@path">Path module</a>.', array('@path' => 'http://drupal.org/documentation/modules/path', '@pathauto' => 'http://drupal.org/project/pathauto')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Creating aliases') . '</dt>';
$output .= '<dd>' . t('Users with sufficient <a href="@permissions">permissions</a> can create aliases under the <em>URL path settings</em> section when they create or edit content. Some examples of aliases are: ', array('@permissions' => url('admin/people/permissions', array('fragment' => 'module-path'))));
$output .= '<ul><li>' . t('<em>member/jane-smith</em> aliased to internal path <em>user/123</em>') . '</li>';
$output .= '<li>' . t('<em>about-us/team</em> aliased to internal path <em>node/456</em>') . '</li>';
$output .= '</ul></dd>';
$output .= '<dt>' . t('Managing aliases') . '</dt>';
$output .= '<dd>' . t('The Path module provides a way to search and view a <a href="@aliases">list of all aliases</a> that are in use on your website. Aliases can be added, edited and deleted through this list.', array('@aliases' => url('admin/config/search/path'))) . '</dd>';
$output .= '</dl>';
return $output;
case 'admin/config/search/path':
return '<p>' . t("An alias defines a different name for an existing URL path - for example, the alias 'about' for the URL path 'node/1'. A URL path can have multiple aliases.") . '</p>';
case 'admin/config/search/path/add':
return '<p>' . t('Enter the path you wish to create the alias for, followed by the name of the new alias.') . '</p>';
}
}
/**
* Implements hook_permission().
*/
function path_permission() {
return array(
'administer url aliases' => array(
'title' => t('Administer URL aliases'),
),
'create url aliases' => array(
'title' => t('Create and edit URL aliases'),
),
);
}
/**
* Implements hook_menu().
*/
function path_menu() {
$items['admin/config/search/path'] = array(
'title' => 'URL aliases',
'description' => "Change your site's URL paths by aliasing them.",
'page callback' => 'path_admin_overview',
'access arguments' => array('administer url aliases'),
'weight' => -5,
'file' => 'path.admin.inc',
);
$items['admin/config/search/path/edit/%path'] = array(
'title' => 'Edit alias',
'page callback' => 'path_admin_edit',
'page arguments' => array(5),
'access arguments' => array('administer url aliases'),
'file' => 'path.admin.inc',
);
$items['admin/config/search/path/delete/%path'] = array(
'title' => 'Delete alias',
'page callback' => 'drupal_get_form',
'page arguments' => array('path_admin_delete_confirm', 5),
'access arguments' => array('administer url aliases'),
'file' => 'path.admin.inc',
);
$items['admin/config/search/path/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/config/search/path/add'] = array(
'title' => 'Add alias',
'page callback' => 'path_admin_edit',
'access arguments' => array('administer url aliases'),
'type' => MENU_LOCAL_ACTION,
'file' => 'path.admin.inc',
);
return $items;
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for node_form().
*
* @see path_form_element_validate()
*/
function path_form_node_form_alter(&$form, $form_state) {
$path = array();
if (!empty($form['#node']->nid)) {
$conditions = array('source' => 'node/' . $form['#node']->nid);
$langcode = entity_language('node', $form['#node']);
if ($langcode != LANGUAGE_NONE) {
$conditions['language'] = $langcode;
}
$path = path_load($conditions);
if ($path === FALSE) {
$path = array();
}
}
$path += array(
'pid' => NULL,
'source' => isset($form['#node']->nid) ? 'node/' . $form['#node']->nid : NULL,
'alias' => '',
'language' => isset($langcode) ? $langcode : LANGUAGE_NONE,
);
$form['path'] = array(
'#type' => 'fieldset',
'#title' => t('URL path settings'),
'#collapsible' => TRUE,
'#collapsed' => empty($path['alias']),
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('path-form'),
),
'#attached' => array(
'js' => array(drupal_get_path('module', 'path') . '/path.js'),
),
'#access' => user_access('create url aliases') || user_access('administer url aliases'),
'#weight' => 30,
'#tree' => TRUE,
'#element_validate' => array('path_form_element_validate'),
);
$form['path']['alias'] = array(
'#type' => 'textfield',
'#title' => t('URL alias'),
'#default_value' => $path['alias'],
'#maxlength' => 255,
'#description' => t('Optionally specify an alternative URL by which this content can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
);
$form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
$form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
$form['path']['language'] = array('#type' => 'value', '#value' => $path['language']);
}
/**
* Form element validation handler for URL alias form element.
*
* @see path_form_node_form_alter()
*/
function path_form_element_validate($element, &$form_state, $complete_form) {
// Trim the submitted value.
$alias = trim($form_state['values']['path']['alias']);
if (!empty($alias)) {
form_set_value($element['alias'], $alias, $form_state);
// Node language (Locale module) needs special care. Since the language of
// the URL alias depends on the node language, and the node language can be
// switched right within the same form, we need to conditionally overload
// the originally assigned URL alias language.
// @todo Remove this after converting Path module to a field, and, after
// stopping Locale module from abusing the content language system.
if (isset($form_state['values']['language'])) {
form_set_value($element['language'], $form_state['values']['language'], $form_state);
}
$path = $form_state['values']['path'];
// Ensure that the submitted alias does not exist yet.
$query = db_select('url_alias')
->condition('alias', $path['alias'])
->condition('language', $path['language']);
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.'));
}
}
}
/**
* Implements hook_node_insert().
*/
function path_node_insert($node) {
if (isset($node->path) && isset($node->path['alias'])) {
$path = $node->path;
$path['alias'] = trim($path['alias']);
// Only save a non-empty alias.
if (!empty($path['alias'])) {
// Ensure fields for programmatic executions.
$langcode = entity_language('node', $node);
$path['source'] = 'node/' . $node->nid;
$path['language'] = isset($langcode) ? $langcode : LANGUAGE_NONE;
path_save($path);
}
}
}
/**
* Implements hook_node_update().
*/
function path_node_update($node) {
if (isset($node->path)) {
$path = $node->path;
$path['alias'] = isset($path['alias']) ? trim($path['alias']) : '';
// Delete old alias if user erased it.
if (!empty($path['pid']) && !$path['alias']) {
path_delete($path['pid']);
}
path_node_insert($node);
}
}
/**
* Implements hook_node_delete().
*/
function path_node_delete($node) {
// Delete all aliases associated with this node.
path_delete(array('source' => 'node/' . $node->nid));
}
/**
* Implements hook_form_FORM_ID_alter() for taxonomy_form_term().
*/
function path_form_taxonomy_form_term_alter(&$form, $form_state) {
// Make sure this does not show up on the delete confirmation form.
if (empty($form_state['confirm_delete'])) {
$langcode = entity_language('taxonomy_term', (object) $form['#term']);
$langcode = !empty($langcode) ? $langcode : LANGUAGE_NONE;
$conditions = array('source' => 'taxonomy/term/' . $form['#term']['tid'], 'language' => $langcode);
$path = (isset($form['#term']['tid']) ? path_load($conditions) : array());
if ($path === FALSE) {
$path = array();
}
$path += array(
'pid' => NULL,
'source' => isset($form['#term']['tid']) ? 'taxonomy/term/' . $form['#term']['tid'] : NULL,
'alias' => '',
'language' => $langcode,
);
$form['path'] = array(
'#access' => user_access('create url aliases') || user_access('administer url aliases'),
'#tree' => TRUE,
'#element_validate' => array('path_form_element_validate'),
);
$form['path']['alias'] = array(
'#type' => 'textfield',
'#title' => t('URL alias'),
'#default_value' => $path['alias'],
'#maxlength' => 255,
'#weight' => 0,
'#description' => t("Optionally specify an alternative URL by which this term can be accessed. Use a relative path and don't add a trailing slash or the URL alias won't work."),
);
$form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
$form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
$form['path']['language'] = array('#type' => 'value', '#value' => $path['language']);
}
}
/**
* Implements hook_taxonomy_term_insert().
*/
function path_taxonomy_term_insert($term) {
if (isset($term->path)) {
$path = $term->path;
$path['alias'] = trim($path['alias']);
// Only save a non-empty alias.
if (!empty($path['alias'])) {
// Ensure fields for programmatic executions.
$path['source'] = 'taxonomy/term/' . $term->tid;
// Core does not provide a way to store the term language but contrib
// modules can do it so we need to take this into account.
$langcode = entity_language('taxonomy_term', $term);
$path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE;
path_save($path);
}
}
}
/**
* Implements hook_taxonomy_term_update().
*/
function path_taxonomy_term_update($term) {
if (isset($term->path)) {
$path = $term->path;
$path['alias'] = trim($path['alias']);
// Delete old alias if user erased it.
if (!empty($path['pid']) && empty($path['alias'])) {
path_delete($path['pid']);
}
// Only save a non-empty alias.
if (!empty($path['alias'])) {
// Ensure fields for programmatic executions.
$path['source'] = 'taxonomy/term/' . $term->tid;
// Core does not provide a way to store the term language but contrib
// modules can do it so we need to take this into account.
$langcode = entity_language('taxonomy_term', $term);
$path['language'] = !empty($langcode) ? $langcode : LANGUAGE_NONE;
path_save($path);
}
}
}
/**
* Implements hook_taxonomy_term_delete().
*/
function path_taxonomy_term_delete($term) {
// Delete all aliases associated with this term.
path_delete(array('source' => 'taxonomy/term/' . $term->tid));
}

537
modules/path/path.test Normal file
View file

@ -0,0 +1,537 @@
<?php
/**
* @file
* Tests for the Path module.
*/
/**
* Provides a base class for testing the Path module.
*/
class PathTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Path alias functionality',
'description' => 'Add, edit, delete, and change alias and verify its consistency in the database.',
'group' => 'Path',
);
}
function setUp() {
parent::setUp('path');
// Create test user and login.
$web_user = $this->drupalCreateUser(array('create page content', 'edit own page content', 'administer url aliases', 'create url aliases'));
$this->drupalLogin($web_user);
}
/**
* Tests the path cache.
*/
function testPathCache() {
// Create test node.
$node1 = $this->drupalCreateNode();
// Create alias.
$edit = array();
$edit['source'] = 'node/' . $node1->nid;
$edit['alias'] = $this->randomName(8);
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
// Visit the system path for the node and confirm a cache entry is
// created.
cache_clear_all('*', 'cache_path', TRUE);
$this->drupalGet($edit['source']);
$this->assertTrue(cache_get($edit['source'], 'cache_path'), 'Cache entry was created.');
// Visit the alias for the node and confirm a cache entry is created.
cache_clear_all('*', 'cache_path', TRUE);
$this->drupalGet($edit['alias']);
$this->assertTrue(cache_get($edit['source'], 'cache_path'), 'Cache entry was created.');
}
/**
* Tests alias functionality through the admin interfaces.
*/
function testAdminAlias() {
// Create test node.
$node1 = $this->drupalCreateNode();
// Create alias.
$edit = array();
$edit['source'] = 'node/' . $node1->nid;
$edit['alias'] = $this->randomName(8);
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($edit['alias']);
$this->assertText($node1->title, 'Alias works.');
$this->assertResponse(200);
// Change alias to one containing "exotic" characters.
$pid = $this->getPID($edit['alias']);
$previous = $edit['alias'];
$edit['alias'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
"%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
"éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
$this->drupalPost('admin/config/search/path/edit/' . $pid, $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($edit['alias']);
$this->assertText($node1->title, 'Changed alias works.');
$this->assertResponse(200);
drupal_static_reset('drupal_lookup_path');
// Confirm that previous alias no longer works.
$this->drupalGet($previous);
$this->assertNoText($node1->title, 'Previous alias no longer works.');
$this->assertResponse(404);
// Create second test node.
$node2 = $this->drupalCreateNode();
// Set alias to second test node.
$edit['source'] = 'node/' . $node2->nid;
// leave $edit['alias'] the same
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
// Confirm no duplicate was created.
$this->assertRaw(t('The alias %alias is already in use in this language.', array('%alias' => $edit['alias'])), 'Attempt to move alias was rejected.');
// Delete alias.
$this->drupalPost('admin/config/search/path/edit/' . $pid, array(), t('Delete'));
$this->drupalPost(NULL, array(), t('Confirm'));
// Confirm that the alias no longer works.
$this->drupalGet($edit['alias']);
$this->assertNoText($node1->title, 'Alias was successfully deleted.');
$this->assertResponse(404);
}
/**
* Tests alias functionality through the node interfaces.
*/
function testNodeAlias() {
// Create test node.
$node1 = $this->drupalCreateNode();
// Create alias.
$edit = array();
$edit['path[alias]'] = $this->randomName(8);
$this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($edit['path[alias]']);
$this->assertText($node1->title, 'Alias works.');
$this->assertResponse(200);
// Change alias to one containing "exotic" characters.
$previous = $edit['path[alias]'];
$edit['path[alias]'] = "- ._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
"%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
"éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
$this->drupalPost('node/' . $node1->nid . '/edit', $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($edit['path[alias]']);
$this->assertText($node1->title, 'Changed alias works.');
$this->assertResponse(200);
// Make sure that previous alias no longer works.
$this->drupalGet($previous);
$this->assertNoText($node1->title, 'Previous alias no longer works.');
$this->assertResponse(404);
// Create second test node.
$node2 = $this->drupalCreateNode();
// Set alias to second test node.
// Leave $edit['path[alias]'] the same.
$this->drupalPost('node/' . $node2->nid . '/edit', $edit, t('Save'));
// Confirm that the alias didn't make a duplicate.
$this->assertText(t('The alias is already in use.'), 'Attempt to moved alias was rejected.');
// Delete alias.
$this->drupalPost('node/' . $node1->nid . '/edit', array('path[alias]' => ''), t('Save'));
// Confirm that the alias no longer works.
$this->drupalGet($edit['path[alias]']);
$this->assertNoText($node1->title, 'Alias was successfully deleted.');
$this->assertResponse(404);
}
/**
* Returns the path ID.
*
* @param $alias
* A string containing an aliased path.
*
* @return int
* Integer representing the path ID.
*/
function getPID($alias) {
return db_query("SELECT pid FROM {url_alias} WHERE alias = :alias", array(':alias' => $alias))->fetchField();
}
/**
* Tests that duplicate aliases fail validation.
*/
function testDuplicateNodeAlias() {
// Create one node with a random alias.
$node_one = $this->drupalCreateNode();
$edit = array();
$edit['path[alias]'] = $this->randomName();
$this->drupalPost('node/' . $node_one->nid . '/edit', $edit, t('Save'));
// Now create another node and try to set the same alias.
$node_two = $this->drupalCreateNode();
$this->drupalPost('node/' . $node_two->nid . '/edit', $edit, t('Save'));
$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 URL aliases for taxonomy terms.
*/
class PathTaxonomyTermTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Taxonomy term URL aliases',
'description' => 'Tests URL aliases for taxonomy terms.',
'group' => 'Path',
);
}
function setUp() {
parent::setUp('path', 'taxonomy');
// Create and login user.
$web_user = $this->drupalCreateUser(array('administer url aliases', 'administer taxonomy', 'access administration pages'));
$this->drupalLogin($web_user);
}
/**
* Tests alias functionality through the admin interfaces.
*/
function testTermAlias() {
// Create a term in the default 'Tags' vocabulary with URL alias.
$vocabulary = taxonomy_vocabulary_load(1);
$description = $this->randomName();;
$edit = array();
$edit['name'] = $this->randomName();
$edit['description[value]'] = $description;
$edit['path[alias]'] = $this->randomName();
$this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($edit['path[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();
$edit2 = array();
$edit2['path[alias]'] = $this->randomName();
$this->drupalPost('taxonomy/term/' . $tid . '/edit', $edit2, t('Save'));
// Confirm that the changed alias works.
$this->drupalGet($edit2['path[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($description, 'Old URL alias has been removed after altering.');
$this->assertResponse(404, 'Old URL alias returns 404.');
// Remove the term's URL alias.
$edit3 = array();
$edit3['path[alias]'] = '';
$this->drupalPost('taxonomy/term/' . $tid . '/edit', $edit3, t('Save'));
// Confirm that the alias no longer works.
$this->drupalGet($edit2['path[alias]']);
$this->assertNoText($description, 'Old URL alias has been removed after altering.');
$this->assertResponse(404, 'Old URL alias returns 404.');
}
}
/**
* Tests URL aliases for translated nodes.
*/
class PathLanguageTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Path aliases with translated nodes',
'description' => 'Confirm that paths work with translated nodes',
'group' => 'Path',
);
}
function setUp() {
parent::setUp('path', 'locale', 'translation');
// Create and login user.
$this->web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'translate content', 'access administration pages'));
$this->drupalLogin($this->web_user);
// Enable French language.
$edit = array();
$edit['langcode'] = 'fr';
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
// Enable URL language detection and selection.
$edit = array('language[enabled][locale-url]' => 1);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
}
/**
* Test alias functionality through the admin interfaces.
*/
function testAliasTranslation() {
// Set 'page' content type to enable translation.
variable_set('language_content_type_page', 2);
$english_node = $this->drupalCreateNode(array('type' => 'page'));
$english_alias = $this->randomName();
// Edit the node to set language and path.
$edit = array();
$edit['language'] = 'en';
$edit['path[alias]'] = $english_alias;
$this->drupalPost('node/' . $english_node->nid . '/edit', $edit, t('Save'));
// Confirm that the alias works.
$this->drupalGet($english_alias);
$this->assertText($english_node->title, 'Alias works.');
// Translate the node into French.
$this->drupalGet('node/' . $english_node->nid . '/translate');
$this->clickLink(t('add translation'));
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $this->randomName();
$edit["body[$langcode][0][value]"] = $this->randomName();
$french_alias = $this->randomName();
$edit['path[alias]'] = $french_alias;
$this->drupalPost(NULL, $edit, t('Save'));
// Clear the path lookup cache.
drupal_lookup_path('wipe');
// Ensure the node was created.
$french_node = $this->drupalGetNodeByTitle($edit["title"]);
$this->assertTrue(($french_node), 'Node found in database.');
// Confirm that the alias works.
$this->drupalGet('fr/' . $edit['path[alias]']);
$this->assertText($french_node->title, 'Alias for French translation works.');
// Confirm that the alias is returned by url().
drupal_static_reset('language_list');
drupal_static_reset('locale_url_outbound_alter');
$languages = language_list();
$url = url('node/' . $french_node->nid, array('language' => $languages[$french_node->language]));
$this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.');
// Confirm that the alias works even when changing language negotiation
// options. Enable User language detection and selection over URL one.
$edit = array(
'language[enabled][locale-user]' => 1,
'language[weight][locale-user]' => -9,
'language[enabled][locale-url]' => 1,
'language[weight][locale-url]' => -8,
);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
// Change user language preference.
$edit = array('language' => 'fr');
$this->drupalPost("user/{$this->web_user->uid}/edit", $edit, t('Save'));
// Check that the English alias works. In this situation French is the
// current UI and content language, while URL language is English (since we
// do not have a path prefix we fall back to the site's default language).
// We need to ensure that the user language preference is not taken into
// account while determining the path alias language, because if this
// happens we have no way to check that the path alias is valid: there is no
// path alias for French matching the english alias. So drupal_lookup_path()
// needs to use the URL language to check whether the alias is valid.
$this->drupalGet($english_alias);
$this->assertText($english_node->title, 'Alias for English translation works.');
// Check that the French alias works.
$this->drupalGet("fr/$french_alias");
$this->assertText($french_node->title, 'Alias for French translation works.');
// Disable URL language negotiation.
$edit = array('language[enabled][locale-url]' => FALSE);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
// Check that the English alias still works.
$this->drupalGet($english_alias);
$this->assertText($english_node->title, 'Alias for English translation works.');
// Check that the French alias is not available. We check the unprefixed
// alias because we disabled URL language negotiation above. In this
// situation only aliases in the default language and language neutral ones
// should keep working.
$this->drupalGet($french_alias);
$this->assertResponse(404, 'Alias for French translation is unavailable when URL language negotiation is disabled.');
// drupal_lookup_path() has an internal static cache. Check to see that
// it has the appropriate contents at this point.
drupal_lookup_path('wipe');
$french_node_path = drupal_lookup_path('source', $french_alias, $french_node->language);
$this->assertEqual($french_node_path, 'node/' . $french_node->nid, 'Normal path works.');
// Second call should return the same path.
$french_node_path = drupal_lookup_path('source', $french_alias, $french_node->language);
$this->assertEqual($french_node_path, 'node/' . $french_node->nid, 'Normal path is the same.');
// Confirm that the alias works.
$french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language);
$this->assertEqual($french_node_alias, $french_alias, 'Alias works.');
// Second call should return the same alias.
$french_node_alias = drupal_lookup_path('alias', 'node/' . $french_node->nid, $french_node->language);
$this->assertEqual($french_node_alias, $french_alias, 'Alias is the same.');
}
}
/**
* Tests the user interface for creating path aliases, with languages.
*/
class PathLanguageUITestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Path aliases with languages',
'description' => 'Confirm that the Path module user interface works with languages.',
'group' => 'Path',
);
}
function setUp() {
parent::setUp('path', 'locale');
// Create and login user.
$web_user = $this->drupalCreateUser(array('edit any page content', 'create page content', 'administer url aliases', 'create url aliases', 'administer languages', 'access administration pages'));
$this->drupalLogin($web_user);
// Enable French language.
$edit = array();
$edit['langcode'] = 'fr';
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
// Enable URL language detection and selection.
$edit = array('language[enabled][locale-url]' => 1);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
}
/**
* Tests that a language-neutral URL alias works.
*/
function testLanguageNeutralURLs() {
$name = $this->randomName(8);
$edit = array();
$edit['source'] = 'admin/config/search/path';
$edit['alias'] = $name;
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
$this->drupalGet($name);
$this->assertText(t('Filter aliases'), 'Language-neutral URL alias works');
}
/**
* Tests that a default language URL alias works.
*/
function testDefaultLanguageURLs() {
$name = $this->randomName(8);
$edit = array();
$edit['source'] = 'admin/config/search/path';
$edit['alias'] = $name;
$edit['language'] = 'en';
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
$this->drupalGet($name);
$this->assertText(t('Filter aliases'), 'English URL alias works');
}
/**
* Tests that a non-default language URL alias works.
*/
function testNonDefaultURLs() {
$name = $this->randomName(8);
$edit = array();
$edit['source'] = 'admin/config/search/path';
$edit['alias'] = $name;
$edit['language'] = 'fr';
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
$this->drupalGet('fr/' . $name);
$this->assertText(t('Filter aliases'), 'Foreign URL alias works');
}
}
/**
* Tests that paths are not prefixed on a monolingual site.
*/
class PathMonolingualTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => 'Paths on non-English monolingual sites',
'description' => 'Confirm that paths are not changed on monolingual non-English sites',
'group' => 'Path',
);
}
function setUp() {
global $language;
parent::setUp('path', 'locale', 'translation');
// Create and login user.
$web_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
$this->drupalLogin($web_user);
// Enable French language.
$edit = array();
$edit['langcode'] = 'fr';
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
// Make French the default language.
$edit = array('site_default' => 'fr');
$this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
// Disable English.
$edit = array('enabled[en]' => FALSE);
$this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
// Verify that French is the only language.
$this->assertFalse(drupal_multilingual(), 'Site is mono-lingual');
$this->assertEqual(language_default('language'), 'fr', 'French is the default language');
// Set language detection to URL.
$edit = array('language[enabled][locale-url]' => TRUE);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
// Force languages to be initialized.
drupal_language_initialize();
}
/**
* Verifies that links do not have language prefixes in them.
*/
function testPageLinks() {
// Navigate to 'admin/config' path.
$this->drupalGet('admin/config');
// Verify that links in this page do not have a 'fr/' prefix.
$this->assertNoLinkByHref('/fr/', 'Links do not contain language prefix');
// Verify that links in this page can be followed and work.
$this->clickLink(t('Languages'));
$this->assertResponse(200, 'Clicked link results in a valid page');
$this->assertText(t('Add language'), 'Page contains the add language text');
}
}