Projet

Général

Profil

Paste
Télécharger (23,8 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / pathauto / pathauto.inc @ 384fc62a

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
}