Projet

Général

Profil

Paste
Télécharger (24,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / pathauto / pathauto.inc @ 3aa14731

1
<?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 $langcode
63
 *   A string indicating the path's language.
64
 *
65
 * @return bool
66
 *   TRUE if an alias exists, FALSE if not.
67
 *
68
 * @deprecated Use path_pathauto_is_alias_reserved() instead.
69
 */
70
function _pathauto_alias_exists($alias, $source, $langcode = LANGUAGE_NONE) {
71
  return path_pathauto_is_alias_reserved($alias, $source, $langcode);
72
}
73

    
74
/**
75
 * Fetches an existing URL alias given a path and optional language.
76
 *
77
 * @param string $source
78
 *   An internal Drupal path.
79
 * @param string $language
80
 *   An optional language code to look up the path in.
81
 *
82
 * @return bool|array
83
 *   FALSE if no alias was found or an associative array containing the
84
 *   following keys:
85
 *   - pid: Unique path alias identifier.
86
 *   - alias: The URL alias.
87
 */
88
function _pathauto_existing_alias_data($source, $language = LANGUAGE_NONE) {
89
  $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();
90
  return path_load(array('pid' => $pid));
91
}
92

    
93
/**
94
 * Clean up a string segment to be used in an URL alias.
95
 *
96
 * Performs the following possible alterations:
97
 * - Remove all HTML tags.
98
 * - Process the string through the transliteration module.
99
 * - Replace or remove punctuation with the separator character.
100
 * - Remove back-slashes.
101
 * - Replace non-ascii and non-numeric characters with the separator.
102
 * - Remove common words.
103
 * - Replace whitespace with the separator character.
104
 * - Trim duplicate, leading, and trailing separators.
105
 * - Convert to lower-case.
106
 * - Shorten to a desired length and logical position based on word boundaries.
107
 *
108
 * This function should *not* be called on URL alias or path strings because it
109
 * is assumed that they are already clean.
110
 *
111
 * @param string $string
112
 *   A string to clean.
113
 * @param array $options
114
 *   (optional) A keyed array of settings and flags to control the Pathauto
115
 *   clean string replacement process. Supported options are:
116
 *   - langcode: A language code to be used when translating strings.
117
 *
118
 * @return string
119
 *   The cleaned string.
120
 */
121
function pathauto_cleanstring($string, array $options = array()) {
122
  // Use the advanced drupal_static() pattern, since this is called very often.
123
  static $drupal_static_fast;
124
  if (!isset($drupal_static_fast)) {
125
    $drupal_static_fast['cache'] = &drupal_static(__FUNCTION__);
126
  }
127
  $cache = &$drupal_static_fast['cache'];
128

    
129
  // Generate and cache variables used in this function so that on the second
130
  // call to pathauto_cleanstring() we focus on processing.
131
  if (!isset($cache)) {
132
    $cache = array(
133
      'separator' => variable_get('pathauto_separator', '-'),
134
      'strings' => array(),
135
      'transliterate' => variable_get('pathauto_transliterate', FALSE) && module_exists('transliteration'),
136
      'punctuation' => array(),
137
      'reduce_ascii' => (bool) variable_get('pathauto_reduce_ascii', FALSE),
138
      'ignore_words_regex' => FALSE,
139
      'lowercase' => (bool) variable_get('pathauto_case', PATHAUTO_CASE_LOWER),
140
      'maxlength' => min(variable_get('pathauto_max_component_length', 100), _pathauto_get_schema_alias_maxlength()),
141
    );
142

    
143
    // Generate and cache the punctuation replacements for strtr().
144
    $punctuation = pathauto_punctuation_chars();
145
    foreach ($punctuation as $name => $details) {
146
      $action = variable_get('pathauto_punctuation_' . $name, PATHAUTO_PUNCTUATION_REMOVE);
147
      switch ($action) {
148
        case PATHAUTO_PUNCTUATION_REMOVE:
149
          $cache['punctuation'][$details['value']] = '';
150
          break;
151
        case PATHAUTO_PUNCTUATION_REPLACE:
152
          $cache['punctuation'][$details['value']] = $cache['separator'];
153
          break;
154
        case PATHAUTO_PUNCTUATION_DO_NOTHING:
155
          // Literally do nothing.
156
          break;
157
      }
158
    }
159

    
160
    // Generate and cache the ignored words regular expression.
161
    $ignore_words = variable_get('pathauto_ignore_words', PATHAUTO_IGNORE_WORDS);
162
    $ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words);
163
    if ($ignore_words_regex) {
164
      $cache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b';
165
      if (function_exists('mb_eregi_replace')) {
166
        mb_regex_encoding('UTF-8');
167
        $cache['ignore_words_callback'] = 'mb_eregi_replace';
168
      }
169
      else {
170
        $cache['ignore_words_callback'] = 'preg_replace';
171
        $cache['ignore_words_regex'] = '/' . $cache['ignore_words_regex'] . '/i';
172
      }
173
    }
174
  }
175

    
176
  // Empty strings do not need any processing.
177
  if ($string === '' || $string === NULL) {
178
    return '';
179
  }
180

    
181
  $langcode = NULL;
182
  if (!empty($options['language']->language)) {
183
    $langcode = $options['language']->language;
184
  }
185
  elseif (!empty($options['langcode'])) {
186
    $langcode = $options['langcode'];
187
  }
188

    
189
  // Check if the string has already been processed, and if so return the
190
  // cached result.
191
  if (isset($cache['strings'][$langcode][$string])) {
192
    return $cache['strings'][$langcode][$string];
193
  }
194

    
195
  // Remove all HTML tags from the string.
196
  $output = strip_tags(decode_entities($string));
197

    
198
  // Optionally transliterate (by running through the Transliteration module)
199
  if ($cache['transliterate']) {
200
    // If the reduce strings to letters and numbers is enabled, don't bother
201
    // replacing unknown characters with a question mark. Use an empty string
202
    // instead.
203
    $output = transliteration_get($output, $cache['reduce_ascii'] ? '' : '?', $langcode);
204
  }
205

    
206
  // Replace or drop punctuation based on user settings
207
  $output = strtr($output, $cache['punctuation']);
208

    
209
  // Reduce strings to letters and numbers
210
  if ($cache['reduce_ascii']) {
211
    $output = preg_replace('/[^a-zA-Z0-9\/]+/', $cache['separator'], $output);
212
  }
213

    
214
  // Get rid of words that are on the ignore list
215
  if ($cache['ignore_words_regex']) {
216
    $words_removed = $cache['ignore_words_callback']($cache['ignore_words_regex'], '', $output);
217
    if (drupal_strlen(trim($words_removed)) > 0) {
218
      $output = $words_removed;
219
    }
220
  }
221

    
222
  // Always replace whitespace with the separator.
223
  $output = preg_replace('/\s+/', $cache['separator'], $output);
224

    
225
  // Trim duplicates and remove trailing and leading separators.
226
  $output = _pathauto_clean_separators($output, $cache['separator']);
227

    
228
  // Optionally convert to lower case.
229
  if ($cache['lowercase']) {
230
    $output = drupal_strtolower($output);
231
  }
232

    
233
  // Shorten to a logical place based on word boundaries.
234
  $output = truncate_utf8($output, $cache['maxlength'], TRUE);
235

    
236
  // Cache this result in the static array.
237
  $cache['strings'][$langcode][$string] = $output;
238

    
239
  return $output;
240
}
241

    
242
/**
243
 * Trims duplicate, leading, and trailing separators from a string.
244
 *
245
 * @param string $string
246
 *   The string to clean path separators from.
247
 * @param string $separator
248
 *   The path separator to use when cleaning.
249
 *
250
 * @return string
251
 *   The cleaned version of the string.
252
 *
253
 * @see pathauto_cleanstring()
254
 * @see pathauto_clean_alias()
255
 */
256
function _pathauto_clean_separators($string, $separator = NULL) {
257
  static $default_separator;
258

    
259
  if (!isset($separator)) {
260
    if (!isset($default_separator)) {
261
      $default_separator = variable_get('pathauto_separator', '-');
262
    }
263
    $separator = $default_separator;
264
  }
265

    
266
  $output = $string;
267

    
268
  if (strlen($separator)) {
269
    // Trim any leading or trailing separators.
270
    $output = trim($output, $separator);
271

    
272
    // Escape the separator for use in regular expressions.
273
    $seppattern = preg_quote($separator, '/');
274

    
275
    // Replace multiple separators with a single one.
276
    $output = preg_replace("/$seppattern+/", $separator, $output);
277

    
278
    // Replace trailing separators around slashes.
279
    if ($separator !== '/') {
280
      $output = preg_replace("/\/+$seppattern\/+|$seppattern\/+|\/+$seppattern/", "/", $output);
281
    }
282
  }
283

    
284
  return $output;
285
}
286

    
287
/**
288
 * Clean up an URL alias.
289
 *
290
 * Performs the following alterations:
291
 * - Trim duplicate, leading, and trailing back-slashes.
292
 * - Trim duplicate, leading, and trailing separators.
293
 * - Shorten to a desired length and logical position based on word boundaries.
294
 *
295
 * @param string $alias
296
 *   A string with the URL alias to clean up.
297
 *
298
 * @return string
299
 *   The cleaned URL alias.
300
 */
301
function pathauto_clean_alias($alias) {
302
  $cache = &drupal_static(__FUNCTION__);
303

    
304
  if (!isset($cache)) {
305
    $cache = array(
306
      'maxlength' => min(variable_get('pathauto_max_length', 100), _pathauto_get_schema_alias_maxlength()),
307
    );
308
  }
309

    
310
  $output = $alias;
311

    
312
  // Trim duplicate, leading, and trailing separators. Do this before cleaning
313
  // backslashes since a pattern like "[token1]/[token2]-[token3]/[token4]"
314
  // could end up like "value1/-/value2" and if backslashes were cleaned first
315
  // this would result in a duplicate blackslash.
316
  $output = _pathauto_clean_separators($output);
317

    
318
  // Trim duplicate, leading, and trailing backslashes.
319
  $output = _pathauto_clean_separators($output, '/');
320

    
321
  // Shorten to a logical place based on word boundaries.
322
  $output = truncate_utf8($output, $cache['maxlength'], TRUE);
323

    
324
  return $output;
325
}
326

    
327
/**
328
 * Apply patterns to create an alias.
329
 *
330
 * @param $module
331
 *   The name of your module (e.g., 'node').
332
 * @param $op
333
 *   Operation being performed on the content being aliased
334
 *   ('insert', 'update', 'return', or 'bulkupdate').
335
 * @param $source
336
 *   An internal Drupal path to be aliased.
337
 * @param $data
338
 *   An array of keyed objects to pass to token_replace(). For simple
339
 *   replacement scenarios 'node', 'user', and others are common keys, with an
340
 *   accompanying node or user object being the value. Some token types, like
341
 *   'site', do not require any explicit information from $data and can be
342
 *   replaced even if it is empty.
343
 * @param $type
344
 *   For modules which provided pattern items in hook_pathauto(),
345
 *   the relevant identifier for the specific item to be aliased
346
 *   (e.g., $node->type).
347
 * @param $language
348
 *   A string specify the path's language.
349
 *
350
 * @return array|null|false
351
 *   The alias array that was created, NULL if an empty alias was generated, or
352
 *   FALSE if the alias generation was not possible.
353
 *
354
 * @see _pathauto_set_alias()
355
 * @see token_replace()
356
 */
357
function pathauto_create_alias($module, $op, $source, $data, $type = NULL, $language = LANGUAGE_NONE) {
358
  // Retrieve and apply the pattern for this content type.
359
  $pattern = pathauto_pattern_load_by_entity($module, $type, $language);
360

    
361
  // Allow other modules to alter the pattern.
362
  $context = array(
363
    'module' => $module,
364
    'op' => $op,
365
    'source' => $source,
366
    'data' => $data,
367
    'type' => $type,
368
    'language' => &$language,
369
  );
370
  drupal_alter('pathauto_pattern', $pattern, $context);
371

    
372
  if (empty($pattern)) {
373
    // No pattern? Do nothing (otherwise we may blow away existing aliases...)
374
    return FALSE;
375
  }
376

    
377
  // Special handling when updating an item which is already aliased.
378
  $existing_alias = NULL;
379
  if ($op != 'insert') {
380
    if ($existing_alias = _pathauto_existing_alias_data($source, $language)) {
381
      switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
382
        case PATHAUTO_UPDATE_ACTION_NO_NEW:
383
          // If an alias already exists, and the update action is set to do nothing,
384
          // then gosh-darn it, do nothing.
385
          return FALSE;
386
      }
387
    }
388
  }
389

    
390
  // Replace any tokens in the pattern. Uses callback option to clean replacements. No sanitization.
391
  $alias = token_replace($pattern, $data, array(
392
    'sanitize' => FALSE,
393
    'clear' => TRUE,
394
    'callback' => 'pathauto_clean_token_values',
395
    'language' => (object) array('language' => $language),
396
    'pathauto' => TRUE,
397
  ));
398

    
399
  // Check if the token replacement has not actually replaced any values. If
400
  // that is the case, then stop because we should not generate an alias.
401
  // @see token_scan()
402
  $pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern);
403
  if ($alias === $pattern_tokens_removed) {
404
    return;
405
  }
406

    
407
  $alias = pathauto_clean_alias($alias);
408

    
409
  // Allow other modules to alter the alias.
410
  $context['source'] = &$source;
411
  $context['pattern'] = $pattern;
412
  drupal_alter('pathauto_alias', $alias, $context);
413

    
414
  // If we have arrived at an empty string, discontinue.
415
  if (!drupal_strlen($alias)) {
416
    return;
417
  }
418

    
419
  // If the alias already exists, generate a new, hopefully unique, variant.
420
  $original_alias = $alias;
421
  pathauto_alias_uniquify($alias, $source, $language);
422
  if ($original_alias != $alias) {
423
    // Alert the user why this happened.
424
    _pathauto_verbose(t('The automatically generated alias %original_alias conflicted with an existing alias. Alias changed to %alias.', array(
425
      '%original_alias' => $original_alias,
426
      '%alias' => $alias,
427
    )), $op);
428
  }
429

    
430
  // Return the generated alias if requested.
431
  if ($op == 'return') {
432
    return $alias;
433
  }
434

    
435
  // Build the new path alias array and send it off to be created.
436
  $path = array(
437
    'source' => $source,
438
    'alias' => $alias,
439
    'language' => $language,
440
  );
441
  $path = _pathauto_set_alias($path, $existing_alias, $op);
442
  return $path;
443
}
444

    
445
/**
446
 * Check to ensure a path alias is unique and add suffix variants if necessary.
447
 *
448
 * Given an alias 'content/test' if a path alias with the exact alias already
449
 * exists, the function will change the alias to 'content/test-0' and will
450
 * increase the number suffix until it finds a unique alias.
451
 *
452
 * @param $alias
453
 *   A string with the alias. Can be altered by reference.
454
 * @param $source
455
 *   A string with the path source.
456
 * @param $langcode
457
 *   A string with a language code.
458
 */
459
function pathauto_alias_uniquify(&$alias, $source, $langcode) {
460
  if (!pathauto_is_alias_reserved($alias, $source, $langcode)) {
461
    return;
462
  }
463

    
464
  // If the alias already exists, generate a new, hopefully unique, variant
465
  $maxlength = min(variable_get('pathauto_max_length', 100), _pathauto_get_schema_alias_maxlength());
466
  $separator = variable_get('pathauto_separator', '-');
467
  $original_alias = $alias;
468

    
469
  $i = 0;
470
  do {
471
    // Append an incrementing numeric suffix until we find a unique alias.
472
    $unique_suffix = $separator . $i;
473
    $alias = truncate_utf8($original_alias, $maxlength - drupal_strlen($unique_suffix), TRUE) . $unique_suffix;
474
    $i++;
475
  } while (pathauto_is_alias_reserved($alias, $source, $langcode));
476
}
477

    
478
/**
479
 * Verify if the given path is a valid menu callback.
480
 *
481
 * Taken from menu_execute_active_handler().
482
 *
483
 * @param $path
484
 *   A string containing a relative path.
485
 * @return
486
 *   TRUE if the path already exists.
487
 */
488
function _pathauto_path_is_callback($path) {
489
  // We need to use a try/catch here because of a core bug which will throw an
490
  // exception if $path is something like 'node/foo/bar'.
491
  // @todo Remove when http://drupal.org/node/1003788 is fixed in core.
492
  try {
493
    $menu = menu_get_item($path);
494
  }
495
  catch (Exception $e) {
496
    return FALSE;
497
  }
498

    
499
  if (isset($menu['path']) && $menu['path'] == $path) {
500
    return TRUE;
501
  }
502
  elseif (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) {
503
    // Do not allow existing files or directories to get assigned an automatic
504
    // alias. Note that we do not need to use is_link() to check for symbolic
505
    // links since this returns TRUE for either is_file() or is_dir() already.
506
    return TRUE;
507
  }
508
  return FALSE;
509
}
510

    
511
/**
512
 * Private function for Pathauto to create an alias.
513
 *
514
 * @param $path
515
 *   An associative array containing the following keys:
516
 *   - source: The internal system path.
517
 *   - alias: The URL alias.
518
 *   - pid: (optional) Unique path alias identifier.
519
 *   - language: (optional) The language of the alias.
520
 * @param $existing_alias
521
 *   (optional) An associative array of the existing path alias.
522
 * @param $op
523
 *   An optional string with the operation being performed.
524
 *
525
 * @return
526
 *   The saved path from path_save() or FALSE if the path was not saved.
527
 *
528
 * @see path_save()
529
 */
530
function _pathauto_set_alias(array $path, $existing_alias = NULL, $op = NULL) {
531
  $verbose = _pathauto_verbose(NULL, $op);
532

    
533
  // Alert users if they are trying to create an alias that is the same as the internal path
534
  if ($path['source'] == $path['alias']) {
535
    if ($verbose) {
536
      _pathauto_verbose(t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $path['alias'])));
537
    }
538
    return FALSE;
539
  }
540

    
541
  // Skip replacing the current alias with an identical alias
542
  if (empty($existing_alias) || $existing_alias['alias'] != $path['alias']) {
543
    $path += array('pathauto' => TRUE, 'original' => $existing_alias);
544

    
545
    // If there is already an alias, respect some update actions.
546
    if (!empty($existing_alias)) {
547
      switch (variable_get('pathauto_update_action', PATHAUTO_UPDATE_ACTION_DELETE)) {
548
        case PATHAUTO_UPDATE_ACTION_NO_NEW:
549
          // Do not create the alias.
550
          return FALSE;
551
        case PATHAUTO_UPDATE_ACTION_LEAVE:
552
          // Create a new alias instead of overwriting the existing by leaving
553
          // $path['pid'] empty.
554
          break;
555
        case PATHAUTO_UPDATE_ACTION_DELETE:
556
          // The delete actions should overwrite the existing alias.
557
          $path['pid'] = $existing_alias['pid'];
558
          break;
559
      }
560
    }
561

    
562
    // Save the path array.
563
    path_save($path);
564

    
565
    if ($verbose) {
566
      if (!empty($existing_alias['pid'])) {
567
        _pathauto_verbose(t('Created new alias %alias for %source, replacing %old_alias.', array('%alias' => $path['alias'], '%source' => $path['source'], '%old_alias' => $existing_alias['alias'])));
568
      }
569
      else {
570
        _pathauto_verbose(t('Created new alias %alias for %source.', array('%alias' => $path['alias'], '%source' => $path['source'])));
571
      }
572
    }
573

    
574
    return $path;
575
  }
576
}
577

    
578
/**
579
 * Output a helpful message if verbose output is enabled.
580
 *
581
 * Verbose output is only enabled when:
582
 * - The 'pathauto_verbose' setting is enabled.
583
 * - The current user has the 'notify of path changes' permission.
584
 * - The $op parameter is anything but 'bulkupdate' or 'return'.
585
 *
586
 * @param $message
587
 *   An optional string of the verbose message to display. This string should
588
 *   already be run through t().
589
 * @param $op
590
 *   An optional string with the operation being performed.
591
 * @return
592
 *   TRUE if verbose output is enabled, or FALSE otherwise.
593
 */
594
function _pathauto_verbose($message = NULL, $op = NULL) {
595
  static $verbose;
596

    
597
  if (!isset($verbose)) {
598
    $verbose = variable_get('pathauto_verbose', FALSE) && user_access('notify of path changes');
599
  }
600

    
601
  if (!$verbose || (isset($op) && in_array($op, array('bulkupdate', 'return')))) {
602
    return FALSE;
603
  }
604

    
605
  if ($message) {
606
    drupal_set_message($message);
607
  }
608

    
609
  return $verbose;
610
}
611

    
612
/**
613
 * Clean tokens so they are URL friendly.
614
 *
615
 * @param $replacements
616
 *   An array of token replacements that need to be "cleaned" for use in the URL.
617
 * @param $data
618
 *   An array of objects used to generate the replacements.
619
 * @param $options
620
 *   An array of options used to generate the replacements.
621
 */
622
function pathauto_clean_token_values(&$replacements, $data = array(), $options = array()) {
623
  foreach ($replacements as $token => $value) {
624
    // Only clean non-path tokens.
625
    if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) {
626
      $replacements[$token] = pathauto_cleanstring($value, $options);
627
    }
628
  }
629
}
630

    
631
/**
632
 * Return an array of arrays for punctuation values.
633
 *
634
 * Returns an array of arrays for punctuation values keyed by a name, including
635
 * the value and a textual description.
636
 * Can and should be expanded to include "all" non text punctuation values.
637
 *
638
 * @return
639
 *   An array of arrays for punctuation values keyed by a name, including the
640
 *   value and a textual description.
641
 */
642
function pathauto_punctuation_chars() {
643
  $punctuation = &drupal_static(__FUNCTION__);
644

    
645
  if (!isset($punctuation)) {
646
    $cid = 'pathauto:punctuation:' . $GLOBALS['language']->language;
647
    if ($cache = cache_get($cid)) {
648
      $punctuation = $cache->data;
649
    }
650
    else {
651
      $punctuation = array();
652
      $punctuation['double_quotes']      = array('value' => '"', 'name' => t('Double quotation marks'));
653
      $punctuation['quotes']             = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)"));
654
      $punctuation['backtick']           = array('value' => '`', 'name' => t('Back tick'));
655
      $punctuation['comma']              = array('value' => ',', 'name' => t('Comma'));
656
      $punctuation['period']             = array('value' => '.', 'name' => t('Period'));
657
      $punctuation['hyphen']             = array('value' => '-', 'name' => t('Hyphen'));
658
      $punctuation['underscore']         = array('value' => '_', 'name' => t('Underscore'));
659
      $punctuation['colon']              = array('value' => ':', 'name' => t('Colon'));
660
      $punctuation['semicolon']          = array('value' => ';', 'name' => t('Semicolon'));
661
      $punctuation['pipe']               = array('value' => '|', 'name' => t('Vertical bar (pipe)'));
662
      $punctuation['left_curly']         = array('value' => '{', 'name' => t('Left curly bracket'));
663
      $punctuation['left_square']        = array('value' => '[', 'name' => t('Left square bracket'));
664
      $punctuation['right_curly']        = array('value' => '}', 'name' => t('Right curly bracket'));
665
      $punctuation['right_square']       = array('value' => ']', 'name' => t('Right square bracket'));
666
      $punctuation['plus']               = array('value' => '+', 'name' => t('Plus sign'));
667
      $punctuation['equal']              = array('value' => '=', 'name' => t('Equal sign'));
668
      $punctuation['asterisk']           = array('value' => '*', 'name' => t('Asterisk'));
669
      $punctuation['ampersand']          = array('value' => '&', 'name' => t('Ampersand'));
670
      $punctuation['percent']            = array('value' => '%', 'name' => t('Percent sign'));
671
      $punctuation['caret']              = array('value' => '^', 'name' => t('Caret'));
672
      $punctuation['dollar']             = array('value' => '$', 'name' => t('Dollar sign'));
673
      $punctuation['hash']               = array('value' => '#', 'name' => t('Number sign (pound sign, hash)'));
674
      $punctuation['at']                 = array('value' => '@', 'name' => t('At sign'));
675
      $punctuation['exclamation']        = array('value' => '!', 'name' => t('Exclamation mark'));
676
      $punctuation['tilde']              = array('value' => '~', 'name' => t('Tilde'));
677
      $punctuation['left_parenthesis']   = array('value' => '(', 'name' => t('Left parenthesis'));
678
      $punctuation['right_parenthesis']  = array('value' => ')', 'name' => t('Right parenthesis'));
679
      $punctuation['question_mark']      = array('value' => '?', 'name' => t('Question mark'));
680
      $punctuation['less_than']          = array('value' => '<', 'name' => t('Less-than sign'));
681
      $punctuation['greater_than']       = array('value' => '>', 'name' => t('Greater-than sign'));
682
      $punctuation['slash']              = array('value' => '/', 'name' => t('Slash'));
683
      $punctuation['back_slash']         = array('value' => '\\', 'name' => t('Backslash'));
684

    
685
      // Allow modules to alter the punctuation list and cache the result.
686
      drupal_alter('pathauto_punctuation_chars', $punctuation);
687
      cache_set($cid, $punctuation);
688
    }
689
  }
690

    
691
  return $punctuation;
692
}
693

    
694
/**
695
 * Fetch the maximum length of the {url_alias}.alias field from the schema.
696
 *
697
 * @return
698
 *   An integer of the maximum URL alias length allowed by the database.
699
 */
700
function _pathauto_get_schema_alias_maxlength() {
701
  $maxlength = &drupal_static(__FUNCTION__);
702
  if (!isset($maxlength)) {
703
    $schema = drupal_get_schema('url_alias');
704
    $maxlength = $schema['fields']['alias']['length'];
705
  }
706
  return $maxlength;
707
}