Issue #1269742 by katbailey, beejeebus, pwolanin, g.oechsler, ksenzee: Make path lookup code into an pluggable class.
parent
daf490d3a4
commit
e3d863834c
|
@ -2490,6 +2490,10 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) {
|
|||
->register('keyvalue.database', 'Drupal\Core\KeyValueStore\KeyValueDatabaseFactory')
|
||||
->addArgument(new Reference('database'));
|
||||
|
||||
$container->register('path.alias_manager', 'Drupal\Core\Path\AliasManager')
|
||||
->addArgument(new Reference('database'))
|
||||
->addArgument(new Reference('keyvalue.database'));
|
||||
|
||||
// Register the EntityManager.
|
||||
$container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager');
|
||||
}
|
||||
|
|
|
@ -2208,7 +2208,7 @@ function url($path = NULL, array $options = array()) {
|
|||
}
|
||||
elseif (!empty($path) && !$options['alias']) {
|
||||
$langcode = isset($options['language']) && isset($options['language']->langcode) ? $options['language']->langcode : '';
|
||||
$alias = drupal_get_path_alias($original_path, $langcode);
|
||||
$alias = drupal_container()->get('path.alias_manager')->getPathAlias($original_path, $langcode);
|
||||
if ($alias != $original_path) {
|
||||
$path = $alias;
|
||||
}
|
||||
|
@ -4922,9 +4922,6 @@ function _drupal_bootstrap_full($skip = FALSE) {
|
|||
// current_path().
|
||||
drupal_language_initialize();
|
||||
|
||||
// Initialize current_path() prior to invoking hook_init().
|
||||
drupal_path_initialize();
|
||||
|
||||
// Let all modules take action before the menu system handles the request.
|
||||
// We do not want this while running update.php.
|
||||
if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
|
||||
|
|
|
@ -2994,7 +2994,7 @@ function _menu_delete_item($item, $force = FALSE) {
|
|||
* @param $item
|
||||
* An associative array representing a menu link item, with elements:
|
||||
* - link_path: (required) The path of the menu item, which should be
|
||||
* normalized first by calling drupal_get_normal_path() on it.
|
||||
* normalized first by calling drupal_container()->get('path.alias_manager')->getSystemPath() on it.
|
||||
* - link_title: (required) Title to appear in menu for the link.
|
||||
* - menu_name: (optional) The machine name of the menu for the link.
|
||||
* Defaults to 'tools'.
|
||||
|
|
|
@ -2,296 +2,13 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Functions to handle paths in Drupal, including path aliasing.
|
||||
* Functions to handle paths in Drupal.
|
||||
*
|
||||
* These functions are not loaded for cached pages, but modules that need
|
||||
* to use them in hook_boot() or hook exit() can make them available, by
|
||||
* executing "drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initializes the current path to the proper normal path.
|
||||
*/
|
||||
function drupal_path_initialize() {
|
||||
// At this point, the current path is either the request path (due to
|
||||
// drupal_environment_initialize()) or some modified version of it due to
|
||||
// other bootstrap code (e.g., language negotiation), but it has not yet been
|
||||
// normalized by drupal_get_normal_path().
|
||||
$path = _current_path();
|
||||
|
||||
// If on the front page, resolve to the front page path, including for calls
|
||||
// to current_path() while drupal_get_normal_path() is in progress.
|
||||
if (empty($path)) {
|
||||
$path = config('system.site')->get('page.front');
|
||||
_current_path($path);
|
||||
}
|
||||
|
||||
// Normalize the path.
|
||||
_current_path(drupal_get_normal_path($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an alias, return its Drupal system URL if one exists. Given a Drupal
|
||||
* system URL return one of its aliases if such a one exists. Otherwise,
|
||||
* return FALSE.
|
||||
*
|
||||
* @param $action
|
||||
* One of the following values:
|
||||
* - wipe: delete the alias cache.
|
||||
* - alias: return an alias for a given Drupal system path (if one exists).
|
||||
* - source: return the Drupal system URL for a path alias (if one exists).
|
||||
* @param $path
|
||||
* The path to investigate for corresponding aliases or system URLs.
|
||||
* @param $langcode
|
||||
* Optional language code to search the path with. Defaults to the page language.
|
||||
* If there's no path defined for that language it will search paths without
|
||||
* language.
|
||||
*
|
||||
* @return
|
||||
* Either a Drupal system path, an aliased path, or FALSE if no path was
|
||||
* found.
|
||||
*/
|
||||
function drupal_lookup_path($action, $path = '', $langcode = NULL) {
|
||||
// Use the advanced drupal_static() pattern, since this is called very often.
|
||||
static $drupal_static_fast;
|
||||
if (!isset($drupal_static_fast)) {
|
||||
$drupal_static_fast['cache'] = &drupal_static(__FUNCTION__);
|
||||
}
|
||||
$cache = &$drupal_static_fast['cache'];
|
||||
|
||||
if (!isset($cache)) {
|
||||
$cache = array(
|
||||
'map' => array(),
|
||||
'no_source' => array(),
|
||||
'whitelist' => NULL,
|
||||
'system_paths' => array(),
|
||||
'no_aliases' => array(),
|
||||
'first_call' => TRUE,
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve the path alias whitelist.
|
||||
if (!isset($cache['whitelist'])) {
|
||||
$cache['whitelist'] = state()->get('system.path_alias_whitelist', NULL);
|
||||
if (!isset($cache['whitelist'])) {
|
||||
$cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
// If no language is explicitly specified we default to the current URL
|
||||
// language. If we used a language different from the one conveyed by the
|
||||
// requested URL, we might end up being unable to check if there is a path
|
||||
// alias matching the URL path.
|
||||
$langcode = $langcode ? $langcode : language(LANGUAGE_TYPE_URL)->langcode;
|
||||
|
||||
if ($action == 'wipe') {
|
||||
$cache = array();
|
||||
$cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
|
||||
}
|
||||
elseif ($cache['whitelist'] && $path != '') {
|
||||
if ($action == 'alias') {
|
||||
// During the first call to drupal_lookup_path() per language, load the
|
||||
// expected system paths for the page from cache.
|
||||
if (!empty($cache['first_call'])) {
|
||||
$cache['first_call'] = FALSE;
|
||||
|
||||
$cache['map'][$langcode] = array();
|
||||
// Load system paths from cache.
|
||||
$cid = current_path();
|
||||
if ($cached = cache('path')->get($cid)) {
|
||||
$cache['system_paths'] = $cached->data;
|
||||
// Now fetch the aliases corresponding to these system paths.
|
||||
$args = array(
|
||||
':system' => $cache['system_paths'],
|
||||
':langcode' => $langcode,
|
||||
':langcode_undetermined' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
// Always get the language-specific alias before the language-neutral
|
||||
// one. For example 'de' is less than 'und' so the order needs to be
|
||||
// ASC, while 'xx-lolspeak' is more than 'und' so the order needs to
|
||||
// be DESC. We also order by pid ASC so that fetchAllKeyed() returns
|
||||
// the most recently created alias for each source. Subsequent queries
|
||||
// using fetchField() must use pid DESC to have the same effect.
|
||||
// For performance reasons, the query builder is not used here.
|
||||
if ($langcode == LANGUAGE_NOT_SPECIFIED) {
|
||||
// Prevent PDO from complaining about a token the query doesn't use.
|
||||
unset($args[':langcode']);
|
||||
$result = db_query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode = :langcode_undetermined ORDER BY pid ASC', $args);
|
||||
}
|
||||
elseif ($langcode < LANGUAGE_NOT_SPECIFIED) {
|
||||
$result = db_query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid ASC', $args);
|
||||
}
|
||||
else {
|
||||
$result = db_query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid ASC', $args);
|
||||
}
|
||||
$cache['map'][$langcode] = $result->fetchAllKeyed();
|
||||
// Keep a record of paths with no alias to avoid querying twice.
|
||||
$cache['no_aliases'][$langcode] = array_flip(array_diff_key($cache['system_paths'], array_keys($cache['map'][$langcode])));
|
||||
}
|
||||
}
|
||||
// If the alias has already been loaded, return it.
|
||||
if (isset($cache['map'][$langcode][$path])) {
|
||||
return $cache['map'][$langcode][$path];
|
||||
}
|
||||
// Check the path whitelist, if the top_level part before the first /
|
||||
// is not in the list, then there is no need to do anything further,
|
||||
// it is not in the database.
|
||||
elseif (!isset($cache['whitelist'][strtok($path, '/')])) {
|
||||
return FALSE;
|
||||
}
|
||||
// For system paths which were not cached, query aliases individually.
|
||||
elseif (!isset($cache['no_aliases'][$langcode][$path])) {
|
||||
$args = array(
|
||||
':source' => $path,
|
||||
':langcode' => $langcode,
|
||||
':langcode_undetermined' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
// See the queries above.
|
||||
if ($langcode == LANGUAGE_NOT_SPECIFIED) {
|
||||
unset($args[':langcode']);
|
||||
$alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode = :langcode_undetermined ORDER BY pid DESC", $args)->fetchField();
|
||||
}
|
||||
elseif ($langcode > LANGUAGE_NOT_SPECIFIED) {
|
||||
$alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args)->fetchField();
|
||||
}
|
||||
else {
|
||||
$alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args)->fetchField();
|
||||
}
|
||||
$cache['map'][$langcode][$path] = $alias;
|
||||
return $alias;
|
||||
}
|
||||
}
|
||||
// Check $no_source for this $path in case we've already determined that there
|
||||
// isn't a path that has this alias
|
||||
elseif ($action == 'source' && !isset($cache['no_source'][$langcode][$path])) {
|
||||
// Look for the value $path within the cached $map
|
||||
$source = FALSE;
|
||||
if (!isset($cache['map'][$langcode]) || !($source = array_search($path, $cache['map'][$langcode]))) {
|
||||
$args = array(
|
||||
':alias' => $path,
|
||||
':langcode' => $langcode,
|
||||
':langcode_undetermined' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
// See the queries above.
|
||||
if ($langcode == LANGUAGE_NOT_SPECIFIED) {
|
||||
unset($args[':langcode']);
|
||||
$result = db_query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode = :langcode_undetermined ORDER BY pid DESC", $args);
|
||||
}
|
||||
elseif ($langcode > LANGUAGE_NOT_SPECIFIED) {
|
||||
$result = db_query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args);
|
||||
}
|
||||
else {
|
||||
$result = db_query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args);
|
||||
}
|
||||
if ($source = $result->fetchField()) {
|
||||
$cache['map'][$langcode][$source] = $path;
|
||||
}
|
||||
else {
|
||||
// We can't record anything into $map because we do not have a valid
|
||||
// index and there is no need because we have not learned anything
|
||||
// about any Drupal path. Thus cache to $no_source.
|
||||
$cache['no_source'][$langcode][$path] = TRUE;
|
||||
}
|
||||
}
|
||||
return $source;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache system paths for a page.
|
||||
*
|
||||
* Cache an array of the system paths available on each page. We assume
|
||||
* that aliases will be needed for the majority of these paths during
|
||||
* subsequent requests, and load them in a single query during
|
||||
* drupal_lookup_path().
|
||||
*/
|
||||
function drupal_cache_system_paths() {
|
||||
// Check if the system paths for this page were loaded from cache in this
|
||||
// request to avoid writing to cache on every request.
|
||||
$cache = &drupal_static('drupal_lookup_path', array());
|
||||
if (empty($cache['system_paths']) && !empty($cache['map'])) {
|
||||
|
||||
// @todo Because we are not within the request scope at this time, we cannot
|
||||
// use current_path(), which would give us the system path to use as the
|
||||
// key. Instead we call _current_path(), which may give us the alias
|
||||
// instead. However, at lookup time the system path will be used as the
|
||||
// key, because it uses current_path(), and so it will be a cache miss.
|
||||
// There is a critical issue for fixing the path alias logic here:
|
||||
// http://drupal.org/node/1269742
|
||||
|
||||
// Generate a cache ID (cid) specifically for this page.
|
||||
$cid = _current_path();
|
||||
// The static $map array used by drupal_lookup_path() includes all
|
||||
// system paths for the page request.
|
||||
if ($paths = current($cache['map'])) {
|
||||
$data = array_keys($paths);
|
||||
$expire = REQUEST_TIME + (60 * 60 * 24);
|
||||
cache('path')->set($cid, $data, $expire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an internal Drupal path, return the alias set by the administrator.
|
||||
*
|
||||
* If no path is provided, the function will return the alias of the current
|
||||
* page.
|
||||
*
|
||||
* @param $path
|
||||
* An internal Drupal path.
|
||||
* @param $langcode
|
||||
* An optional language code to look up the path in.
|
||||
*
|
||||
* @return
|
||||
* An aliased path if one was found, or the original path if no alias was
|
||||
* found.
|
||||
*/
|
||||
function drupal_get_path_alias($path = NULL, $langcode = NULL) {
|
||||
// If no path is specified, use the current page's path.
|
||||
if ($path == NULL) {
|
||||
$path = current_path();
|
||||
}
|
||||
$result = $path;
|
||||
if ($alias = drupal_lookup_path('alias', $path, $langcode)) {
|
||||
$result = $alias;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path alias, return the internal path it represents.
|
||||
*
|
||||
* @param $path
|
||||
* A Drupal path alias.
|
||||
* @param $langcode
|
||||
* An optional language code to look up the path in.
|
||||
*
|
||||
* @return
|
||||
* The internal path represented by the alias, or the original alias if no
|
||||
* internal path was found.
|
||||
*/
|
||||
function drupal_get_normal_path($path, $langcode = NULL) {
|
||||
$original_path = $path;
|
||||
|
||||
// Lookup the path alias first.
|
||||
if ($source = drupal_lookup_path('source', $path, $langcode)) {
|
||||
$path = $source;
|
||||
}
|
||||
|
||||
// Allow other modules to alter the inbound URL. We cannot use drupal_alter()
|
||||
// here because we need to run hook_url_inbound_alter() in the reverse order
|
||||
// of hook_url_outbound_alter().
|
||||
foreach (array_reverse(module_implements('url_inbound_alter')) as $module) {
|
||||
$function = $module . '_url_inbound_alter';
|
||||
$function($path, $original_path, $langcode);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current page is the front page.
|
||||
*
|
||||
|
@ -378,36 +95,6 @@ function current_path() {
|
|||
return _current_path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the path alias white list.
|
||||
*
|
||||
* @param $source
|
||||
* An optional system path for which an alias is being inserted.
|
||||
*
|
||||
* @return
|
||||
* An array containing a white list of path aliases.
|
||||
*/
|
||||
function drupal_path_alias_whitelist_rebuild($source = NULL) {
|
||||
// When paths are inserted, only rebuild the whitelist if the system path
|
||||
// has a top level component which is not already in the whitelist.
|
||||
if (!empty($source)) {
|
||||
$whitelist = state()->get('system.path_alias_whitelist', NULL);
|
||||
if (isset($whitelist[strtok($source, '/')])) {
|
||||
return $whitelist;
|
||||
}
|
||||
}
|
||||
// For each alias in the database, get the top level component of the system
|
||||
// path it corresponds to. This is the portion of the path before the first
|
||||
// '/', if present, otherwise the whole path itself.
|
||||
$whitelist = array();
|
||||
$result = db_query("SELECT DISTINCT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias}");
|
||||
foreach ($result as $row) {
|
||||
$whitelist[$row->path] = TRUE;
|
||||
}
|
||||
state()->set('system.path_alias_whitelist', $whitelist);
|
||||
return $whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a specific URL alias from the database.
|
||||
*
|
||||
|
@ -415,13 +102,7 @@ function drupal_path_alias_whitelist_rebuild($source = NULL) {
|
|||
* A string representing the source, a number representing the pid, or an
|
||||
* array of query conditions.
|
||||
*
|
||||
* @return
|
||||
* FALSE if no alias was found or an associative array containing the
|
||||
* following keys:
|
||||
* - source: The internal system path.
|
||||
* - alias: The URL alias.
|
||||
* - pid: Unique path alias identifier.
|
||||
* - langcode: The language code of the alias.
|
||||
* @see \Drupal\Core\Path\Path::load()
|
||||
*/
|
||||
function path_load($conditions) {
|
||||
if (is_numeric($conditions)) {
|
||||
|
@ -433,68 +114,7 @@ function path_load($conditions) {
|
|||
elseif (!is_array($conditions)) {
|
||||
return FALSE;
|
||||
}
|
||||
$select = db_select('url_alias');
|
||||
foreach ($conditions as $field => $value) {
|
||||
$select->condition($field, $value);
|
||||
}
|
||||
return $select
|
||||
->fields('url_alias')
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a path alias to the database.
|
||||
*
|
||||
* @param $path
|
||||
* An associative array containing the following keys:
|
||||
* - source: The internal system path.
|
||||
* - alias: The URL alias.
|
||||
* - pid: (optional) Unique path alias identifier.
|
||||
* - langcode: (optional) The language code of the alias.
|
||||
*/
|
||||
function path_save(&$path) {
|
||||
$path += array('langcode' => LANGUAGE_NOT_SPECIFIED);
|
||||
|
||||
// Load the stored alias, if any.
|
||||
if (!empty($path['pid']) && !isset($path['original'])) {
|
||||
$path['original'] = path_load($path['pid']);
|
||||
}
|
||||
|
||||
if (empty($path['pid'])) {
|
||||
drupal_write_record('url_alias', $path);
|
||||
module_invoke_all('path_insert', $path);
|
||||
}
|
||||
else {
|
||||
drupal_write_record('url_alias', $path, array('pid'));
|
||||
module_invoke_all('path_update', $path);
|
||||
}
|
||||
|
||||
// Clear internal properties.
|
||||
unset($path['original']);
|
||||
|
||||
// Clear the static alias cache.
|
||||
drupal_clear_path_cache($path['source']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a URL alias.
|
||||
*
|
||||
* @param $criteria
|
||||
* A number representing the pid or an array of criteria.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
$query->execute();
|
||||
module_invoke_all('path_delete', $path);
|
||||
drupal_clear_path_cache($path['source']);
|
||||
return drupal_container()->get('path.crud')->load($conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -597,14 +217,3 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) {
|
|||
return $item && $item['access'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the path cache.
|
||||
*
|
||||
* @param $source
|
||||
* An optional system path for which an alias is being changed.
|
||||
*/
|
||||
function drupal_clear_path_cache($source = NULL) {
|
||||
// Clear the drupal_lookup_path() static cache.
|
||||
drupal_static_reset('drupal_lookup_path');
|
||||
drupal_path_alias_whitelist_rebuild($source);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\CacheDecorator\AliasManagerCacheDecorator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\CacheDecorator;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Path\AliasManagerInterface;
|
||||
|
||||
/**
|
||||
* Class used by the PathSubscriber to get the system path and cache path lookups.
|
||||
*/
|
||||
class AliasManagerCacheDecorator implements CacheDecoratorInterface, AliasManagerInterface {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Path\AliasManagerInterface
|
||||
*/
|
||||
protected $aliasManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface;
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Stack of request paths for use as cids when caching system paths.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cacheKeys = array();
|
||||
|
||||
/**
|
||||
* Holds an array of previously cached paths based on a request path.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $preloadedPathLookups = array();
|
||||
|
||||
/**
|
||||
* Whether the cache needs to be written.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $cacheNeedsWriting = TRUE;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\Core\CacheDecorator\AliasManagerCacheDecorator.
|
||||
*/
|
||||
public function __construct(AliasManagerInterface $alias_manager, CacheBackendInterface $cache) {
|
||||
$this->aliasManager = $alias_manager;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\CacheDecorator\CacheDecoratorInterface::setCacheKey().
|
||||
*/
|
||||
public function setCacheKey($key) {
|
||||
$this->cacheKeys[] = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\CacheDecorator\CacheDecoratorInterface::writeCache().
|
||||
*
|
||||
* Cache an array of the system paths available on each page. We assume
|
||||
* that aliases will be needed for the majority of these paths during
|
||||
* subsequent requests, and load them in a single query during path alias
|
||||
* lookup.
|
||||
*/
|
||||
public function writeCache() {
|
||||
$path_lookups = $this->getPathLookups();
|
||||
// Check if the system paths for this page were loaded from cache in this
|
||||
// request to avoid writing to cache on every request.
|
||||
if ($this->cacheNeedsWriting && !empty($path_lookups) && !empty($this->cacheKeys)) {
|
||||
// Use the system path of the current request for the cache ID (cid).
|
||||
$cid = end($this->cacheKeys);
|
||||
// Set the path cache to expire in 24 hours.
|
||||
$expire = REQUEST_TIME + (60 * 60 * 24);
|
||||
$this->cache->set($cid, $path_lookups, $expire);
|
||||
}
|
||||
// We are at the end of the request, so pop off the last request path.
|
||||
array_pop($this->cacheKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::getSystemPath().
|
||||
*/
|
||||
public function getSystemPath($path, $path_language = NULL) {
|
||||
$system_path = $this->aliasManager->getSystemPath($path, $path_language);
|
||||
// We need to pass on the list of previously cached system paths for this
|
||||
// key to the alias manager for use in subsequent lookups.
|
||||
$cached = $this->cache->get($system_path);
|
||||
$cached_paths = array();
|
||||
if ($cached) {
|
||||
$cached_paths = $cached->data;
|
||||
$this->cacheNeedsWriting = FALSE;
|
||||
}
|
||||
$this->preloadPathLookups($cached_paths);
|
||||
return $system_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::getPathAlias().
|
||||
*/
|
||||
public function getPathAlias($path, $path_language = NULL) {
|
||||
return $this->aliasManager->getPathAlias($path, $path_language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::getPathLookups().
|
||||
*/
|
||||
public function getPathLookups() {
|
||||
return $this->aliasManager->getPathLookups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::preloadPathLookups().
|
||||
*/
|
||||
public function preloadPathLookups(array $path_list) {
|
||||
$this->aliasManager->preloadPathLookups($path_list);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\CacheDecorator\CacheDecoratorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\CacheDecorator;
|
||||
|
||||
/**
|
||||
* Defines an interface for cache decorator implementations.
|
||||
*/
|
||||
interface CacheDecoratorInterface {
|
||||
|
||||
/**
|
||||
* Specify the key to use when writing the cache.
|
||||
*/
|
||||
public function setCacheKey($key);
|
||||
|
||||
/**
|
||||
* Write the cache.
|
||||
*/
|
||||
public function writeCache();
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterNestedMatchersPass;
|
|||
use Drupal\Core\DependencyInjection\Compiler\RegisterSerializationClassesPass;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Scope;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
@ -82,6 +83,20 @@ class CoreBundle extends Bundle
|
|||
$container->register('nested_matcher', 'Drupal\Core\Routing\NestedMatcher')
|
||||
->addTag('chained_matcher', array('priority' => 5));
|
||||
|
||||
$container
|
||||
->register('cache.path', 'Drupal\Core\Cache\CacheBackendInterface')
|
||||
->setFactoryClass('Drupal\Core\Cache\CacheFactory')
|
||||
->setFactoryMethod('get')
|
||||
->addArgument('path');
|
||||
|
||||
$container->register('path.alias_manager.cached', 'Drupal\Core\CacheDecorator\AliasManagerCacheDecorator')
|
||||
->addArgument(new Reference('path.alias_manager'))
|
||||
->addArgument(new Reference('cache.path'));
|
||||
|
||||
$container->register('path.crud', 'Drupal\Core\Path\Path')
|
||||
->addArgument(new Reference('database'))
|
||||
->addArgument(new Reference('path.alias_manager'));
|
||||
|
||||
// The following services are tagged as 'nested_matcher' services and are
|
||||
// processed in the RegisterNestedMatchersPass compiler pass. Each one
|
||||
// needs to be set on the matcher using a different method, so we use a
|
||||
|
@ -110,6 +125,7 @@ class CoreBundle extends Bundle
|
|||
$container->register('maintenance_mode_subscriber', 'Drupal\Core\EventSubscriber\MaintenanceModeSubscriber')
|
||||
->addTag('event_subscriber');
|
||||
$container->register('path_subscriber', 'Drupal\Core\EventSubscriber\PathSubscriber')
|
||||
->addArgument(new Reference('path.alias_manager.cached'))
|
||||
->addTag('event_subscriber');
|
||||
$container->register('legacy_request_subscriber', 'Drupal\Core\EventSubscriber\LegacyRequestSubscriber')
|
||||
->addTag('event_subscriber');
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
namespace Drupal\Core\EventSubscriber;
|
||||
|
||||
use Drupal\Core\CacheDecorator\AliasManagerCacheDecorator;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
|
@ -16,23 +18,31 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|||
*/
|
||||
class PathSubscriber extends PathListenerBase implements EventSubscriberInterface {
|
||||
|
||||
protected $aliasManager;
|
||||
|
||||
public function __construct(AliasManagerCacheDecorator $alias_manager) {
|
||||
$this->aliasManager = $alias_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the system path.
|
||||
*
|
||||
* @todo The path system should be objectified to remove the function calls in
|
||||
* this method.
|
||||
*
|
||||
* @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function onKernelRequestPathResolve(GetResponseEvent $event) {
|
||||
$request = $event->getRequest();
|
||||
|
||||
$path = $this->extractPath($request);
|
||||
|
||||
$path = drupal_get_normal_path($path);
|
||||
|
||||
$path = $this->aliasManager->getSystemPath($path);
|
||||
$this->setPath($request, $path);
|
||||
$this->aliasManager->setCacheKey($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures system paths for the request get cached.
|
||||
*/
|
||||
public function onKernelTerminate(PostResponseEvent $event) {
|
||||
$this->aliasManager->writeCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,6 +126,7 @@ class PathSubscriber extends PathListenerBase implements EventSubscriberInterfac
|
|||
$events[KernelEvents::REQUEST][] = array('onKernelRequestLanguageResolve', 150);
|
||||
$events[KernelEvents::REQUEST][] = array('onKernelRequestFrontPageResolve', 101);
|
||||
$events[KernelEvents::REQUEST][] = array('onKernelRequestPathResolve', 100);
|
||||
$events[KernelEvents::TERMINATE][] = array('onKernelTerminate', 200);
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ class RequestCloseSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
public function onTerminate(PostResponseEvent $event) {
|
||||
module_invoke_all('exit');
|
||||
drupal_cache_system_paths();
|
||||
module_implements_write_cache();
|
||||
system_run_automated_cron();
|
||||
}
|
||||
|
@ -41,7 +40,7 @@ class RequestCloseSubscriber implements EventSubscriberInterface {
|
|||
* An array of event listener definitions.
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[KernelEvents::TERMINATE][] = array('onTerminate');
|
||||
$events[KernelEvents::TERMINATE][] = array('onTerminate', 100);
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ class ExceptionController extends ContainerAware {
|
|||
$system_path = $request->attributes->get('system_path');
|
||||
watchdog('access denied', $system_path, array(), WATCHDOG_WARNING);
|
||||
|
||||
$path = drupal_get_normal_path(config('system.site')->get('page.403'));
|
||||
$path = $this->container->get('path.alias_manager')->getSystemPath(config('system.site')->get('page.403'));
|
||||
if ($path && $path != $system_path) {
|
||||
// Keep old path for reference, and to allow forms to redirect to it.
|
||||
if (!isset($_GET['destination'])) {
|
||||
|
@ -173,7 +173,7 @@ class ExceptionController extends ContainerAware {
|
|||
$_GET['destination'] = $system_path;
|
||||
}
|
||||
|
||||
$path = drupal_get_normal_path(config('system.site')->get('page.404'));
|
||||
$path = $this->container->get('path.alias_manager')->getSystemPath(config('system.site')->get('page.404'));
|
||||
if ($path && $path != $system_path) {
|
||||
// @todo Um, how do I specify an override URL again? Totally not clear. Do
|
||||
// that and sub-call the kernel rather than using meah().
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\Path\AliasManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Path;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\KeyValueStore\KeyValueDatabaseFactory;
|
||||
|
||||
class AliasManager implements AliasManagerInterface {
|
||||
|
||||
/**
|
||||
* The database connectino to use for path lookups.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The Key/Value Store to use for state
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\DatabaseStorage
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The default langcode to use when none is specified for path lookups.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $langcode;
|
||||
|
||||
/**
|
||||
* Holds the map of path lookups per language.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $lookupMap = array();
|
||||
|
||||
/**
|
||||
* Holds an array of path alias for which no source was found.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $noSource = array();
|
||||
|
||||
/**
|
||||
* Holds the array of whitelisted path aliases.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $whitelist;
|
||||
|
||||
/**
|
||||
* Holds an array of system paths that have no aliases.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $noAliases = array();
|
||||
|
||||
/**
|
||||
* Whether lookupPath() has not yet been called.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $firstLookup = TRUE;
|
||||
|
||||
/**
|
||||
* Holds an array of previously looked up paths for the current request path.
|
||||
*
|
||||
* This will only ever get populated if the alias manager is being used in
|
||||
* the context of a request.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $preloadedPathLookups = array();
|
||||
|
||||
public function __construct(Connection $connection, KeyValueDatabaseFactory $keyvalue) {
|
||||
$this->connection = $connection;
|
||||
$this->state = $keyvalue->get('state');
|
||||
$this->langcode = language(LANGUAGE_TYPE_URL)->langcode;
|
||||
$this->whitelist = $this->state->get('system.path_alias_whitelist', NULL);
|
||||
if (!isset($this->whitelist)) {
|
||||
$this->whitelist = $this->pathAliasWhitelistRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::getSystemPath().
|
||||
*/
|
||||
public function getSystemPath($path, $path_language = NULL) {
|
||||
// If no language is explicitly specified we default to the current URL
|
||||
// language. If we used a language different from the one conveyed by the
|
||||
// requested URL, we might end up being unable to check if there is a path
|
||||
// alias matching the URL path.
|
||||
$path_language = $path_language ?: $this->langcode;
|
||||
$original_path = $path;
|
||||
// Lookup the path alias first.
|
||||
if (!empty($path) && $source = $this->lookupPathSource($path, $path_language)) {
|
||||
$path = $source;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::getPathAlias().
|
||||
*/
|
||||
public function getPathAlias($path, $path_language = NULL) {
|
||||
// If no language is explicitly specified we default to the current URL
|
||||
// language. If we used a language different from the one conveyed by the
|
||||
// requested URL, we might end up being unable to check if there is a path
|
||||
// alias matching the URL path.
|
||||
$path_language = $path_language ?: $this->langcode;
|
||||
$result = $path;
|
||||
if (!empty($path) && $alias = $this->lookupPathAlias($path, $path_language)) {
|
||||
$result = $alias;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::cacheClear().
|
||||
*/
|
||||
public function cacheClear($source = NULL) {
|
||||
$this->lookupMap = array();
|
||||
$this->noSource = array();
|
||||
$this->no_aliases = array();
|
||||
$this->firstCall = TRUE;
|
||||
$this->preloadedPathLookups = array();
|
||||
$this->whitelist = $this->pathAliasWhitelistRebuild($source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::getPathLookups().
|
||||
*/
|
||||
public function getPathLookups() {
|
||||
$current = current($this->lookupMap);
|
||||
if ($current) {
|
||||
return array_keys($current);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Path\AliasManagerInterface::preloadPathLookups().
|
||||
*/
|
||||
public function preloadPathLookups(array $path_list) {
|
||||
$this->preloadedPathLookups = $path_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a Drupal system URL return one of its aliases if such a one exists.
|
||||
* Otherwise, return FALSE.
|
||||
|
||||
* @param $path
|
||||
* The path to investigate for corresponding aliases.
|
||||
* @param $langcode
|
||||
* Optional language code to search the path with. Defaults to the page language.
|
||||
* If there's no path defined for that language it will search paths without
|
||||
* language.
|
||||
*
|
||||
* @return
|
||||
* An aliased path, or FALSE if no path was found.
|
||||
*/
|
||||
protected function lookupPathAlias($path, $langcode) {
|
||||
// During the first call to this method per language, load the expected
|
||||
// system paths for the page from cache.
|
||||
if (!empty($this->firstLookup)) {
|
||||
$this->firstLookup = FALSE;
|
||||
$this->lookupMap[$langcode] = array();
|
||||
// Load system paths from cache.
|
||||
if (!empty($this->preloadedPathLookups)) {
|
||||
// Now fetch the aliases corresponding to these system paths.
|
||||
$args = array(
|
||||
':system' => $this->preloadedPathLookups,
|
||||
':langcode' => $langcode,
|
||||
':langcode_undetermined' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
// Always get the language-specific alias before the language-neutral
|
||||
// one. For example 'de' is less than 'und' so the order needs to be
|
||||
// ASC, while 'xx-lolspeak' is more than 'und' so the order needs to
|
||||
// be DESC. We also order by pid ASC so that fetchAllKeyed() returns
|
||||
// the most recently created alias for each source. Subsequent queries
|
||||
// using fetchField() must use pid DESC to have the same effect.
|
||||
// For performance reasons, the query builder is not used here.
|
||||
if ($langcode == LANGUAGE_NOT_SPECIFIED) {
|
||||
// Prevent PDO from complaining about a token the query doesn't use.
|
||||
unset($args[':langcode']);
|
||||
$result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode = :langcode_undetermined ORDER BY pid ASC', $args);
|
||||
}
|
||||
elseif ($langcode < LANGUAGE_NOT_SPECIFIED) {
|
||||
$result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid ASC', $args);
|
||||
}
|
||||
else {
|
||||
$result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid ASC', $args);
|
||||
}
|
||||
$this->lookupMap[$langcode] = $result->fetchAllKeyed();
|
||||
// Keep a record of paths with no alias to avoid querying twice.
|
||||
$this->noAliases[$langcode] = array_flip(array_diff_key($this->preloadedPathLookups, array_keys($this->lookupMap[$langcode])));
|
||||
}
|
||||
}
|
||||
// If the alias has already been loaded, return it.
|
||||
if (isset($this->lookupMap[$langcode][$path])) {
|
||||
return $this->lookupMap[$langcode][$path];
|
||||
}
|
||||
// Check the path whitelist, if the top-level part before the first /
|
||||
// is not in the list, then there is no need to do anything further,
|
||||
// it is not in the database.
|
||||
elseif (!isset($this->whitelist[strtok($path, '/')])) {
|
||||
return FALSE;
|
||||
}
|
||||
// For system paths which were not cached, query aliases individually.
|
||||
elseif (!isset($this->noAliases[$langcode][$path])) {
|
||||
$args = array(
|
||||
':source' => $path,
|
||||
':langcode' => $langcode,
|
||||
':langcode_undetermined' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
// See the queries above.
|
||||
if ($langcode == LANGUAGE_NOT_SPECIFIED) {
|
||||
unset($args[':langcode']);
|
||||
$alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode = :langcode_undetermined ORDER BY pid DESC", $args)->fetchField();
|
||||
}
|
||||
elseif ($langcode > LANGUAGE_NOT_SPECIFIED) {
|
||||
$alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args)->fetchField();
|
||||
}
|
||||
else {
|
||||
$alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args)->fetchField();
|
||||
}
|
||||
$this->lookupMap[$langcode][$path] = $alias;
|
||||
return $alias;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an alias, return its Drupal system URL if one exists. Otherwise,
|
||||
* return FALSE.
|
||||
*
|
||||
* @param $path
|
||||
* The path to investigate for corresponding system URLs.
|
||||
* @param $langcode
|
||||
* Optional language code to search the path with. Defaults to the page language.
|
||||
* If there's no path defined for that language it will search paths without
|
||||
* language.
|
||||
*
|
||||
* @return
|
||||
* A Drupal system path, or FALSE if no path was found.
|
||||
*/
|
||||
protected function lookupPathSource($path, $langcode) {
|
||||
if ($this->whitelist && !isset($this->noSource[$langcode][$path])) {
|
||||
// Look for the value $path within the cached $map
|
||||
$source = FALSE;
|
||||
if (!isset($this->lookupMap[$langcode]) || !($source = array_search($path, $this->lookupMap[$langcode]))) {
|
||||
$args = array(
|
||||
':alias' => $path,
|
||||
':langcode' => $langcode,
|
||||
':langcode_undetermined' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
// See the queries above.
|
||||
if ($langcode == LANGUAGE_NOT_SPECIFIED) {
|
||||
unset($args[':langcode']);
|
||||
$result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode = :langcode_undetermined ORDER BY pid DESC", $args);
|
||||
}
|
||||
elseif ($langcode > LANGUAGE_NOT_SPECIFIED) {
|
||||
$result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args);
|
||||
}
|
||||
else {
|
||||
$result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args);
|
||||
}
|
||||
if ($source = $result->fetchField()) {
|
||||
$this->lookupMap[$langcode][$source] = $path;
|
||||
}
|
||||
else {
|
||||
// We can't record anything into $map because we do not have a valid
|
||||
// index and there is no need because we have not learned anything
|
||||
// about any Drupal path. Thus cache to $no_source.
|
||||
$this->noSource[$langcode][$path] = TRUE;
|
||||
}
|
||||
}
|
||||
return $source;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the path alias white list.
|
||||
*
|
||||
* @param $source
|
||||
* An optional system path for which an alias is being inserted.
|
||||
*
|
||||
* @return
|
||||
* An array containing a white list of path aliases.
|
||||
*/
|
||||
protected function pathAliasWhitelistRebuild($source = NULL) {
|
||||
// When paths are inserted, only rebuild the whitelist if the system path
|
||||
// has a top level component which is not already in the whitelist.
|
||||
if (!empty($source)) {
|
||||
// @todo Inject state so we don't have this function call.
|
||||
$whitelist = $this->state->get('system.path_alias_whitelist', NULL);
|
||||
if (isset($whitelist[strtok($source, '/')])) {
|
||||
return $whitelist;
|
||||
}
|
||||
}
|
||||
// For each alias in the database, get the top level component of the system
|
||||
// path it corresponds to. This is the portion of the path before the first
|
||||
// '/', if present, otherwise the whole path itself.
|
||||
$whitelist = array();
|
||||
$result = $this->connection->query("SELECT DISTINCT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias}");
|
||||
foreach ($result as $row) {
|
||||
$whitelist[$row->path] = TRUE;
|
||||
}
|
||||
$this->state->set('system.path_alias_whitelist', $whitelist);
|
||||
return $whitelist;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\Path\AliasManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Path;
|
||||
|
||||
interface AliasManagerInterface {
|
||||
|
||||
/**
|
||||
* Given a path alias, return the internal path it represents.
|
||||
*
|
||||
* @param $path
|
||||
* A Drupal path alias.
|
||||
* @param $path_language
|
||||
* An optional language code to look up the path in.
|
||||
*
|
||||
* @return
|
||||
* The internal path represented by the alias, or the original alias if no
|
||||
* internal path was found.
|
||||
*/
|
||||
public function getSystemPath($path, $path_language = NULL);
|
||||
|
||||
/**
|
||||
* Given an internal Drupal path, return the alias set by the administrator.
|
||||
*
|
||||
* @param $path
|
||||
* An internal Drupal path.
|
||||
*
|
||||
* @param $path_language
|
||||
* An optional language code to look up the path in.
|
||||
*
|
||||
* @return
|
||||
* An aliased path if one was found, or the original path if no alias was
|
||||
* found.
|
||||
*/
|
||||
public function getPathAlias($path, $path_language = NULL);
|
||||
|
||||
/**
|
||||
* Returns an array of system paths that have been looked up.
|
||||
*
|
||||
* @return array
|
||||
* An array of all system paths that have been looked up during the current
|
||||
* request.
|
||||
*/
|
||||
public function getPathLookups();
|
||||
|
||||
/**
|
||||
* Preload a set of paths for bulk alias lookups.
|
||||
*
|
||||
* @param $path_list
|
||||
* An array of system paths.
|
||||
*/
|
||||
public function preloadPathLookups(array $path_list);
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\Core\Path\Path.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Path;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
/**
|
||||
* Defines a class for CRUD operations on path aliases.
|
||||
*/
|
||||
class Path {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Constructs a Path CRUD object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* A database connection for reading and writing path aliases.
|
||||
*
|
||||
* @param \Drupal\Core\Path\AliasManager $alias_manager
|
||||
* An alias manager with an internal cache of stored aliases.
|
||||
*
|
||||
* @todo This class should not take an alias manager in its constructor. Once
|
||||
* we move to firing an event for CRUD operations instead of invoking a
|
||||
* hook, we can have a listener that calls cacheClear() on the alias manager.
|
||||
*/
|
||||
public function __construct(Connection $connection, AliasManager $alias_manager) {
|
||||
$this->connection = $connection;
|
||||
$this->alias_manager = $alias_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a path alias to the database.
|
||||
*
|
||||
* @param string $source
|
||||
* The internal system path.
|
||||
*
|
||||
* @param string $alias
|
||||
* The URL alias.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The language code of the alias.
|
||||
*
|
||||
* @param int $pid
|
||||
* Unique path alias identifier.
|
||||
*
|
||||
* @return
|
||||
* FALSE if the path could not be saved or an associative array containing
|
||||
* the following keys:
|
||||
* - source: The internal system path.
|
||||
* - alias: The URL alias.
|
||||
* - pid: Unique path alias identifier.
|
||||
* - langcode: The language code of the alias.
|
||||
*/
|
||||
public function save($source, $alias, $langcode = LANGUAGE_NOT_SPECIFIED, $pid = NULL) {
|
||||
|
||||
$fields = array(
|
||||
'source' => $source,
|
||||
'alias' => $alias,
|
||||
'langcode' => $langcode,
|
||||
);
|
||||
|
||||
// Insert or update the alias.
|
||||
if (empty($pid)) {
|
||||
$query = $this->connection->insert('url_alias')
|
||||
->fields($fields);
|
||||
$pid = $query->execute();
|
||||
$fields['pid'] = $pid;
|
||||
// @todo: Find a correct place to invoke hook_path_insert().
|
||||
$hook = 'path_insert';
|
||||
}
|
||||
else {
|
||||
$fields['pid'] = $pid;
|
||||
$query = $this->connection->update('url_alias')
|
||||
->fields($fields)
|
||||
->condition('pid', $pid);
|
||||
$pid = $query->execute();
|
||||
// @todo: figure out where we can invoke hook_path_update()
|
||||
$hook = 'path_update';
|
||||
}
|
||||
if ($pid) {
|
||||
// @todo Switch to using an event for this instead of a hook.
|
||||
module_invoke_all($hook, $fields);
|
||||
$this->alias_manager->cacheClear();
|
||||
return $fields;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a specific URL alias from the database.
|
||||
*
|
||||
* @param $conditions
|
||||
* An array of query conditions.
|
||||
*
|
||||
* @return
|
||||
* FALSE if no alias was found or an associative array containing the
|
||||
* following keys:
|
||||
* - source: The internal system path.
|
||||
* - alias: The URL alias.
|
||||
* - pid: Unique path alias identifier.
|
||||
* - langcode: The language code of the alias.
|
||||
*/
|
||||
public function load($conditions) {
|
||||
$select = $this->connection->select('url_alias');
|
||||
foreach ($conditions as $field => $value) {
|
||||
$select->condition($field, $value);
|
||||
}
|
||||
return $select
|
||||
->fields('url_alias')
|
||||
->execute()
|
||||
->fetchAssoc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a URL alias.
|
||||
*
|
||||
* @param array $conditions
|
||||
* An array of criteria.
|
||||
*/
|
||||
public function delete($conditions) {
|
||||
$path = $this->load($conditions);
|
||||
$query = $this->connection->delete('url_alias');
|
||||
foreach ($conditions as $field => $value) {
|
||||
$query->condition($field, $value);
|
||||
}
|
||||
$deleted = $query->execute();
|
||||
// @todo Switch to using an event for this instead of a hook.
|
||||
module_invoke_all('path_delete', $path);
|
||||
$this->alias_manager->cacheClear();
|
||||
return $deleted;
|
||||
}
|
||||
}
|
|
@ -851,7 +851,7 @@ function block_block_list_alter(&$blocks) {
|
|||
if ($block->visibility < BLOCK_VISIBILITY_PHP) {
|
||||
// Compare the lowercase path alias (if any) and internal path.
|
||||
$path = current_path();
|
||||
$path_alias = drupal_strtolower(drupal_get_path_alias($path));
|
||||
$path_alias = drupal_strtolower(drupal_container()->get('path.alias_manager')->getPathAlias($path));
|
||||
$page_match = drupal_match_path($path_alias, $pages) || (($path != $path_alias) && drupal_match_path($path, $pages));
|
||||
// When $block->visibility has a value of 0 (BLOCK_VISIBILITY_NOTLISTED),
|
||||
// the block is displayed on all pages except those listed in $block->pages.
|
||||
|
|
|
@ -109,13 +109,13 @@ class LocalePathTest extends WebTestBase {
|
|||
'alias' => $custom_path,
|
||||
'langcode' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
path_save($edit);
|
||||
$lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, 'en');
|
||||
drupal_container()->get('path.crud')->save($edit['source'], $edit['alias'], $edit['langcode']);
|
||||
$lookup_path = drupal_container()->get('path.alias_manager')->getPathAlias('node/' . $node->nid, 'en');
|
||||
$this->assertEqual($english_path, $lookup_path, t('English language alias has priority.'));
|
||||
// Same check for language 'xx'.
|
||||
$lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, $prefix);
|
||||
$lookup_path = drupal_container()->get('path.alias_manager')->getPathAlias('node/' . $node->nid, $prefix);
|
||||
$this->assertEqual($custom_language_path, $lookup_path, t('Custom language alias has priority.'));
|
||||
path_delete($edit);
|
||||
drupal_container()->get('path.crud')->delete($edit);
|
||||
|
||||
// Create language nodes to check priority of aliases.
|
||||
$first_node = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
|
||||
|
@ -127,7 +127,7 @@ class LocalePathTest extends WebTestBase {
|
|||
'alias' => $custom_path,
|
||||
'langcode' => 'en',
|
||||
);
|
||||
path_save($edit);
|
||||
drupal_container()->get('path.crud')->save($edit['source'], $edit['alias'], $edit['langcode']);
|
||||
|
||||
// Assign a custom path alias to second node with LANGUAGE_NOT_SPECIFIED.
|
||||
$edit = array(
|
||||
|
@ -135,7 +135,7 @@ class LocalePathTest extends WebTestBase {
|
|||
'alias' => $custom_path,
|
||||
'langcode' => LANGUAGE_NOT_SPECIFIED,
|
||||
);
|
||||
path_save($edit);
|
||||
drupal_container()->get('path.crud')->save($edit['source'], $edit['alias'], $edit['langcode']);
|
||||
|
||||
// Test that both node titles link to our path alias.
|
||||
$this->drupalGet('<front>');
|
||||
|
|
|
@ -417,7 +417,7 @@ function menu_edit_item($form, &$form_state, $type, $item, $menu) {
|
|||
*/
|
||||
function menu_edit_item_validate($form, &$form_state) {
|
||||
$item = &$form_state['values'];
|
||||
$normal_path = drupal_get_normal_path($item['link_path']);
|
||||
$normal_path = drupal_container()->get('path.alias_manager')->getSystemPath($item['link_path']);
|
||||
if ($item['link_path'] != $normal_path) {
|
||||
drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $item['link_path'], '%normal_path' => $normal_path)));
|
||||
$item['link_path'] = $normal_path;
|
||||
|
|
|
@ -57,9 +57,7 @@ class PathAliasTest extends PathTestBase {
|
|||
// Visit the alias for the node and confirm a cache entry is created.
|
||||
cache('path')->flush();
|
||||
$this->drupalGet($edit['alias']);
|
||||
// @todo The alias should actually have been cached with the system path as
|
||||
// the key, see the todo in drupal_cache_system_paths() in path.inc.
|
||||
$this->assertTrue(cache('path')->get($edit['alias']), 'Cache entry was created.');
|
||||
$this->assertTrue(cache('path')->get($edit['source']), 'Cache entry was created.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +92,7 @@ class PathAliasTest extends PathTestBase {
|
|||
$this->assertText($node1->label(), 'Changed alias works.');
|
||||
$this->assertResponse(200);
|
||||
|
||||
drupal_static_reset('drupal_lookup_path');
|
||||
drupal_container()->get('path.alias_manager')->cacheClear();
|
||||
// Confirm that previous alias no longer works.
|
||||
$this->drupalGet($previous);
|
||||
$this->assertNoText($node1->label(), 'Previous alias no longer works.');
|
||||
|
|
|
@ -82,7 +82,7 @@ class PathLanguageTest extends PathTestBase {
|
|||
$this->drupalPost(NULL, $edit, t('Save'));
|
||||
|
||||
// Clear the path lookup cache.
|
||||
drupal_lookup_path('wipe');
|
||||
drupal_container()->get('path.alias_manager')->cacheClear();
|
||||
|
||||
// Ensure the node was created.
|
||||
$french_node = $this->drupalGetNodeByTitle($edit["title"]);
|
||||
|
@ -121,7 +121,7 @@ class PathLanguageTest extends PathTestBase {
|
|||
// 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()
|
||||
// path alias for French matching the english alias. So the alias manager
|
||||
// needs to use the URL language to check whether the alias is valid.
|
||||
$this->drupalGet($english_alias);
|
||||
$this->assertText($english_node->label(), 'Alias for English translation works.');
|
||||
|
@ -145,20 +145,20 @@ class PathLanguageTest extends PathTestBase {
|
|||
$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
|
||||
// The alias manager has an internal path lookup 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->langcode);
|
||||
drupal_container()->get('path.alias_manager')->cacheClear();
|
||||
$french_node_path = drupal_container()->get('path.alias_manager')->getSystemPath($french_alias, $french_node->langcode);
|
||||
$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->langcode);
|
||||
$french_node_path = drupal_container()->get('path.alias_manager')->getSystemPath($french_alias, $french_node->langcode);
|
||||
$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->langcode);
|
||||
$french_node_alias = drupal_container()->get('path.alias_manager')->getPathAlias('node/' . $french_node->nid, $french_node->langcode);
|
||||
$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->langcode);
|
||||
$french_node_alias = drupal_container()->get('path.alias_manager')->getPathAlias('node/' . $french_node->nid, $french_node->langcode);
|
||||
$this->assertEqual($french_node_alias, $french_alias, 'Alias is the same.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ function path_admin_overview($keys = NULL) {
|
|||
|
||||
// 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->langcode)) {
|
||||
if ($data->alias != drupal_container()->get('path.alias_manager')->getPathAlias($data->source, $data->langcode)) {
|
||||
$row['class'] = array('warning');
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ function path_admin_form_delete_submit($form, &$form_state) {
|
|||
*/
|
||||
function path_admin_form_validate($form, &$form_state) {
|
||||
$source = &$form_state['values']['source'];
|
||||
$source = drupal_get_normal_path($source);
|
||||
$source = drupal_container()->get('path.alias_manager')->getSystemPath($source);
|
||||
$alias = $form_state['values']['alias'];
|
||||
$pid = isset($form_state['values']['pid']) ? $form_state['values']['pid'] : 0;
|
||||
// Language is only set if language.module is enabled, otherwise save for all
|
||||
|
@ -249,7 +249,15 @@ function path_admin_form_submit($form, &$form_state) {
|
|||
// Remove unnecessary values.
|
||||
form_state_values_clean($form_state);
|
||||
|
||||
path_save($form_state['values']);
|
||||
$pid = isset($form_state['values']['pid']) ? $form_state['values']['pid'] : 0;
|
||||
$source = &$form_state['values']['source'];
|
||||
$source = drupal_container()->get('path.alias_manager')->getSystemPath($source);
|
||||
$alias = $form_state['values']['alias'];
|
||||
// Language is only set if language.module is enabled, otherwise save for all
|
||||
// languages.
|
||||
$langcode = isset($form_state['values']['langcode']) ? $form_state['values']['langcode'] : LANGUAGE_NOT_SPECIFIED;
|
||||
|
||||
drupal_container()->get('path.crud')->save($source, $alias, $langcode, $pid);
|
||||
|
||||
drupal_set_message(t('The alias has been saved.'));
|
||||
$form_state['redirect'] = 'admin/config/search/path';
|
||||
|
@ -281,7 +289,7 @@ function path_admin_delete_confirm($form, &$form_state, $path) {
|
|||
*/
|
||||
function path_admin_delete_confirm_submit($form, &$form_state) {
|
||||
if ($form_state['values']['confirm']) {
|
||||
path_delete($form_state['path']['pid']);
|
||||
drupal_container()->get('path.crud')->delete(array('pid' => $form_state['path']['pid']));
|
||||
$form_state['redirect'] = 'admin/config/search/path';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
* - pid: Unique path alias identifier.
|
||||
* - langcode: The language code of the alias.
|
||||
*
|
||||
* @see path_save()
|
||||
* @see \Drupal\Core\Path\Path::save()
|
||||
*/
|
||||
function hook_path_insert($path) {
|
||||
db_insert('mytable')
|
||||
|
@ -41,7 +41,7 @@ function hook_path_insert($path) {
|
|||
* - pid: Unique path alias identifier.
|
||||
* - langcode: The language code of the alias.
|
||||
*
|
||||
* @see path_save()
|
||||
* @see \Drupal\Core\Path\Path::save()
|
||||
*/
|
||||
function hook_path_update($path) {
|
||||
db_update('mytable')
|
||||
|
@ -60,7 +60,7 @@ function hook_path_update($path) {
|
|||
* - pid: Unique path alias identifier.
|
||||
* - langcode: The language code of the alias.
|
||||
*
|
||||
* @see path_delete()
|
||||
* @see \Drupal\Core\Path\Path::delete()
|
||||
*/
|
||||
function hook_path_delete($path) {
|
||||
db_delete('mytable')
|
||||
|
|
|
@ -107,7 +107,7 @@ function path_form_node_form_alter(&$form, $form_state) {
|
|||
if ($node->langcode != LANGUAGE_NOT_SPECIFIED) {
|
||||
$conditions['langcode'] = $node->langcode;
|
||||
}
|
||||
$path = path_load($conditions);
|
||||
$path = drupal_container()->get('path.crud')->load($conditions);
|
||||
if ($path === FALSE) {
|
||||
$path = array();
|
||||
}
|
||||
|
@ -190,14 +190,13 @@ function path_form_element_validate($element, &$form_state, $complete_form) {
|
|||
*/
|
||||
function path_node_insert(Node $node) {
|
||||
if (isset($node->path)) {
|
||||
$path = $node->path;
|
||||
$path['alias'] = trim($path['alias']);
|
||||
$alias = trim($node->path['alias']);
|
||||
// Only save a non-empty alias.
|
||||
if (!empty($path['alias'])) {
|
||||
if (!empty($alias)) {
|
||||
// Ensure fields for programmatic executions.
|
||||
$path['source'] = 'node/' . $node->nid;
|
||||
$path['langcode'] = isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED;
|
||||
path_save($path);
|
||||
$source = 'node/' . $node->nid;
|
||||
$langcode = isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED;
|
||||
drupal_container()->get('path.crud')->save($source, $alias, $langcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -208,17 +207,17 @@ function path_node_insert(Node $node) {
|
|||
function path_node_update(Node $node) {
|
||||
if (isset($node->path)) {
|
||||
$path = $node->path;
|
||||
$path['alias'] = trim($path['alias']);
|
||||
$alias = trim($path['alias']);
|
||||
// Delete old alias if user erased it.
|
||||
if (!empty($path['pid']) && empty($path['alias'])) {
|
||||
path_delete($path['pid']);
|
||||
drupal_container()->get('path.crud')->delete(array('pid' => $path['pid']));
|
||||
}
|
||||
// Only save a non-empty alias.
|
||||
if (!empty($path['alias'])) {
|
||||
// Ensure fields for programmatic executions.
|
||||
$path['source'] = 'node/' . $node->nid;
|
||||
$path['langcode'] = isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED;
|
||||
path_save($path);
|
||||
$source = 'node/' . $node->nid;
|
||||
$langcode = isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED;
|
||||
drupal_container()->get('path.crud')->save($source, $alias, $langcode, $path['pid']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +227,7 @@ function path_node_update(Node $node) {
|
|||
*/
|
||||
function path_node_predelete(Node $node) {
|
||||
// Delete all aliases associated with this node.
|
||||
path_delete(array('source' => 'node/' . $node->nid));
|
||||
drupal_container()->get('path.crud')->delete(array('source' => 'node/' . $node->nid));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,7 +237,7 @@ function path_form_taxonomy_term_form_alter(&$form, $form_state) {
|
|||
// Make sure this does not show up on the delete confirmation form.
|
||||
if (empty($form_state['confirm_delete'])) {
|
||||
$term = $form_state['controller']->getEntity($form_state);
|
||||
$path = (isset($term->tid) ? path_load('taxonomy/term/' . $term->tid) : array());
|
||||
$path = (isset($term->tid) ? drupal_container()->get('path.crud')->load(array('source' => 'taxonomy/term/' . $term->tid)) : array());
|
||||
if ($path === FALSE) {
|
||||
$path = array();
|
||||
}
|
||||
|
@ -279,7 +278,7 @@ function path_taxonomy_term_insert(Term $term) {
|
|||
// Ensure fields for programmatic executions.
|
||||
$path['source'] = 'taxonomy/term/' . $term->tid;
|
||||
$path['langcode'] = LANGUAGE_NOT_SPECIFIED;
|
||||
path_save($path);
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias'], $path['langcode']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -293,14 +292,15 @@ function path_taxonomy_term_update(Term $term) {
|
|||
$path['alias'] = trim($path['alias']);
|
||||
// Delete old alias if user erased it.
|
||||
if (!empty($path['pid']) && empty($path['alias'])) {
|
||||
path_delete($path['pid']);
|
||||
drupal_container()->get('path.crud')->delete(array('pid' => $path['pid']));
|
||||
}
|
||||
// Only save a non-empty alias.
|
||||
if (!empty($path['alias'])) {
|
||||
$pid = (!empty($path['pid']) ? $path['pid'] : NULL);
|
||||
// Ensure fields for programmatic executions.
|
||||
$path['source'] = 'taxonomy/term/' . $term->tid;
|
||||
$path['langcode'] = LANGUAGE_NOT_SPECIFIED;
|
||||
path_save($path);
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias'], $path['langcode'], $pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ function path_taxonomy_term_update(Term $term) {
|
|||
*/
|
||||
function path_taxonomy_term_delete(Term $term) {
|
||||
// Delete all aliases associated with this term.
|
||||
path_delete(array('source' => 'taxonomy/term/' . $term->tid));
|
||||
drupal_container()->get('path.crud')->delete(array('source' => 'taxonomy/term/' . $term->tid));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -643,7 +643,7 @@ function search_index($sid, $module, $text, $langcode) {
|
|||
if ($tagname == 'a') {
|
||||
// Check if link points to a node on this site
|
||||
if (preg_match($node_regexp, $value, $match)) {
|
||||
$path = drupal_get_normal_path($match[1]);
|
||||
$path = drupal_container()->get('path.alias_manager')->getSystemPath($match[1]);
|
||||
if (preg_match('!(?:node|book)/(?:view/)?([0-9]+)!i', $path, $match)) {
|
||||
$linknid = $match[1];
|
||||
if ($linknid > 0) {
|
||||
|
|
|
@ -31,7 +31,7 @@ class ShortcutLinksTest extends ShortcutTestBase {
|
|||
'source' => 'node/' . $this->node->nid,
|
||||
'alias' => $this->randomName(8),
|
||||
);
|
||||
path_save($path);
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias']);
|
||||
|
||||
// Create some paths to test.
|
||||
$test_cases = array(
|
||||
|
@ -54,7 +54,7 @@ class ShortcutLinksTest extends ShortcutTestBase {
|
|||
$saved_set = shortcut_set_load($set->set_name);
|
||||
$paths = $this->getShortcutInformation($saved_set, 'link_path');
|
||||
$test_path = empty($test['path']) ? '<front>' : $test['path'];
|
||||
$this->assertTrue(in_array(drupal_get_normal_path($test_path), $paths), 'Shortcut created: '. $test['path']);
|
||||
$this->assertTrue(in_array(drupal_container()->get('path.alias_manager')->getSystemPath($test_path), $paths), 'Shortcut created: '. $test['path']);
|
||||
$this->assertLink($title, 0, 'Shortcut link found on the page.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,7 +430,7 @@ function _shortcut_link_form_elements($shortcut_link = NULL) {
|
|||
);
|
||||
}
|
||||
else {
|
||||
$shortcut_link['link_path'] = ($shortcut_link['link_path'] == '<front>') ? '' : drupal_get_path_alias($shortcut_link['link_path']);
|
||||
$shortcut_link['link_path'] = ($shortcut_link['link_path'] == '<front>') ? '' : drupal_container()->get('path.alias_manager')->getPathAlias($shortcut_link['link_path']);
|
||||
}
|
||||
|
||||
$form['shortcut_link']['#tree'] = TRUE;
|
||||
|
@ -477,7 +477,7 @@ function shortcut_link_edit_validate($form, &$form_state) {
|
|||
*/
|
||||
function shortcut_link_edit_submit($form, &$form_state) {
|
||||
// Normalize the path in case it is an alias.
|
||||
$shortcut_path = drupal_get_normal_path($form_state['values']['shortcut_link']['link_path']);
|
||||
$shortcut_path = drupal_container()->get('path.alias_manager')->getSystemPath($form_state['values']['shortcut_link']['link_path']);
|
||||
if (empty($shortcut_path)) {
|
||||
$shortcut_path = '<front>';
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ function shortcut_link_add_submit($form, &$form_state) {
|
|||
*/
|
||||
function shortcut_admin_add_link($shortcut_link, &$shortcut_set) {
|
||||
// Normalize the path in case it is an alias.
|
||||
$shortcut_link['link_path'] = drupal_get_normal_path($shortcut_link['link_path']);
|
||||
$shortcut_link['link_path'] = drupal_container()->get('path.alias_manager')->getSystemPath($shortcut_link['link_path']);
|
||||
if (empty($shortcut_link['link_path'])) {
|
||||
$shortcut_link['link_path'] = '<front>';
|
||||
}
|
||||
|
|
|
@ -616,7 +616,7 @@ function shortcut_set_title_exists($title) {
|
|||
*/
|
||||
function shortcut_valid_link($path) {
|
||||
// Do not use URL aliases.
|
||||
$normal_path = drupal_get_normal_path($path);
|
||||
$normal_path = drupal_container()->get('path.alias_manager')->getSystemPath($path);
|
||||
if ($path != $normal_path) {
|
||||
$path = $normal_path;
|
||||
}
|
||||
|
|
|
@ -760,6 +760,12 @@ abstract class WebTestBase extends TestBase {
|
|||
variable_set('mail_system', array('default-system' => 'Drupal\Core\Mail\VariableLog'));
|
||||
|
||||
drupal_set_time_limit($this->timeLimit);
|
||||
// Temporary fix so that when running from run-tests.sh we don't get an
|
||||
// empty current path which would indicate we're on the home page.
|
||||
$path = current_path();
|
||||
if (empty($path)) {
|
||||
_current_path('run-tests');
|
||||
}
|
||||
$this->setup = TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ function statistics_block_view($delta = '') {
|
|||
* A string as a link, truncated to the width, linked to the given $path.
|
||||
*/
|
||||
function _statistics_link($path, $width = 35) {
|
||||
$title = drupal_get_path_alias($path);
|
||||
$title = drupal_container()->get('path.alias_manager')->getPathAlias($path);
|
||||
$title = truncate_utf8($title, $width, FALSE, TRUE);
|
||||
return l($title, $path);
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ class JavaScriptTest extends WebTestBase {
|
|||
$this->drupalGet('common-test/query-string');
|
||||
$this->assertPattern('@<script>.+drupalSettings.+"currentPath":"common-test\\\/query-string"@s', 'currentPath is in the JS settings');
|
||||
$path = array('source' => 'common-test/query-string', 'alias' => 'common-test/currentpath-check');
|
||||
path_save($path);
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias']);
|
||||
$this->drupalGet('common-test/currentpath-check');
|
||||
$this->assertPattern('@<script>.+drupalSettings.+"currentPath":"common-test\\\/query-string"@s', 'currentPath is in the JS settings for an aliased path');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\system\Tests\Path\CrudTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Path;
|
||||
|
||||
use Drupal\simpletest\UnitTestBase;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\KeyValueStore\KeyValueDatabaseFactory;
|
||||
use Drupal\Core\Path\Path;
|
||||
use Drupal\Core\Path\AliasManager;
|
||||
|
||||
/**
|
||||
* Tests path alias CRUD and lookup functionality.
|
||||
*/
|
||||
class AliasTest extends UnitTestBase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Path Alias Unit Tests'),
|
||||
'description' => t('Tests path alias CRUD and lookup functionality.'),
|
||||
'group' => t('Path API'),
|
||||
);
|
||||
}
|
||||
|
||||
function __construct($test_id = NULL) {
|
||||
parent::__construct($test_id);
|
||||
|
||||
$this->fixtures = new UrlAliasFixtures();
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
$this->fixtures->dropTables(Database::getConnection());
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
|
||||
function testCRUD() {
|
||||
//Prepare database table.
|
||||
$connection = Database::getConnection();
|
||||
$this->fixtures->createTables($connection);
|
||||
|
||||
//Create AliasManager and Path object.
|
||||
$aliasManager = new AliasManager($connection, new KeyValueDatabaseFactory($connection));
|
||||
$path = new Path($connection, $aliasManager);
|
||||
|
||||
$aliases = $this->fixtures->sampleUrlAliases();
|
||||
|
||||
//Create a few aliases
|
||||
foreach ($aliases as $idx => $alias) {
|
||||
$path->save($alias['source'], $alias['alias'], $alias['langcode']);
|
||||
|
||||
$result = $connection->query('SELECT * FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'], ':langcode' => $alias['langcode']));
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
$this->assertEqual(count($rows), 1, format_string('Created an entry for %alias.', array('%alias' => $alias['alias'])));
|
||||
|
||||
//Cache the pid for further tests.
|
||||
$aliases[$idx]['pid'] = $rows[0]->pid;
|
||||
}
|
||||
|
||||
//Load a few aliases
|
||||
foreach ($aliases as $alias) {
|
||||
$pid = $alias['pid'];
|
||||
$loadedAlias = $path->load(array('pid' => $pid));
|
||||
$this->assertEqual($loadedAlias, $alias, format_string('Loaded the expected path with pid %pid.', array('%pid' => $pid)));
|
||||
}
|
||||
|
||||
//Update a few aliases
|
||||
foreach ($aliases as $alias) {
|
||||
$path->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']);
|
||||
|
||||
$result = $connection->query('SELECT pid FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'] . '_updated', ':langcode' => $alias['langcode']));
|
||||
$pid = $result->fetchField();
|
||||
|
||||
$this->assertEqual($pid, $alias['pid'], format_string('Updated entry for pid %pid.', array('%pid' => $pid)));
|
||||
}
|
||||
|
||||
//Delete a few aliases
|
||||
foreach ($aliases as $alias) {
|
||||
$pid = $alias['pid'];
|
||||
$path->delete(array('pid' => $pid));
|
||||
|
||||
$result = $connection->query('SELECT * FROM {url_alias} WHERE pid = :pid', array(':pid' => $pid));
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
$this->assertEqual(count($rows), 0, format_string('Deleted entry with pid %pid.', array('%pid' => $pid)));
|
||||
}
|
||||
}
|
||||
|
||||
function testLookupPath() {
|
||||
//Prepare database table.
|
||||
$connection = Database::getConnection();
|
||||
$this->fixtures->createTables($connection);
|
||||
|
||||
//Create AliasManager and Path object.
|
||||
$aliasManager = new AliasManager($connection, new KeyValueDatabaseFactory($connection));
|
||||
$pathObject = new Path($connection, $aliasManager);
|
||||
|
||||
// Test the situation where the source is the same for multiple aliases.
|
||||
// Start with a language-neutral alias, which we will override.
|
||||
$path = array(
|
||||
'source' => "user/1",
|
||||
'alias' => 'foo',
|
||||
);
|
||||
|
||||
$pathObject->save($path['source'], $path['alias']);
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'Basic alias lookup works.');
|
||||
$this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'Basic source lookup works.');
|
||||
|
||||
// Create a language specific alias for the default language (English).
|
||||
$path = array(
|
||||
'source' => "user/1",
|
||||
'alias' => "users/Dries",
|
||||
'langcode' => 'en',
|
||||
);
|
||||
$pathObject->save($path['source'], $path['alias'], $path['langcode']);
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'English alias overrides language-neutral alias.');
|
||||
$this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'English source overrides language-neutral source.');
|
||||
|
||||
// Create a language-neutral alias for the same path, again.
|
||||
$path = array(
|
||||
'source' => "user/1",
|
||||
'alias' => 'bar',
|
||||
);
|
||||
$pathObject->save($path['source'], $path['alias']);
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a language-neutral alias.');
|
||||
|
||||
// Create a language-specific (xx-lolspeak) alias for the same path.
|
||||
$path = array(
|
||||
'source' => "user/1",
|
||||
'alias' => 'LOL',
|
||||
'langcode' => 'xx-lolspeak',
|
||||
);
|
||||
$pathObject->save($path['source'], $path['alias'], $path['langcode']);
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a LOLspeak alias.');
|
||||
// The LOLspeak alias should be returned if we really want LOLspeak.
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source'], 'xx-lolspeak'), 'LOL', 'LOLspeak alias returned if we specify xx-lolspeak to the alias manager.');
|
||||
|
||||
// Create a new alias for this path in English, which should override the
|
||||
// previous alias for "user/1".
|
||||
$path = array(
|
||||
'source' => "user/1",
|
||||
'alias' => 'users/my-new-path',
|
||||
'langcode' => 'en',
|
||||
);
|
||||
$pathObject->save($path['source'], $path['alias'], $path['langcode']);
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'Recently created English alias returned.');
|
||||
$this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'Recently created English source returned.');
|
||||
|
||||
// Remove the English aliases, which should cause a fallback to the most
|
||||
// recently created language-neutral alias, 'bar'.
|
||||
$pathObject->delete(array('langcode' => 'en'));
|
||||
$this->assertEqual($aliasManager->getPathAlias($path['source']), 'bar', 'Path lookup falls back to recently created language-neutral alias.');
|
||||
|
||||
// Test the situation where the alias and language are the same, but
|
||||
// the source differs. The newer alias record should be returned.
|
||||
$pathObject->save('user/2', 'bar');
|
||||
$this->assertEqual($aliasManager->getSystemPath('bar'), 'user/2', 'Newer alias record is returned when comparing two LANGUAGE_NOT_SPECIFIED paths with the same alias.');
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\system\Tests\Path\LookupTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Path;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Unit test for drupal_lookup_path().
|
||||
*/
|
||||
class LookupTest extends WebTestBase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Path lookup'),
|
||||
'description' => t('Tests that drupal_lookup_path() returns correct paths.'),
|
||||
'group' => t('Path API'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that drupal_lookup_path() returns the correct path.
|
||||
*/
|
||||
function testDrupalLookupPath() {
|
||||
$account = $this->drupalCreateUser();
|
||||
$uid = $account->uid;
|
||||
$name = $account->name;
|
||||
|
||||
// Test the situation where the source is the same for multiple aliases.
|
||||
// Start with a language-neutral alias, which we will override.
|
||||
$path = array(
|
||||
'source' => "user/$uid",
|
||||
'alias' => 'foo',
|
||||
);
|
||||
path_save($path);
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], 'Basic alias lookup works.');
|
||||
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'Basic source lookup works.');
|
||||
|
||||
// Create a language specific alias for the default language (English).
|
||||
$path = array(
|
||||
'source' => "user/$uid",
|
||||
'alias' => "users/$name",
|
||||
'langcode' => 'en',
|
||||
);
|
||||
path_save($path);
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], 'English alias overrides language-neutral alias.');
|
||||
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'English source overrides language-neutral source.');
|
||||
|
||||
// Create a language-neutral alias for the same path, again.
|
||||
$path = array(
|
||||
'source' => "user/$uid",
|
||||
'alias' => 'bar',
|
||||
);
|
||||
path_save($path);
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source']), "users/$name", 'English alias still returned after entering a language-neutral alias.');
|
||||
|
||||
// Create a language-specific (xx-lolspeak) alias for the same path.
|
||||
$path = array(
|
||||
'source' => "user/$uid",
|
||||
'alias' => 'LOL',
|
||||
'langcode' => 'xx-lolspeak',
|
||||
);
|
||||
path_save($path);
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source']), "users/$name", 'English alias still returned after entering a LOLspeak alias.');
|
||||
// The LOLspeak alias should be returned if we really want LOLspeak.
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source'], 'xx-lolspeak'), 'LOL', 'LOLspeak alias returned if we specify xx-lolspeak to drupal_lookup_path().');
|
||||
|
||||
// Create a new alias for this path in English, which should override the
|
||||
// previous alias for "user/$uid".
|
||||
$path = array(
|
||||
'source' => "user/$uid",
|
||||
'alias' => 'users/my-new-path',
|
||||
'langcode' => 'en',
|
||||
);
|
||||
path_save($path);
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source']), $path['alias'], 'Recently created English alias returned.');
|
||||
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'Recently created English source returned.');
|
||||
|
||||
// Remove the English aliases, which should cause a fallback to the most
|
||||
// recently created language-neutral alias, 'bar'.
|
||||
db_delete('url_alias')
|
||||
->condition('langcode', 'en')
|
||||
->execute();
|
||||
drupal_clear_path_cache();
|
||||
$this->assertEqual(drupal_lookup_path('alias', $path['source']), 'bar', 'Path lookup falls back to recently created language-neutral alias.');
|
||||
|
||||
// Test the situation where the alias and language are the same, but
|
||||
// the source differs. The newer alias record should be returned.
|
||||
$account2 = $this->drupalCreateUser();
|
||||
$path = array(
|
||||
'source' => 'user/' . $account2->uid,
|
||||
'alias' => 'bar',
|
||||
);
|
||||
path_save($path);
|
||||
$this->assertEqual(drupal_lookup_path('source', $path['alias']), $path['source'], 'Newer alias record is returned when comparing two LANGUAGE_NOT_SPECIFIED paths with the same alias.');
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\system\Tests\Path\SaveTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Path;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the path_save() function.
|
||||
*/
|
||||
class SaveTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Enable a helper module that implements hook_path_update().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('path_test');
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => t('Path save'),
|
||||
'description' => t('Tests that path_save() exposes the previous alias value.'),
|
||||
'group' => t('Path API'),
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
path_test_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that path_save() makes the original path available to modules.
|
||||
*/
|
||||
function testDrupalSaveOriginalPath() {
|
||||
$account = $this->drupalCreateUser();
|
||||
$uid = $account->uid;
|
||||
$name = $account->name;
|
||||
|
||||
// Create a language-neutral alias.
|
||||
$path = array(
|
||||
'source' => "user/$uid",
|
||||
'alias' => 'foo',
|
||||
);
|
||||
$path_original = $path;
|
||||
path_save($path);
|
||||
|
||||
// Alter the path.
|
||||
$path['alias'] = 'bar';
|
||||
path_save($path);
|
||||
|
||||
// Test to see if the original alias is available to modules during
|
||||
// hook_path_update().
|
||||
$results = variable_get('path_test_results', array());
|
||||
$this->assertIdentical($results['hook_path_update']['original']['alias'], $path_original['alias'], 'Old path alias available to modules during hook_path_update.');
|
||||
$this->assertIdentical($results['hook_path_update']['original']['source'], $path_original['source'], 'Old path alias available to modules during hook_path_update.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\system\Tests\Path;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
/**
|
||||
* Utility methods to generate sample data, database configuration, etc.
|
||||
*/
|
||||
class UrlAliasFixtures {
|
||||
|
||||
/**
|
||||
* Create the tables required for the sample data.
|
||||
*
|
||||
* @param Drupal\Core\Database\Connection $connection
|
||||
* The connection to use to create the tables.
|
||||
*/
|
||||
public function createTables(Connection $connection) {
|
||||
$tables = $this->urlAliasTableDefinition();
|
||||
$schema = $connection->schema();
|
||||
|
||||
foreach ($tables as $name => $table) {
|
||||
$schema->dropTable($name);
|
||||
$schema->createTable($name, $table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the tables used for the sample data.
|
||||
*
|
||||
* @param Drupal\Core\Database\Connection $connection
|
||||
* The connection to use to drop the tables.
|
||||
*/
|
||||
public function dropTables(Connection $connection) {
|
||||
$tables = $this->urlAliasTableDefinition();
|
||||
$schema = $connection->schema();
|
||||
|
||||
foreach ($tables as $name => $table) {
|
||||
$schema->dropTable($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of URL aliases for testing.
|
||||
*
|
||||
* @return array of URL alias definitions.
|
||||
*/
|
||||
public function sampleUrlAliases() {
|
||||
return array(
|
||||
array(
|
||||
'source' => 'node/1',
|
||||
'alias' => 'alias_for_node_1_en',
|
||||
'langcode' => 'en'
|
||||
),
|
||||
array(
|
||||
'source' => 'node/2',
|
||||
'alias' => 'alias_for_node_2_en',
|
||||
'langcode' => 'en'
|
||||
),
|
||||
array(
|
||||
'source' => 'node/1',
|
||||
'alias' => 'alias_for_node_1_fr',
|
||||
'langcode' => 'fr'
|
||||
),
|
||||
array(
|
||||
'source' => 'node/1',
|
||||
'alias' => 'alias_for_node_1_und',
|
||||
'langcode' => 'und'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the table definition for the URL alias fixtures.
|
||||
*
|
||||
* @return array
|
||||
* Table definitions.
|
||||
*/
|
||||
public function urlAliasTableDefinition() {
|
||||
$tables = array();
|
||||
|
||||
module_load_install('system');
|
||||
$schema = system_schema();
|
||||
|
||||
$tables['url_alias'] = $schema['url_alias'];
|
||||
$tables['key_value'] = $schema['key_value'];
|
||||
|
||||
return $tables;
|
||||
}
|
||||
}
|
|
@ -40,41 +40,43 @@ class UrlAlterFunctionalTest extends WebTestBase {
|
|||
$name = $account->name;
|
||||
|
||||
// Test a single altered path.
|
||||
$this->assertUrlInboundAlter("user/$name", "user/$uid");
|
||||
$this->drupalGet("user/$name");
|
||||
$this->assertResponse('200', 'The user/username path gets resolved correctly');
|
||||
$this->assertUrlOutboundAlter("user/$uid", "user/$name");
|
||||
|
||||
// Test that a path always uses its alias.
|
||||
$path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1');
|
||||
path_save($path);
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias']);
|
||||
$this->assertUrlInboundAlter('alias/test1', "user/$uid/test1");
|
||||
$this->assertUrlOutboundAlter("user/$uid/test1", 'alias/test1');
|
||||
|
||||
// Test that alias source paths are normalized in the interface.
|
||||
$edit = array('source' => "user/$name/edit", 'alias' => 'alias/test2');
|
||||
// Test adding an alias via the UI.
|
||||
$edit = array('source' => "user/$uid/edit", 'alias' => 'alias/test2');
|
||||
$this->drupalPost('admin/config/search/path/add', $edit, t('Save'));
|
||||
$this->assertText(t('The alias has been saved.'));
|
||||
|
||||
// Test that a path always uses its alias.
|
||||
$this->assertUrlInboundAlter('alias/test2', "user/$uid/edit");
|
||||
$this->drupalGet('alias/test2');
|
||||
$this->assertResponse('200', 'The path alias gets resolved correctly');
|
||||
$this->assertUrlOutboundAlter("user/$uid/edit", 'alias/test2');
|
||||
|
||||
// Test a non-existent user is not altered.
|
||||
$uid++;
|
||||
$this->assertUrlInboundAlter("user/$uid", "user/$uid");
|
||||
$this->assertUrlOutboundAlter("user/$uid", "user/$uid");
|
||||
|
||||
// Test that 'forum' is altered to 'community' correctly, both at the root
|
||||
// level and for a specific existing forum.
|
||||
$this->assertUrlInboundAlter('community', 'forum');
|
||||
$this->drupalGet('community');
|
||||
$this->assertText('General discussion', 'The community path gets resolved correctly');
|
||||
$this->assertUrlOutboundAlter('forum', 'community');
|
||||
$forum_vid = config('forum.settings')->get('vocabulary');
|
||||
$term_name = $this->randomName();
|
||||
$tid = db_insert('taxonomy_term_data')
|
||||
->fields(array(
|
||||
'name' => $this->randomName(),
|
||||
'name' => $term_name,
|
||||
'vid' => $forum_vid,
|
||||
))
|
||||
->execute();
|
||||
$this->assertUrlInboundAlter("community/$tid", "forum/$tid");
|
||||
$this->drupalGet("community/$tid");
|
||||
$this->assertText($term_name, 'The community/{tid} path gets resolved correctly');
|
||||
$this->assertUrlOutboundAlter("forum/$tid", "community/$tid");
|
||||
}
|
||||
|
||||
|
@ -87,14 +89,6 @@ class UrlAlterFunctionalTest extends WebTestBase {
|
|||
$this->assertRaw('current_path=url-alter-test/foo', 'current_path() returns the internal path.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that current_path() is initialized when the request path is empty.
|
||||
*/
|
||||
function testGetQInitialized() {
|
||||
$this->drupalGet('');
|
||||
$this->assertText("current_path() is non-empty with an empty request path.", 'current_path() is initialized with an empty request path.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an outbound path is altered to an expected value.
|
||||
*
|
||||
|
@ -118,7 +112,7 @@ class UrlAlterFunctionalTest extends WebTestBase {
|
|||
*
|
||||
* @param $original
|
||||
* A string with the aliased or un-normal path that is run through
|
||||
* drupal_get_normal_path().
|
||||
* drupal_container()->get('path.alias_manager')->getSystemPath().
|
||||
* @param $final
|
||||
* A string with the expected result after url().
|
||||
* @return
|
||||
|
@ -126,7 +120,7 @@ class UrlAlterFunctionalTest extends WebTestBase {
|
|||
*/
|
||||
protected function assertUrlInboundAlter($original, $final) {
|
||||
// Test inbound altering.
|
||||
$result = drupal_get_normal_path($original);
|
||||
$result = drupal_container()->get('path.alias_manager')->getSystemPath($original);
|
||||
$this->assertIdentical($result, $final, format_string('Altered inbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1410,10 +1410,11 @@ function system_site_information_settings($form, &$form_state) {
|
|||
'#type' => 'fieldset',
|
||||
'#title' => t('Front page'),
|
||||
);
|
||||
$front_page = $site_config->get('page.front') != 'user' ? drupal_container()->get('path.alias_manager')->getPathAlias($site_config->get('page.front')) : '';
|
||||
$form['front_page']['site_frontpage'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Default front page'),
|
||||
'#default_value' => ($site_config->get('page.front') != 'user' ? drupal_get_path_alias($site_config->get('page.front')) : ''),
|
||||
'#default_value' => $front_page,
|
||||
'#size' => 40,
|
||||
'#description' => t('Optionally, specify a relative URL to display as the front page. Leave blank to display the default front page.'),
|
||||
'#field_prefix' => url(NULL, array('absolute' => TRUE)),
|
||||
|
@ -1455,7 +1456,7 @@ function system_site_information_settings_validate($form, &$form_state) {
|
|||
}
|
||||
else {
|
||||
// Get the normal path of the front page.
|
||||
form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state);
|
||||
form_set_value($form['front_page']['site_frontpage'], drupal_container()->get('path.alias_manager')->getSystemPath($form_state['values']['site_frontpage']), $form_state);
|
||||
}
|
||||
// Validate front page path.
|
||||
if (!drupal_valid_path($form_state['values']['site_frontpage'])) {
|
||||
|
@ -1463,10 +1464,10 @@ function system_site_information_settings_validate($form, &$form_state) {
|
|||
}
|
||||
// Get the normal paths of both error pages.
|
||||
if (!empty($form_state['values']['site_403'])) {
|
||||
form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state);
|
||||
form_set_value($form['error_page']['site_403'], drupal_container()->get('path.alias_manager')->getSystemPath($form_state['values']['site_403']), $form_state);
|
||||
}
|
||||
if (!empty($form_state['values']['site_404'])) {
|
||||
form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state);
|
||||
form_set_value($form['error_page']['site_404'], drupal_container()->get('path.alias_manager')->getSystemPath($form_state['values']['site_404']), $form_state);
|
||||
}
|
||||
// Validate 403 error path.
|
||||
if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) {
|
||||
|
|
|
@ -3476,7 +3476,7 @@ function hook_system_themes_page_alter(&$theme_groups) {
|
|||
* @param $path_language
|
||||
* The language of the path.
|
||||
*
|
||||
* @see drupal_get_normal_path()
|
||||
* @see \Drupal\Core\Path\AliasManager::getSystemPath()
|
||||
*/
|
||||
function hook_url_inbound_alter(&$path, $original_path, $path_language) {
|
||||
// Create the path user/me/edit, which allows a user to edit their account.
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\url_alter_test\PathSubscriber.
|
||||
*/
|
||||
|
||||
namespace Drupal\url_alter_test;
|
||||
|
||||
use Drupal\Core\EventSubscriber\PathListenerBase;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Path subscriber for url_alter_test.
|
||||
*/
|
||||
class PathSubscriber extends PathListenerBase implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* Resolve the system path based on some arbitrary rules.
|
||||
*
|
||||
* @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function onKernelRequestPathResolve(GetResponseEvent $event) {
|
||||
$request = $event->getRequest();
|
||||
$path = $this->extractPath($request);
|
||||
// Rewrite user/username to user/uid.
|
||||
if (preg_match('!^user/([^/]+)(/.*)?!', $path, $matches)) {
|
||||
if ($account = user_load_by_name($matches[1])) {
|
||||
$matches += array(2 => '');
|
||||
$path = 'user/' . $account->uid . $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite community/ to forum/.
|
||||
if ($path == 'community' || strpos($path, 'community/') === 0) {
|
||||
$path = 'forum' . substr($path, 9);
|
||||
}
|
||||
|
||||
if ($path == 'url-alter-test/bar') {
|
||||
$path = 'url-alter-test/foo';
|
||||
}
|
||||
|
||||
$this->setPath($request, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[KernelEvents::REQUEST][] = array('onKernelRequestPathResolve', 100);
|
||||
return $events;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\url_alter_test\UrlAlterTestBundle.
|
||||
*/
|
||||
|
||||
namespace Drupal\url_alter_test;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
/**
|
||||
* Test bundle class for url_alter_test.
|
||||
*
|
||||
* Used to register an event subscriber that resolves a path alias to a system
|
||||
* path based on an arbitrary set of rules.
|
||||
*
|
||||
* @see \Drupal\url_alter_test\PathSubscriber
|
||||
*/
|
||||
class UrlAlterTestBundle extends Bundle
|
||||
{
|
||||
public function build(ContainerBuilder $container) {
|
||||
$container->register('url_alter_test.path_subscriber', 'Drupal\url_alter_test\PathSubscriber')
|
||||
->addTag('event_subscriber');
|
||||
}
|
||||
}
|
|
@ -26,32 +26,6 @@ function url_alter_test_foo() {
|
|||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_url_inbound_alter().
|
||||
*/
|
||||
function url_alter_test_url_inbound_alter(&$path, $original_path, $path_language) {
|
||||
if (!request_path() && current_path()) {
|
||||
drupal_set_message("current_path() is non-empty with an empty request path.");
|
||||
}
|
||||
|
||||
// Rewrite user/username to user/uid.
|
||||
if (preg_match('!^user/([^/]+)(/.*)?!', $path, $matches)) {
|
||||
if ($account = user_load_by_name($matches[1])) {
|
||||
$matches += array(2 => '');
|
||||
$path = 'user/' . $account->uid . $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
// Rewrite community/ to forum/.
|
||||
if ($path == 'community' || strpos($path, 'community/') === 0) {
|
||||
$path = 'forum' . substr($path, 9);
|
||||
}
|
||||
|
||||
if ($path == 'url-alter-test/bar') {
|
||||
$path = 'url-alter-test/foo';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_url_outbound_alter().
|
||||
*/
|
||||
|
|
|
@ -718,7 +718,7 @@ function views_ui_views_analyze($view) {
|
|||
continue;
|
||||
}
|
||||
if ($display->hasPath() && $path = $display->getOption('path')) {
|
||||
$normal_path = drupal_get_normal_path($path);
|
||||
$normal_path = drupal_container()->get('path.alias_manager')->getSystemPath($path);
|
||||
if ($path != $normal_path) {
|
||||
$ret[] = Analyzer::formatMessage(t('You have configured display %display with a path which is an path alias as well. This might lead to unwanted effects so better use an internal path.', array('%display' => $display['display_title'])), 'warning');
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ for ($i = 0; $i < 12; $i++) {
|
|||
'alias' => "content/poll/$i/results",
|
||||
'source' => "node/$node->nid/results",
|
||||
);
|
||||
path_save($path);
|
||||
drupal_container()->get('path.crud')->save($path['source'], $path['alias']);
|
||||
|
||||
// Add some votes
|
||||
$node = node_load($node->nid);
|
||||
|
|
Loading…
Reference in New Issue