/[drupal]/drupal/includes/path.inc
ViewVC logotype

Contents of /drupal/includes/path.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.47 - (show annotations) (download) (as text)
Sat Oct 24 05:13:43 2009 UTC (4 weeks, 6 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10
Changes since 1.46: +17 -10 lines
File MIME type: text/x-php
#320331 by Dave Reid, dww, John Morahan, cwgordon7, moshe weitzman, c960657, and smoothify: Turn custom_url_rewrite_inbound() and custom_url_rewrite_outbound() into hooks.
1 <?php
2 // $Id: path.inc,v 1.46 2009/10/22 01:27:18 dries Exp $
3
4 /**
5 * @file
6 * Functions to handle paths in Drupal, including path aliasing.
7 *
8 * These functions are not loaded for cached pages, but modules that need
9 * to use them in hook_boot() or hook exit() can make them available, by
10 * executing "drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);".
11 */
12
13 /**
14 * Initialize the $_GET['q'] variable to the proper normal path.
15 */
16 function drupal_path_initialize() {
17 if (!empty($_GET['q'])) {
18 $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));
19 }
20 else {
21 $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
22 }
23 }
24
25 /**
26 * Given an alias, return its Drupal system URL if one exists. Given a Drupal
27 * system URL return one of its aliases if such a one exists. Otherwise,
28 * return FALSE.
29 *
30 * @param $action
31 * One of the following values:
32 * - wipe: delete the alias cache.
33 * - alias: return an alias for a given Drupal system path (if one exists).
34 * - source: return the Drupal system URL for a path alias (if one exists).
35 * @param $path
36 * The path to investigate for corresponding aliases or system URLs.
37 * @param $path_language
38 * Optional language code to search the path with. Defaults to the page language.
39 * If there's no path defined for that language it will search paths without
40 * language.
41 *
42 * @return
43 * Either a Drupal system path, an aliased path, or FALSE if no path was
44 * found.
45 */
46 function drupal_lookup_path($action, $path = '', $path_language = '') {
47 global $language;
48 $cache = &drupal_static(__FUNCTION__, array(
49 'map' => array(),
50 'no_source' => array(),
51 'whitelist' => NULL,
52 'system_paths' => array(),
53 'no_aliases' => array(),
54 'first_call' => TRUE,
55 ));
56
57 // Retrieve the path alias whitelist.
58 if (!isset($cache['whitelist'])) {
59 $cache['whitelist'] = variable_get('path_alias_whitelist', NULL);
60 if (!isset($cache['whitelist'])) {
61 $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
62 }
63 }
64
65 $path_language = $path_language ? $path_language : $language->language;
66
67 if ($action == 'wipe') {
68 $cache = array();
69 $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
70 }
71 elseif ($cache['whitelist'] && $path != '') {
72 if ($action == 'alias') {
73 // During the first call to drupal_lookup_path() per language, load the
74 // expected system paths for the page from cache.
75 if (!empty($cache['first_call'])) {
76 $cache['first_call'] = FALSE;
77
78 $cache['map'][$path_language] = array();
79 // Load system paths from cache.
80 $cid = current_path();
81 if ($cached = cache_get($cid, 'cache_path')) {
82 $cache['system_paths'] = $cached->data;
83 // Now fetch the aliases corresponding to these system paths.
84 // We order by ASC and overwrite array keys to ensure the correct
85 // alias is used when there are multiple aliases per path.
86 $cache['map'][$path_language] = db_query("SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND language IN (:language, '') ORDER BY language ASC, pid ASC", array(
87 ':system' => $cache['system_paths'],
88 ':language' => $path_language
89 ))->fetchAllKeyed();
90 // Keep a record of paths with no alias to avoid querying twice.
91 $cache['no_aliases'][$path_language] = array_flip(array_diff_key($cache['system_paths'], array_keys($cache['map'][$path_language])));
92 }
93 }
94 // If the alias has already been loaded, return it.
95 if (isset($cache['map'][$path_language][$path])) {
96 return $cache['map'][$path_language][$path];
97 }
98 // Check the path whitelist, if the top_level part before the first /
99 // is not in the list, then there is no need to do anything further,
100 // it is not in the database.
101 elseif (!isset($cache['whitelist'][strtok($path, '/')])) {
102 return FALSE;
103 }
104 // For system paths which were not cached, query aliases individually.
105 else if (!isset($cache['no_aliases'][$path_language][$path])) {
106 // Get the most fitting result falling back with alias without language
107 $alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND language IN (:language, '') ORDER BY language DESC, pid DESC", array(
108 ':source' => $path,
109 ':language' => $path_language
110 ))->fetchField();
111 $cache['map'][$path_language][$path] = $alias;
112 return $alias;
113 }
114 }
115 // Check $no_source for this $path in case we've already determined that there
116 // isn't a path that has this alias
117 elseif ($action == 'source' && !isset($cache['no_source'][$path_language][$path])) {
118 // Look for the value $path within the cached $map
119 $source = '';
120 if (!isset($cache['map'][$path_language]) || !($source = array_search($path, $cache['map'][$path_language]))) {
121 // Get the most fitting result falling back with alias without language
122 if ($source = db_query("SELECT source FROM {url_alias} WHERE alias = :alias AND language IN (:language, '') ORDER BY language DESC, pid DESC", array(
123 ':alias' => $path,
124 ':language' => $path_language))
125 ->fetchField()) {
126 $cache['map'][$path_language][$source] = $path;
127 }
128 else {
129 // We can't record anything into $map because we do not have a valid
130 // index and there is no need because we have not learned anything
131 // about any Drupal path. Thus cache to $no_source.
132 $cache['no_source'][$path_language][$path] = TRUE;
133 }
134 }
135 return $source;
136 }
137 }
138
139 return FALSE;
140 }
141
142 /**
143 * Cache system paths for a page.
144 *
145 * Cache an array of the system paths available on each page. We assume
146 * that aiases will be needed for the majority of these paths during
147 * subsequent requests, and load them in a single query during
148 * drupal_lookup_path().
149 */
150 function drupal_cache_system_paths() {
151 // Check if the system paths for this page were loaded from cache in this
152 // request to avoid writing to cache on every request.
153 $cache = &drupal_static('drupal_lookup_path', array());
154 if (!$cache['system_paths']) {
155 // Generate a cache ID (cid) specifically for this page.
156 $cid = current_path();
157 // The static $map array used by drupal_lookup_path() includes all
158 // system paths for the page request.
159 if ($paths = current($cache['map'])) {
160 $data = array_keys($paths);
161 $expire = REQUEST_TIME + (60 * 60 * 24);
162 cache_set($cid, $data, 'cache_path', $expire);
163 }
164 }
165 }
166
167 /**
168 * Given an internal Drupal path, return the alias set by the administrator.
169 *
170 * If no path is provided, the function will return the alias of the current
171 * page.
172 *
173 * @param $path
174 * An internal Drupal path.
175 * @param $path_language
176 * An optional language code to look up the path in.
177 *
178 * @return
179 * An aliased path if one was found, or the original path if no alias was
180 * found.
181 */
182 function drupal_get_path_alias($path = NULL, $path_language = '') {
183 // If no path is specified, use the current page's path.
184 if ($path == NULL) {
185 $path = $_GET['q'];
186 }
187 $result = $path;
188 if ($alias = drupal_lookup_path('alias', $path, $path_language)) {
189 $result = $alias;
190 }
191 return $result;
192 }
193
194 /**
195 * Given a path alias, return the internal path it represents.
196 *
197 * @param $path
198 * A Drupal path alias.
199 * @param $path_language
200 * An optional language code to look up the path in.
201 *
202 * @return
203 * The internal path represented by the alias, or the original alias if no
204 * internal path was found.
205 */
206 function drupal_get_normal_path($path, $path_language = '') {
207 $original_path = $path;
208
209 // Lookup the path alias first.
210 if ($source = drupal_lookup_path('source', $path, $path_language)) {
211 $path = $source;
212 }
213
214 // Allow other modules to alter the inbound URL. We cannot use drupal_alter()
215 // here because we need to run hook_url_inbound_alter() in the reverse order
216 // of hook_url_outbound_alter().
217 foreach (array_reverse(module_implements('url_inbound_alter')) as $module) {
218 $function = $module . '_url_inbound_alter';
219 $function($path, $original_path, $path_language);
220 }
221
222 return $path;
223 }
224
225 /**
226 * Return a component of the current Drupal path.
227 *
228 * When viewing a page at the path "admin/structure/types", for example, arg(0)
229 * returns "admin", arg(1) returns "content", and arg(2) returns "types".
230 *
231 * Avoid use of this function where possible, as resulting code is hard to read.
232 * In menu callback functions, attempt to use named arguments. See the explanation
233 * in menu.inc for how to construct callbacks that take arguments. When attempting
234 * to use this function to load an element from the current path, e.g. loading the
235 * node on a node page, please use menu_get_object() instead.
236 *
237 * @param $index
238 * The index of the component, where each component is separated by a '/'
239 * (forward-slash), and where the first component has an index of 0 (zero).
240 * @param $path
241 * A path to break into components. Defaults to the path of the current page.
242 *
243 * @return
244 * The component specified by $index, or NULL if the specified component was
245 * not found.
246 */
247 function arg($index = NULL, $path = NULL) {
248 $arguments = &drupal_static(__FUNCTION__);
249
250 if (!isset($path)) {
251 $path = $_GET['q'];
252 }
253 if (!isset($arguments[$path])) {
254 $arguments[$path] = explode('/', $path);
255 }
256 if (!isset($index)) {
257 return $arguments[$path];
258 }
259 if (isset($arguments[$path][$index])) {
260 return $arguments[$path][$index];
261 }
262 }
263
264 /**
265 * Get the title of the current page, for display on the page and in the title bar.
266 *
267 * @return
268 * The current page's title.
269 */
270 function drupal_get_title() {
271 $title = drupal_set_title();
272
273 // During a bootstrap, menu.inc is not included and thus we cannot provide a title.
274 if (!isset($title) && function_exists('menu_get_active_title')) {
275 $title = check_plain(menu_get_active_title());
276 }
277
278 return $title;
279 }
280
281 /**
282 * Set the title of the current page, for display on the page and in the title bar.
283 *
284 * @param $title
285 * Optional string value to assign to the page title; or if set to NULL
286 * (default), leaves the current title unchanged.
287 * @param $output
288 * Optional flag - normally should be left as CHECK_PLAIN. Only set to
289 * PASS_THROUGH if you have already removed any possibly dangerous code
290 * from $title using a function like check_plain() or filter_xss(). With this
291 * flag the string will be passed through unchanged.
292 *
293 * @return
294 * The updated title of the current page.
295 */
296 function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
297 $stored_title = &drupal_static(__FUNCTION__);
298
299 if (isset($title)) {
300 $stored_title = ($output == PASS_THROUGH) ? $title : check_plain($title);
301 }
302
303 return $stored_title;
304 }
305
306 /**
307 * Check if the current page is the front page.
308 *
309 * @return
310 * Boolean value: TRUE if the current page is the front page; FALSE if otherwise.
311 */
312 function drupal_is_front_page() {
313 $is_front_page = &drupal_static(__FUNCTION__);
314
315 if (!isset($is_front_page)) {
316 // As drupal_path_initialize updates $_GET['q'] with the 'site_frontpage' path,
317 // we can check it against the 'site_frontpage' variable.
318 $is_front_page = ($_GET['q'] == drupal_get_normal_path(variable_get('site_frontpage', 'node')));
319 }
320
321 return $is_front_page;
322 }
323
324 /**
325 * Check if a path matches any pattern in a set of patterns.
326 *
327 * @param $path
328 * The path to match.
329 * @param $patterns
330 * String containing a set of patterns separated by \n, \r or \r\n.
331 *
332 * @return
333 * Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
334 */
335 function drupal_match_path($path, $patterns) {
336 $regexps = &drupal_static(__FUNCTION__);
337
338 if (!isset($regexps[$patterns])) {
339 $regexps[$patterns] = '/^(' . preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2'), preg_quote($patterns, '/')) . ')$/';
340 }
341 return (bool)preg_match($regexps[$patterns], $path);
342 }
343
344 /**
345 * Return the current URL path of the page being viewed.
346 *
347 * Examples:
348 * - http://example.com/node/306 returns "node/306".
349 * - http://example.com/drupalfolder/node/306 returns "node/306" while
350 * base_path() returns "/drupalfolder/".
351 * - http://example.com/path/alias (which is a path alias for node/306) returns
352 * "node/306" as opposed to the path alias.
353 *
354 * This function is not available in hook_boot() so use $_GET['q'] instead.
355 * However, be careful when doing that because in the case of Example #3
356 * $_GET['q'] will contain "path/alias". If "node/306" is needed, calling
357 * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available.
358 *
359 * @return
360 * The current Drupal URL path.
361 */
362 function current_path() {
363 return $_GET['q'];
364 }
365
366 /**
367 * Rebuild the path alias white list.
368 *
369 * @return
370 * An array containing a white list of path aliases.
371 */
372 function drupal_path_alias_whitelist_rebuild() {
373 // For each alias in the database, get the top level component of the system
374 // path it corresponds to. This is the portion of the path before the first /
375 // if present, otherwise the whole path itself.
376 $whitelist = array();
377 $result = db_query("SELECT SUBSTRING_INDEX(source, '/', 1) AS path FROM {url_alias} GROUP BY path");
378 foreach ($result as $row) {
379 $whitelist[$row->path] = TRUE;
380 }
381 variable_set('path_alias_whitelist', $whitelist);
382 return $whitelist;
383 }
384
385 /**
386 * Fetch a specific URL alias from the database.
387 *
388 * @param $conditions
389 * A string representing the source, a number representing the pid, or an
390 * array of query conditions.
391 *
392 * @return
393 * FALSE if no alias was found or an associative array containing the
394 * following keys:
395 * - source: The internal system path.
396 * - alias: The URL alias.
397 * - pid: Unique path alias identifier.
398 * - language: The language of the alias.
399 */
400 function path_load($conditions) {
401 if (is_numeric($conditions)) {
402 $conditions = array('pid' => $conditions);
403 }
404 elseif (is_string($conditions)) {
405 $conditions = array('source' => $conditions);
406 }
407 elseif (!is_array($conditions)) {
408 return FALSE;
409 }
410 $select = db_select('url_alias');
411 foreach ($conditions as $field => $value) {
412 $select->condition($field, $value);
413 }
414 return $select
415 ->fields('url_alias')
416 ->execute()
417 ->fetchAssoc();
418 }
419
420 /**
421 * Save a path alias to the database.
422 *
423 * @param $path
424 * An associative array containing the following keys:
425 * - source: The internal system path.
426 * - alias: The URL alias.
427 * - pid: (optional) Unique path alias identifier.
428 * - language: (optional) The language of the alias.
429 */
430 function path_save(&$path) {
431 $path += array('pid' => NULL, 'language' => '');
432
433 // Insert or update the alias.
434 $status = drupal_write_record('url_alias', $path, (!empty($path['pid']) ? 'pid' : NULL));
435
436 // Verify that a record was written.
437 if (isset($status) && $status) {
438 if ($status === SAVED_NEW) {
439 module_invoke_all('path_insert', $path);
440 }
441 else {
442 module_invoke_all('path_update', $path);
443 }
444 drupal_clear_path_cache();
445 }
446 }
447
448 /**
449 * Delete a URL alias.
450 *
451 * @param $criteria
452 * A number representing the pid or an array of criteria.
453 */
454 function path_delete($criteria) {
455 if (!is_array($criteria)) {
456 $criteria = array('pid' => $criteria);
457 }
458 $path = path_load($criteria);
459 $query = db_delete('url_alias');
460 foreach ($criteria as $field => $value) {
461 $query->condition($field, $value);
462 }
463 $query->execute();
464 module_invoke_all('path_delete', $path);
465 drupal_clear_path_cache();
466 }
467

  ViewVC Help
Powered by ViewVC 1.1.2