1 |
85ad3d82
|
Assos Assos
|
<?php
|
2 |
|
|
|
3 |
|
|
/**
|
4 |
|
|
* @file
|
5 |
|
|
* Miscellaneous functions for Pathauto.
|
6 |
|
|
*
|
7 |
|
|
* This also contains some constants giving human readable names to some numeric
|
8 |
|
|
* settings; they're included here as they're only rarely used outside this file
|
9 |
|
|
* anyway. Use module_load_include('inc', 'pathauto') if the constants need to
|
10 |
|
|
* be available.
|
11 |
|
|
*
|
12 |
|
|
* @ingroup pathauto
|
13 |
|
|
*/
|
14 |
|
|
|
15 |
|
|
/**
|
16 |
|
|
* Case should be left as is in the generated path.
|
17 |
|
|
*/
|
18 |
|
|
define('PATHAUTO_CASE_LEAVE_ASIS', 0);
|
19 |
|
|
|
20 |
|
|
/**
|
21 |
|
|
* Case should be lowercased in the generated path.
|
22 |
|
|
*/
|
23 |
|
|
define('PATHAUTO_CASE_LOWER', 1);
|
24 |
|
|
|
25 |
|
|
/**
|
26 |
|
|
* "Do nothing. Leave the old alias intact."
|
27 |
|
|
*/
|
28 |
|
|
define('PATHAUTO_UPDATE_ACTION_NO_NEW', 0);
|
29 |
|
|
|
30 |
|
|
/**
|
31 |
|
|
* "Create a new alias. Leave the existing alias functioning."
|
32 |
|
|
*/
|
33 |
|
|
define('PATHAUTO_UPDATE_ACTION_LEAVE', 1);
|
34 |
|
|
|
35 |
|
|
/**
|
36 |
|
|
* "Create a new alias. Delete the old alias."
|
37 |
|
|
*/
|
38 |
|
|
define('PATHAUTO_UPDATE_ACTION_DELETE', 2);
|
39 |
|
|
|
40 |
|
|
/**
|
41 |
|
|
* Remove the punctuation from the alias.
|
42 |
|
|
*/
|
43 |
|
|
define('PATHAUTO_PUNCTUATION_REMOVE', 0);
|
44 |
|
|
|
45 |
|
|
/**
|
46 |
|
|
* Replace the punctuation with the separator in the alias.
|
47 |
|
|
*/
|
48 |
|
|
define('PATHAUTO_PUNCTUATION_REPLACE', 1);
|
49 |
|
|
|
50 |
|
|
/**
|
51 |
|
|
* Leave the punctuation as it is in the alias.
|
52 |
|
|
*/
|
53 |
|
|
define('PATHAUTO_PUNCTUATION_DO_NOTHING', 2);
|
54 |
|
|
|
55 |
|
|
/**
|
56 |
|
|
* Check to see if there is already an alias pointing to a different item.
|
57 |
|
|
*
|
58 |
|
|
* @param $alias
|
59 |
|
|
* A string alias.
|
60 |
|
|
* @param $source
|
61 |
|
|
* A string that is the internal path.
|
62 |
|
|
* @param $language
|
63 |
|
|
* A string indicating the path's language.
|
64 |
|
|
* @return
|
65 |
|
|
* TRUE if an alias exists, FALSE if not.
|
66 |
|
|
*/
|
67 |
|
|
function _pathauto_alias_exists($alias, $source, $language = LANGUAGE_NONE) {
|
68 |
|
|
$pid = db_query_range("SELECT pid FROM {url_alias} WHERE source <> :source AND alias = :alias AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", 0, 1, array(
|
69 |
|
|
':source' => $source,
|
70 |
|
|
':alias' => $alias,
|
71 |
|
|
':language' => $language,
|
72 |
|
|
':language_none' => LANGUAGE_NONE,
|
73 |
|
|
))->fetchField();
|
74 |
|
|
|
75 |
|
|
return !empty($pid);
|
76 |
|
|
}
|
77 |
|
|
|
78 |
|
|
/**
|
79 |
|
|
* Fetches an existing URL alias given a path and optional language.
|
80 |
|
|
*
|
81 |
|
|
* @param $source
|
82 |
|
|
* An internal Drupal path.
|
83 |
|
|
* @param $language
|
84 |
|
|
* An optional language code to look up the path in.
|
85 |
|
|
* @return
|
86 |
|
|
* FALSE if no alias was found or an associative array containing the
|
87 |
|
|
* following keys:
|
88 |
|
|
* - pid: Unique path alias identifier.
|
89 |
|
|
* - alias: The URL alias.
|
90 |
|
|
*/
|
91 |
|
|
function _pathauto_existing_alias_data($source, $language = LANGUAGE_NONE) {
|
92 |
|
|
$pid = db_query_range("SELECT pid FROM {url_alias} WHERE source = :source AND language IN (:language, :language_none) ORDER BY language DESC, pid DESC", 0, 1, array(':source' => $source, ':language' => $language, ':language_none' => LANGUAGE_NONE))->fetchField();
|
93 |
|
|
return path_load(array('pid' => $pid));
|
94 |
|
|
}
|
95 |
|
|
|
96 |
|
|
/**
|
97 |
|
|
* Clean up a string segment to be used in an URL alias.
|
98 |
|
|
*
|
99 |
|
|
* Performs the following possible alterations:
|
100 |
|
|
* - Remove all HTML tags.
|
101 |
|
|
* - Process the string through the transliteration module.
|
102 |
|
|
* - Replace or remove punctuation with the separator character.
|
103 |
|
|
* - Remove back-slashes.
|
104 |
|
|
* - Replace non-ascii and non-numeric characters with the separator.
|
105 |
|
|
* - Remove common words.
|
106 |
|
|
* - Replace whitespace with the separator character.
|
107 |
|
|
* - Trim duplicate, leading, and trailing separators.
|
108 |
|
|
* - Convert to lower-case.
|
109 |
|
|
* - Shorten to a desired length and logical position based on word boundaries.
|
110 |
|
|
*
|
111 |
|
|
* This function should *not* be called on URL alias or path strings because it
|
112 |
|
|
* is assumed that they are already clean.
|
113 |
|
|
*
|
114 |
|
|
* @param $string
|
115 |
|
|
* A string to clean.
|
116 |
|
|
* @return
|
117 |
|
|
* The cleaned string.
|
118 |
|
|
*/
|
119 |
|
|
function pathauto_cleanstring($string) {
|
120 |
|
|
// Use the advanced drupal_static() pattern, since this is called very often.
|
121 |
|
|
static $drupal_static_fast;
|
122 |
|
|
if (!isset($drupal_static_fast)) {
|
123 |
|
|
$drupal_static_fast['cache'] = &drupal_static(__FUNCTION__);
|
124 |
|
|
}
|
125 |
|
|
$cache = &$drupal_static_fast['cache'];
|
126 |
|
|
|
127 |
|
|
// Generate and cache variables used in this function so that on the second
|
128 |
|
|
// call to pathauto_cleanstring() we focus on processing.
|
129 |
|
|
if (!isset($cache)) {
|
130 |
|
|
$cache = array(
|
131 |
|
|
'separator' => variable_get('pathauto_separator', '-'),
|
132 |
|
|
'strings' => array(),
|
133 |
|
|
'transliterate' => variable_get('pathauto_transliterate', FALSE) && module_exists('transliteration'),
|
134 |
|
|
'punctuation' => array(),
|
135 |
|
|
'reduce_ascii' => (bool) variable_get('pathauto_reduce_ascii', FALSE),
|
136 |
|
|
'ignore_words_regex' => FALSE,
|
137 |
|
|
'lowercase' => (bool) variable_get('pathauto_case', PATHAUTO_CASE_LOWER),
|
138 |
|
|
'maxlength' => min(variable_get('pathauto_max_component_length', 100), _pathauto_get_schema_alias_maxlength()),
|
139 |
|
|
);
|
140 |
|
|
|
141 |
|
|
// Generate and cache the punctuation replacements for strtr().
|
142 |
|
|
$punctuation = pathauto_punctuation_chars();
|
143 |
|
|
foreach ($punctuation as $name => $details) {
|
144 |
|
|
$action = variable_get('pathauto_punctuation_' . $name, PATHAUTO_PUNCTUATION_REMOVE);
|
145 |
|
|
switch ($action) {
|
146 |
|
|
case PATHAUTO_PUNCTUATION_REMOVE:
|
147 |
|
|
$cache['punctuation'][$details['value']] = '';
|
148 |
|
|
break;
|
149 |
|
|
case PATHAUTO_PUNCTUATION_REPLACE:
|
150 |
|
|
$cache['punctuation'][$details['value']] = $cache['separator'];
|
151 |
|
|
break;
|
152 |
|
|
case PATHAUTO_PUNCTUATION_DO_NOTHING:
|
153 |
|
|
// Literally do nothing.
|
154 |
|
|
break;
|
155 |
|
|
}
|
156 |
|
|
}
|
157 |
|
|
|
158 |
|
|
// Generate and cache the ignored words regular expression.
|
159 |
|
|
$ignore_words = variable_get('pathauto_ignore_words', PATHAUTO_IGNORE_WORDS);
|
160 |
|
|
$ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words);
|
161 |
|
|
if ($ignore_words_regex) {
|
162 |
|
|
$cache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b';
|
163 |
|
|
if (function_exists('mb_eregi_replace')) {
|
164 |
|
|
$cache['ignore_words_callback'] = 'mb_eregi_replace';
|
165 |
|
|
}
|
166 |
|
|
else {
|
167 |
|
|
$cache['ignore_words_callback'] = 'preg_replace';
|
168 |
|
|
$cache['ignore_words_regex'] = '/' . $cache['ignore_words_regex'] . '/i';
|
169 |
|
|
}
|
170 |
|
|
}
|
171 |
|
|
}
|
172 |
|
|
|
173 |
|
|
// Empty strings do not need any proccessing.
|
174 |
|
|
if ($string === '' || $string === NULL) {
|
175 |
|
|
return '';
|
176 |
|
|
}
|
177 |
|
|
|
178 |
|
|
// Check if the string has already been processed, and if so return the
|
179 |
|
|
// cached result.
|
180 |
|
|
if (isset($cache['strings'][$string])) {
|
181 |
|
|
return $cache['strings'][$string];
|
182 |
|
|
}
|
183 |
|
|
|
184 |
|
|
// Remove all HTML tags from the string.
|
185 |
|
|
$output = strip_tags(decode_entities($string));
|
186 |
|
|
|
187 |
|
|
// Optionally transliterate (by running through the Transliteration module)
|
188 |
|
|
if ($cache['transliterate']) {
|
189 |
|
|
$output = transliteration_get($output);
|
190 |
|
|
}
|
191 |
|
|
|
192 |
|
|
// Replace or drop punctuation based on user settings
|
193 |
|
|
$output = strtr($output, $cache['punctuation']);
|
194 |
|
|
|
195 |
|
|
// Reduce strings to letters and numbers
|
196 |
|
|
if ($cache['reduce_ascii']) {
|
197 |
|
|
$output = preg_replace('/[^a-zA-Z0-9\/]+/', $cache['separator'], $output);
|
198 |
|
|
}
|
199 |
|
|
|
200 |
|
|
// Get rid of words that are on the ignore list
|
201 |
|
|
if ($cache['ignore_words_regex']) {
|
202 |
|
|
$words_removed = $cache['ignore_words_callback']($cache['ignore_words_regex'], '', $output);
|
203 |
|
|
if (drupal_strlen(trim($words_removed)) > 0) {
|
204 |
|
|
$output = $words_removed;
|
205 |
|
|
}
|
206 |
|
|
}
|
207 |
|
|
|
208 |
|
|
// Always replace whitespace with the separator.
|
209 |
|
|
$output = preg_replace('/\s+/', $cache['separator'], $output);
|
210 |
|
|
|
211 |
|
|
// Trim duplicates and remove trailing and leading separators.
|
212 |
|
|
$output = _pathauto_clean_separators($output, $cache['separator']);
|
213 |
|
|
|
214 |
|
|
// Optionally convert to lower case.
|
215 |
|
|
if ($cache['lowercase']) {
|
216 |
|
|
$output = drupal_strtolower($output);
|
217 |
|
|
}
|
218 |
|
|
|
219 |
|
|
// Shorten to a logical place based on word boundaries.
|
220 |
|
|
$output = truncate_utf8($output, $cache['maxlength'], TRUE);
|
221 |
|
|
|
222 |
|
|
// Cache this result in the static array.
|
223 |
|
|
$cache['strings'][$string] = $output;
|
224 |
|
|
|
225 |
|
|
return $output;
|
226 |
|
|
}
|
227 |
|
|
|
228 |
|
|
/**
|
229 |
|
|
* Trims duplicate, leading, and trailing separators from a string.
|
230 |
|
|
*
|
231 |
|
|
* @param $string
|
232 |
|
|
* The string to clean path separators from.
|
233 |
|
|
* @param $separator
|
234 |
|
|
* The path separator to use when cleaning.
|
235 |
|
|
* @return
|
236 |
|
|
* The cleaned version of the string.
|
237 |
|
|
*
|
238 |
|
|
* @see pathauto_cleanstring()
|
239 |
|
|
* @see pathauto_clean_alias()
|
240 |
|
|
*/
|
241 |
|
|
function _pathauto_clean_separators($string, $separator = NULL) {
|
242 |
|
|
static $default_separator;
|
243 |
|
|
|
244 |
|
|
if (!isset($separator)) {
|
245 |
|
|
if (!isset($default_separator)) {
|
246 |
|
|
$default_separator = variable_get('pathauto_separator', '-');
|
247 |
|
|
}
|
248 |
|
|
$separator = $default_separator;
|
249 |
|
|
}
|
250 |
|
|
|
251 |
|
|
$output = $string;
|
252 |
|
|
|
253 |
|
|
// Clean duplicate or trailing separators.
|
254 |
|
|
if (strlen($separator)) {
|
255 |
|
|
// Escape the separator.
|
256 |
|
|
$seppattern = preg_quote($separator, '/');
|
257 |
|
|
|
258 |
|
|
// Trim any leading or trailing separators.
|
259 |
|
|
$output = preg_replace("/^$seppattern+|$seppattern+$/", '', $output);
|
260 |
|
|
|
261 |
|
|
// Replace trailing separators around slashes.
|
262 |
|
|
if ($separator !== '/') {
|
263 |
|
|
$output = preg_replace("/$seppattern+\/|\/$seppattern+/", "/", $output);
|
264 |
|
|
}
|
265 |
|
|
|
266 |
|
|
// Replace multiple separators with a single one.
|
267 |
|
|
$output = preg_replace("/$seppattern+/", $separator, $output);
|
268 |
|
|
}
|
269 |
|
|
|
270 |
|
|
return $output;
|
271 |
|
|
}
|
272 |
|
|
|
273 |
|
|
/**
|
274 |
|
|
* Clean up an URL alias.
|
275 |
|
|
*
|
276 |
|
|
* Performs the following alterations:
|
277 |
|
|
* - Trim duplicate, leading, and trailing back-slashes.
|
278 |
|
|
* - Trim duplicate, leading, and trailing separators.
|
279 |
|
|
* - Shorten to a desired length and logical position based on word boundaries.
|
280 |
|
|
*
|
281 |
|
|
* @param $alias
|
282 |
|
|
* A string with the URL alias to clean up.
|
283 |
|
|
* @return
|
284 |
|
|
* The cleaned URL alias.
|
285 |
|
|
*/
|
286 |
|
|
function pathauto_clean_alias($alias) {
|
287 |
|
|
$cache = &drupal_static(__FUNCTION__);
|
288 |
|
|
|
289 |
|
|
if (!isset($cache)) {
|
290 |
|
|
$cache = array(
|
291 |
|
|
'maxlength' => min(variable_get('pathauto_max_length', 100), _pathauto_get_schema_alias_maxlength()),
|
292 |
|
|
);
|
293 |
|
|
}
|
294 |
|
|
|
295 |
|
|
$output = $alias;
|
296 |
|
|
|
297 |
|
|
// Trim duplicate, leading, and trailing back-slashes.
|
298 |
|
|
$output = _pathauto_clean_separators($output, '/');
|
299 |
|
|
|
300 |
|
|
// Trim duplicate, leading, and trailing separators.
|
301 |
|
|
$output = _pathauto_clean_separators($output);
|
302 |
|
|
|
303 |
|
|
// Shorten to a logical place based on word boundaries.
|
304 |
|
|
$output = truncate_utf8($output, $cache['maxlength'], TRUE);
|
305 |
|
|
|
306 |
|
|
return $output;
|
307 |
|
|
}
|
308 |
|
|
|
309 |
|
|
/**
|
310 |
|
|
* Apply patterns to create an alias.
|
311 |
|
|
*
|
312 |
|
|
* @param $module
|
313 |
|
|
* The name of your module (e.g., 'node').
|
314 |
|
|
* @param $op
|
315 |
|
|
* Operation being performed on the content being aliased
|
316 |
|
|
* ('insert', 'update', 'return', or 'bulkupdate').
|
317 |
|
|
* @param $source
|
318 |
|
|
* An internal Drupal path to be aliased.
|
319 |
|
|
* @param $data
|
320 |
|
|
* An array of keyed objects to pass to token_replace(). For simple
|
321 |
|
|
* replacement scenarios 'node', 'user', and others are common keys, with an
|
322 |
|
|
* accompanying node or user object being the value. Some token types, like
|
323 |
|
|
* 'site', do not require any explicit information from $data and can be
|
324 |
|
|
* replaced even if it is empty.
|
325 |
|
|
* @param $type
|
326 |
|
|
* For modules which provided pattern items in hook_pathauto(),
|
327 |
|
|
* the relevant identifier for the specific item to be aliased
|
328 |
|
|
* (e.g., $node->type).
|
329 |
|
|
* @param $language
|
330 |
|
|
* A string specify the path's language.
|
331 |
|
|
* @return
|
332 |
|
|
* The alias that was created.
|
333 |
|
|
*
|
334 |
|
|
* @see _pathauto_set_alias()
|
335 |
|
|
* @see token_replace()
|
336 |
|
|
*/
|
337 |
|
|
function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $language = LANGUAGE_NONE) {
|
338 |
|
|
// Retrieve and apply the pattern for this content type.
|
339 |
|
|
$pattern = pathauto_pattern_load_by_entity($module, $type, $language);
|
340 |
|
|
if (empty($pattern)) {
|
341 |
|
|
// No pattern? Do nothing (otherwise we may blow away existing aliases...)
|
342 |
|
|
return '';
|
343 |
|
|
}
|
344 |
|
|
|
345 |
|
|
// Special handling when updating an item which is already aliased.
|
346 |
|
|
$existing_alias = NULL;
|
347 |
|
|
if ($op == 'update' || $op == 'bulkupdate') {
|
348 |
|
|
if ($existing_alias = _pathauto_existing_alias_data($source, $language)) {
|
349 |
|
|
switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
|
350 |
|
|
case PATHAUTO_UPDATE_ACTION_NO_NEW:
|
351 |
|
|
// If an alias already exists, and the update action is set to do nothing,
|
352 |
|
|
// then gosh-darn it, do nothing.
|
353 |
|
|
return '';
|
354 |
|
|
}
|
355 |
|
|
}
|
356 |
|
|
}
|
357 |
|
|
|
358 |
|
|
// Replace any tokens in the pattern. Uses callback option to clean replacements. No sanitization.
|
359 |
|
|
$alias = token_replace($pattern, $data, array(
|
360 |
|
|
'sanitize' => FALSE,
|
361 |
|
|
'clear' => TRUE,
|
362 |
|
|
'callback' => 'pathauto_clean_token_values',
|
363 |
|
|
'language' => (object) array('language' => $language),
|
364 |
|
|
'pathauto' => TRUE,
|
365 |
|
|
));
|
366 |
|
|
|
367 |
|
|
// Check if the token replacement has not actually replaced any values. If
|
368 |
|
|
// that is the case, then stop because we should not generate an alias.
|
369 |
|
|
// @see token_scan()
|
370 |
|
|
$pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern);
|
371 |
|
|
if ($alias === $pattern_tokens_removed) {
|
372 |
|
|
return '';
|
373 |
|
|
}
|
374 |
|
|
|
375 |
|
|
$alias = pathauto_clean_alias($alias);
|
376 |
|
|
|
377 |
|
|
// Allow other modules to alter the alias.
|
378 |
|
|
$context = array(
|
379 |
|
|
'module' => $module,
|
380 |
|
|
'op' => $op,
|
381 |
|
|
'source' => &$source,
|
382 |
|
|
'data' => $data,
|
383 |
|
|
'type' => $type,
|
384 |
|
|
'language' => &$language,
|
385 |
|
|
'pattern' => $pattern,
|
386 |
|
|
);
|
387 |
|
|
drupal_alter('pathauto_alias', $alias, $context);
|
388 |
|
|
|
389 |
|
|
// If we have arrived at an empty string, discontinue.
|
390 |
|
|
if (!drupal_strlen($alias)) {
|
391 |
|
|
return '';
|
392 |
|
|
}
|
393 |
|
|
|
394 |
|
|
// If the alias already exists, generate a new, hopefully unique, variant.
|
395 |
|
|
$original_alias = $alias;
|
396 |
|
|
pathauto_alias_uniquify($alias, $source, $language);
|
397 |
|
|
if ($original_alias != $alias) {
|
398 |
|
|
// Alert the user why this happened.
|
399 |
|
|
_pathauto_verbose(t('The automatically generated alias %original_alias conflicted with an existing alias. Alias changed to %alias.', array(
|
400 |
|
|
'%original_alias' => $original_alias,
|
401 |
|
|
'%alias' => $alias,
|
402 |
|
|
)), $op);
|
403 |
|
|
}
|
404 |
|
|
|
405 |
|
|
// Return the generated alias if requested.
|
406 |
|
|
if ($op == 'return') {
|
407 |
|
|
return $alias;
|
408 |
|
|
}
|
409 |
|
|
|
410 |
|
|
// Build the new path alias array and send it off to be created.
|
411 |
|
|
$path = array(
|
412 |
|
|
'source' => $source,
|
413 |
|
|
'alias' => $alias,
|
414 |
|
|
'language' => $language,
|
415 |
|
|
);
|
416 |
|
|
$path = _pathauto_set_alias($path, $existing_alias, $op);
|
417 |
|
|
return $path;
|
418 |
|
|
}
|
419 |
|
|
|
420 |
|
|
/**
|
421 |
|
|
* Check to ensure a path alias is unique and add suffix variants if necessary.
|
422 |
|
|
*
|
423 |
|
|
* Given an alias 'content/test' if a path alias with the exact alias already
|
424 |
|
|
* exists, the function will change the alias to 'content/test-0' and will
|
425 |
|
|
* increase the number suffix until it finds a unique alias.
|
426 |
|
|
*
|
427 |
|
|
* @param $alias
|
428 |
|
|
* A string with the alias. Can be altered by reference.
|
429 |
|
|
* @param $source
|
430 |
|
|
* A string with the path source.
|
431 |
|
|
* @param $langcode
|
432 |
|
|
* A string with a language code.
|
433 |
|
|
*/
|
434 |
|
|
function pathauto_alias_uniquify(&$alias, $source, $langcode) {
|
435 |
|
|
if (!_pathauto_alias_exists($alias, $source, $langcode)) {
|
436 |
|
|
return;
|
437 |
|
|
}
|
438 |
|
|
|
439 |
|
|
// If the alias already exists, generate a new, hopefully unique, variant
|
440 |
|
|
$maxlength = min(variable_get('pathauto_max_length', 100), _pathauto_get_schema_alias_maxlength());
|
441 |
|
|
$separator = variable_get('pathauto_separator', '-');
|
442 |
|
|
$original_alias = $alias;
|
443 |
|
|
|
444 |
|
|
$i = 0;
|
445 |
|
|
do {
|
446 |
|
|
// Append an incrementing numeric suffix until we find a unique alias.
|
447 |
|
|
$unique_suffix = $separator . $i;
|
448 |
|
|
$alias = truncate_utf8($original_alias, $maxlength - drupal_strlen($unique_suffix, TRUE)) . $unique_suffix;
|
449 |
|
|
$i++;
|
450 |
|
|
} while (_pathauto_alias_exists($alias, $source, $langcode));
|
451 |
|
|
}
|
452 |
|
|
|
453 |
|
|
/**
|
454 |
|
|
* Verify if the given path is a valid menu callback.
|
455 |
|
|
*
|
456 |
|
|
* Taken from menu_execute_active_handler().
|
457 |
|
|
*
|
458 |
|
|
* @param $path
|
459 |
|
|
* A string containing a relative path.
|
460 |
|
|
* @return
|
461 |
|
|
* TRUE if the path already exists.
|
462 |
|
|
*/
|
463 |
|
|
function _pathauto_path_is_callback($path) {
|
464 |
|
|
// We need to use a try/catch here because of a core bug which will throw an
|
465 |
|
|
// exception if $path is something like 'node/foo/bar'.
|
466 |
|
|
// @todo Remove when http://drupal.org/node/1302158 is fixed in core.
|
467 |
|
|
try {
|
468 |
|
|
$menu = menu_get_item($path);
|
469 |
|
|
}
|
470 |
|
|
catch (Exception $e) {
|
471 |
|
|
return FALSE;
|
472 |
|
|
}
|
473 |
|
|
|
474 |
|
|
if (isset($menu['path']) && $menu['path'] == $path) {
|
475 |
|
|
return TRUE;
|
476 |
|
|
}
|
477 |
|
|
elseif (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) {
|
478 |
|
|
// Do not allow existing files or directories to get assigned an automatic
|
479 |
|
|
// alias. Note that we do not need to use is_link() to check for symbolic
|
480 |
|
|
// links since this returns TRUE for either is_file() or is_dir() already.
|
481 |
|
|
return TRUE;
|
482 |
|
|
}
|
483 |
|
|
return FALSE;
|
484 |
|
|
}
|
485 |
|
|
|
486 |
|
|
/**
|
487 |
|
|
* Private function for Pathauto to create an alias.
|
488 |
|
|
*
|
489 |
|
|
* @param $path
|
490 |
|
|
* An associative array containing the following keys:
|
491 |
|
|
* - source: The internal system path.
|
492 |
|
|
* - alias: The URL alias.
|
493 |
|
|
* - pid: (optional) Unique path alias identifier.
|
494 |
|
|
* - language: (optional) The language of the alias.
|
495 |
|
|
* @param $existing_alias
|
496 |
|
|
* (optional) An associative array of the existing path alias.
|
497 |
|
|
* @param $op
|
498 |
|
|
* An optional string with the operation being performed.
|
499 |
|
|
*
|
500 |
|
|
* @return
|
501 |
|
|
* The saved path from path_save() or NULL if the path was not saved.
|
502 |
|
|
*
|
503 |
|
|
* @see path_save()
|
504 |
|
|
*/
|
505 |
|
|
function _pathauto_set_alias(array $path, $existing_alias = NULL, $op = NULL) {
|
506 |
|
|
$verbose = _pathauto_verbose(NULL, $op);
|
507 |
|
|
|
508 |
|
|
// Alert users that an existing callback cannot be overridden automatically
|
509 |
|
|
if (_pathauto_path_is_callback($path['alias'])) {
|
510 |
|
|
if ($verbose) {
|
511 |
|
|
_pathauto_verbose(t('Ignoring alias %alias due to existing path conflict.', array('%alias' => $path['alias'])));
|
512 |
|
|
}
|
513 |
|
|
return;
|
514 |
|
|
}
|
515 |
|
|
// Alert users if they are trying to create an alias that is the same as the internal path
|
516 |
|
|
if ($path['source'] == $path['alias']) {
|
517 |
|
|
if ($verbose) {
|
518 |
|
|
_pathauto_verbose(t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $path['alias'])));
|
519 |
|
|
}
|
520 |
|
|
return;
|
521 |
|
|
}
|
522 |
|
|
|
523 |
|
|
// Skip replacing the current alias with an identical alias
|
524 |
|
|
if (empty($existing_alias) || $existing_alias['alias'] != $path['alias']) {
|
525 |
|
|
$path += array('pathauto' => TRUE, 'original' => $existing_alias);
|
526 |
|
|
|
527 |
|
|
// If there is already an alias, respect some update actions.
|
528 |
|
|
if (!empty($existing_alias)) {
|
529 |
|
|
switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
|
530 |
|
|
case PATHAUTO_UPDATE_ACTION_NO_NEW:
|
531 |
|
|
// Do not create the alias.
|
532 |
|
|
return;
|
533 |
|
|
case PATHAUTO_UPDATE_ACTION_LEAVE:
|
534 |
|
|
// Create a new alias instead of overwriting the existing by leaving
|
535 |
|
|
// $path['pid'] empty.
|
536 |
|
|
break;
|
537 |
|
|
case PATHAUTO_UPDATE_ACTION_DELETE:
|
538 |
|
|
// The delete actions should overwrite the existing alias.
|
539 |
|
|
$path['pid'] = $existing_alias['pid'];
|
540 |
|
|
break;
|
541 |
|
|
}
|
542 |
|
|
}
|
543 |
|
|
|
544 |
|
|
// Save the path array.
|
545 |
|
|
path_save($path);
|
546 |
|
|
|
547 |
|
|
if ($verbose) {
|
548 |
|
|
if (!empty($existing_alias['pid'])) {
|
549 |
|
|
_pathauto_verbose(t('Created new alias %alias for %source, replacing %old_alias.', array('%alias' => $path['alias'], '%source' => $path['source'], '%old_alias' => $existing_alias['alias'])));
|
550 |
|
|
}
|
551 |
|
|
else {
|
552 |
|
|
_pathauto_verbose(t('Created new alias %alias for %source.', array('%alias' => $path['alias'], '%source' => $path['source'])));
|
553 |
|
|
}
|
554 |
|
|
}
|
555 |
|
|
|
556 |
|
|
return $path;
|
557 |
|
|
}
|
558 |
|
|
}
|
559 |
|
|
|
560 |
|
|
/**
|
561 |
|
|
* Output a helpful message if verbose output is enabled.
|
562 |
|
|
*
|
563 |
|
|
* Verbose output is only enabled when:
|
564 |
|
|
* - The 'pathauto_verbose' setting is enabled.
|
565 |
|
|
* - The current user has the 'notify of path changes' permission.
|
566 |
|
|
* - The $op parameter is anything but 'bulkupdate' or 'return'.
|
567 |
|
|
*
|
568 |
|
|
* @param $message
|
569 |
|
|
* An optional string of the verbose message to display. This string should
|
570 |
|
|
* already be run through t().
|
571 |
|
|
* @param $op
|
572 |
|
|
* An optional string with the operation being performed.
|
573 |
|
|
* @return
|
574 |
|
|
* TRUE if verbose output is enabled, or FALSE otherwise.
|
575 |
|
|
*/
|
576 |
|
|
function _pathauto_verbose($message = NULL, $op = NULL) {
|
577 |
|
|
static $verbose;
|
578 |
|
|
|
579 |
|
|
if (!isset($verbose)) {
|
580 |
|
|
$verbose = variable_get('pathauto_verbose', FALSE) && user_access('notify of path changes');
|
581 |
|
|
}
|
582 |
|
|
|
583 |
|
|
if (!$verbose || (isset($op) && in_array($op, array('bulkupdate', 'return')))) {
|
584 |
|
|
return FALSE;
|
585 |
|
|
}
|
586 |
|
|
|
587 |
|
|
if ($message) {
|
588 |
|
|
drupal_set_message($message);
|
589 |
|
|
}
|
590 |
|
|
|
591 |
|
|
return $verbose;
|
592 |
|
|
}
|
593 |
|
|
|
594 |
|
|
/**
|
595 |
|
|
* Clean tokens so they are URL friendly.
|
596 |
|
|
*
|
597 |
|
|
* @param $replacements
|
598 |
|
|
* An array of token replacements that need to be "cleaned" for use in the URL.
|
599 |
|
|
* @param $data
|
600 |
|
|
* An array of objects used to generate the replacements.
|
601 |
|
|
* @param $options
|
602 |
|
|
* An array of options used to generate the replacements.
|
603 |
|
|
*/
|
604 |
|
|
function pathauto_clean_token_values(&$replacements, $data = array(), $options = array()) {
|
605 |
|
|
foreach ($replacements as $token => $value) {
|
606 |
|
|
// Only clean non-path tokens.
|
607 |
|
|
if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) {
|
608 |
|
|
$replacements[$token] = pathauto_cleanstring($value);
|
609 |
|
|
}
|
610 |
|
|
}
|
611 |
|
|
}
|
612 |
|
|
|
613 |
|
|
/**
|
614 |
|
|
* Return an array of arrays for punctuation values.
|
615 |
|
|
*
|
616 |
|
|
* Returns an array of arrays for punctuation values keyed by a name, including
|
617 |
|
|
* the value and a textual description.
|
618 |
|
|
* Can and should be expanded to include "all" non text punctuation values.
|
619 |
|
|
*
|
620 |
|
|
* @return
|
621 |
|
|
* An array of arrays for punctuation values keyed by a name, including the
|
622 |
|
|
* value and a textual description.
|
623 |
|
|
*/
|
624 |
|
|
function pathauto_punctuation_chars() {
|
625 |
|
|
$punctuation = &drupal_static(__FUNCTION__);
|
626 |
|
|
|
627 |
|
|
if (!isset($punctuation)) {
|
628 |
|
|
$cid = 'pathauto:punctuation:' . $GLOBALS['language']->language;
|
629 |
|
|
if ($cache = cache_get($cid)) {
|
630 |
|
|
$punctuation = $cache->data;
|
631 |
|
|
}
|
632 |
|
|
else {
|
633 |
|
|
$punctuation = array();
|
634 |
|
|
$punctuation['double_quotes'] = array('value' => '"', 'name' => t('Double quotation marks'));
|
635 |
|
|
$punctuation['quotes'] = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)"));
|
636 |
|
|
$punctuation['backtick'] = array('value' => '`', 'name' => t('Back tick'));
|
637 |
|
|
$punctuation['comma'] = array('value' => ',', 'name' => t('Comma'));
|
638 |
|
|
$punctuation['period'] = array('value' => '.', 'name' => t('Period'));
|
639 |
|
|
$punctuation['hyphen'] = array('value' => '-', 'name' => t('Hyphen'));
|
640 |
|
|
$punctuation['underscore'] = array('value' => '_', 'name' => t('Underscore'));
|
641 |
|
|
$punctuation['colon'] = array('value' => ':', 'name' => t('Colon'));
|
642 |
|
|
$punctuation['semicolon'] = array('value' => ';', 'name' => t('Semicolon'));
|
643 |
|
|
$punctuation['pipe'] = array('value' => '|', 'name' => t('Vertical bar (pipe)'));
|
644 |
|
|
$punctuation['left_curly'] = array('value' => '{', 'name' => t('Left curly bracket'));
|
645 |
|
|
$punctuation['left_square'] = array('value' => '[', 'name' => t('Left square bracket'));
|
646 |
|
|
$punctuation['right_curly'] = array('value' => '}', 'name' => t('Right curly bracket'));
|
647 |
|
|
$punctuation['right_square'] = array('value' => ']', 'name' => t('Right square bracket'));
|
648 |
|
|
$punctuation['plus'] = array('value' => '+', 'name' => t('Plus sign'));
|
649 |
|
|
$punctuation['equal'] = array('value' => '=', 'name' => t('Equal sign'));
|
650 |
|
|
$punctuation['asterisk'] = array('value' => '*', 'name' => t('Asterisk'));
|
651 |
|
|
$punctuation['ampersand'] = array('value' => '&', 'name' => t('Ampersand'));
|
652 |
|
|
$punctuation['percent'] = array('value' => '%', 'name' => t('Percent sign'));
|
653 |
|
|
$punctuation['caret'] = array('value' => '^', 'name' => t('Caret'));
|
654 |
|
|
$punctuation['dollar'] = array('value' => '$', 'name' => t('Dollar sign'));
|
655 |
|
|
$punctuation['hash'] = array('value' => '#', 'name' => t('Number sign (pound sign, hash)'));
|
656 |
|
|
$punctuation['at'] = array('value' => '@', 'name' => t('At sign'));
|
657 |
|
|
$punctuation['exclamation'] = array('value' => '!', 'name' => t('Exclamation mark'));
|
658 |
|
|
$punctuation['tilde'] = array('value' => '~', 'name' => t('Tilde'));
|
659 |
|
|
$punctuation['left_parenthesis'] = array('value' => '(', 'name' => t('Left parenthesis'));
|
660 |
|
|
$punctuation['right_parenthesis'] = array('value' => ')', 'name' => t('Right parenthesis'));
|
661 |
|
|
$punctuation['question_mark'] = array('value' => '?', 'name' => t('Question mark'));
|
662 |
|
|
$punctuation['less_than'] = array('value' => '<', 'name' => t('Less-than sign'));
|
663 |
|
|
$punctuation['greater_than'] = array('value' => '>', 'name' => t('Greater-than sign'));
|
664 |
|
|
$punctuation['slash'] = array('value' => '/', 'name' => t('Slash'));
|
665 |
|
|
$punctuation['back_slash'] = array('value' => '\\', 'name' => t('Backslash'));
|
666 |
|
|
|
667 |
|
|
// Allow modules to alter the punctuation list and cache the result.
|
668 |
|
|
drupal_alter('pathauto_punctuation_chars', $punctuation);
|
669 |
|
|
cache_set($cid, $punctuation);
|
670 |
|
|
}
|
671 |
|
|
}
|
672 |
|
|
|
673 |
|
|
return $punctuation;
|
674 |
|
|
}
|
675 |
|
|
|
676 |
|
|
/**
|
677 |
|
|
* Fetch the maximum length of the {url_alias}.alias field from the schema.
|
678 |
|
|
*
|
679 |
|
|
* @return
|
680 |
|
|
* An integer of the maximum URL alias length allowed by the database.
|
681 |
|
|
*/
|
682 |
|
|
function _pathauto_get_schema_alias_maxlength() {
|
683 |
|
|
$maxlength = &drupal_static(__FUNCTION__);
|
684 |
|
|
if (!isset($maxlength)) {
|
685 |
|
|
$schema = drupal_get_schema('url_alias');
|
686 |
|
|
$maxlength = $schema['fields']['alias']['length'];
|
687 |
|
|
}
|
688 |
|
|
return $maxlength;
|
689 |
|
|
} |