path.module 11.7 KB
Newer Older
Steven Wittens's avatar
Steven Wittens committed
1
2
3
4
5
6
7
<?php

/**
 * @file
 * Enables users to rename URLs.
 */

8
use Drupal\node\Node;
9

10
use Drupal\taxonomy\Term;
11

Steven Wittens's avatar
Steven Wittens committed
12
/**
13
 * Implements hook_help().
Steven Wittens's avatar
Steven Wittens committed
14
 */
15
16
function path_help($path, $arg) {
  switch ($path) {
Steven Wittens's avatar
Steven Wittens committed
17
    case 'admin/help#path':
18
19
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
20
      $output .= '<p>' . t('The Path module allows you to specify an alias, or custom URL, for any existing internal system path. Aliases should not be confused with URL redirects, which allow you to forward a changed or inactive URL to a new URL. In addition to making URLs more readable, aliases also help search engines index content more effectively. Multiple aliases may be used for a single internal system path. To automate the aliasing of paths, you can install the contributed module <a href="@pathauto">Pathauto</a>. For more information, see the online handbook entry for the <a href="@path">Path module</a>.', array('@path' => 'http://drupal.org/documentation/modules/path', '@pathauto' => 'http://drupal.org/project/pathauto')) . '</p>';
21
22
23
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Creating aliases') . '</dt>';
24
      $output .= '<dd>' . t('Users with sufficient <a href="@permissions">permissions</a> can create aliases under the <em>URL path settings</em> section when they create or edit content. Some examples of aliases are: ', array('@permissions' => url('admin/people/permissions', array('fragment' => 'module-path'))));
25
26
27
28
      $output .= '<ul><li>' . t('<em>member/jane-smith</em> aliased to internal path <em>user/123</em>') . '</li>';
      $output .= '<li>' . t('<em>about-us/team</em> aliased to internal path <em>node/456</em>') . '</li>';
      $output .= '</ul></dd>';
      $output .= '<dt>' . t('Managing aliases') . '</dt>';
29
      $output .= '<dd>' . t('The Path module provides a way to search and view a <a href="@aliases">list of all aliases</a> that are in use on your website. Aliases can be added, edited and deleted through this list.', array('@aliases' => url('admin/config/search/path'))) . '</dd>';
30
      $output .= '</dl>';
Steven Wittens's avatar
Steven Wittens committed
31
      return $output;
32

33
    case 'admin/config/search/path':
34
      return '<p>' . t("An alias defines a different name for an existing URL path - for example, the alias 'about' for the URL path 'node/1'. A URL path can have multiple aliases.") . '</p>';
35

36
    case 'admin/config/search/path/add':
37
      return '<p>' . t('Enter the path you wish to create the alias for, followed by the name of the new alias.') . '</p>';
Steven Wittens's avatar
Steven Wittens committed
38
39
40
  }
}

41
/**
42
 * Implements hook_permission().
43
44
45
46
47
48
49
 */
function path_permission() {
  return array(
    'administer url aliases' => array(
      'title' => t('Administer URL aliases'),
    ),
    'create url aliases' => array(
50
      'title' => t('Create and edit URL aliases'),
51
52
53
54
    ),
  );
}

Steven Wittens's avatar
Steven Wittens committed
55
/**
56
 * Implements hook_menu().
Steven Wittens's avatar
Steven Wittens committed
57
 */
58
function path_menu() {
59
  $items['admin/config/search/path'] = array(
60
61
    'title' => 'URL aliases',
    'description' => "Change your site's URL paths by aliasing them.",
62
    'page callback' => 'path_admin_overview',
63
    'access arguments' => array('administer url aliases'),
64
    'weight' => -5,
65
    'file' => 'path.admin.inc',
66
  );
67
  $items['admin/config/search/path/edit/%path'] = array(
68
    'title' => 'Edit alias',
69
    'page callback' => 'path_admin_edit',
70
    'page arguments' => array(5),
71
    'access arguments' => array('administer url aliases'),
72
    'file' => 'path.admin.inc',
73
  );
74
  $items['admin/config/search/path/delete/%path'] = array(
75
    'title' => 'Delete alias',
76
    'page callback' => 'drupal_get_form',
77
    'page arguments' => array('path_admin_delete_confirm', 5),
78
    'access arguments' => array('administer url aliases'),
79
    'file' => 'path.admin.inc',
80
  );
81
  $items['admin/config/search/path/list'] = array(
82
    'title' => 'List',
83
84
85
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
86
  $items['admin/config/search/path/add'] = array(
87
    'title' => 'Add alias',
88
    'page callback' => 'path_admin_edit',
89
    'access arguments' => array('administer url aliases'),
90
    'type' => MENU_LOCAL_ACTION,
91
    'file' => 'path.admin.inc',
92
  );
Steven Wittens's avatar
Steven Wittens committed
93
94
95
96
97

  return $items;
}

/**
98
99
100
 * Implements hook_form_BASE_FORM_ID_alter() for node_form().
 *
 * @see path_form_element_validate()
Steven Wittens's avatar
Steven Wittens committed
101
 */
102
function path_form_node_form_alter(&$form, $form_state) {
103
  $node = $form_state['controller']->getEntity($form_state);
104
  $path = array();
105
106
107
108
  if (!empty($node->nid)) {
    $conditions = array('source' => 'node/' . $node->nid);
    if ($node->langcode != LANGUAGE_NOT_SPECIFIED) {
      $conditions['langcode'] = $node->langcode;
109
110
111
112
    }
    $path = path_load($conditions);
    if ($path === FALSE) {
      $path = array();
113
114
    }
  }
115
116
  $path += array(
    'pid' => NULL,
117
    'source' => isset($node->nid) ? 'node/' . $node->nid : NULL,
118
    'alias' => '',
119
    'langcode' => isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED,
120
121
122
123
124
125
126
127
  );

  $form['path'] = array(
    '#type' => 'fieldset',
    '#title' => t('URL path settings'),
    '#collapsible' => TRUE,
    '#collapsed' => empty($path['alias']),
    '#group' => 'additional_settings',
128
129
130
    '#attributes' => array(
      'class' => array('path-form'),
    ),
131
    '#attached' => array(
132
      'library' => array(array('path', 'drupal.path')),
133
134
135
136
137
138
139
140
141
142
143
    ),
    '#access' => user_access('create url aliases') || user_access('administer url aliases'),
    '#weight' => 30,
    '#tree' => TRUE,
    '#element_validate' => array('path_form_element_validate'),
  );
  $form['path']['alias'] = array(
    '#type' => 'textfield',
    '#title' => t('URL alias'),
    '#default_value' => $path['alias'],
    '#maxlength' => 255,
144
    '#description' => t('The alternative URL for this content. Use a relative path without a trailing slash. For example, enter "about" for the about page.'),
145
146
147
  );
  $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
  $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
148
  $form['path']['langcode'] = array('#type' => 'value', '#value' => $path['langcode']);
149
}
Steven Wittens's avatar
Steven Wittens committed
150

151
/**
152
 * Form element validation handler for URL alias form element.
153
154
 *
 * @see path_form_node_form_alter()
155
 */
156
157
158
159
160
function path_form_element_validate($element, &$form_state, $complete_form) {
  if (!empty($form_state['values']['path']['alias'])) {
    // Trim the submitted value.
    $alias = trim($form_state['values']['path']['alias']);
    form_set_value($element['alias'], $alias, $form_state);
161
162
163
164
    // Node language needs special care. Since the language of the URL alias
    // depends on the node language, and the node language can be switched
    // right within the same form, we need to conditionally overload the
    // originally assigned URL alias language.
165
166
    // @todo Remove this after converting Path module to a field, and, after
    //   stopping Locale module from abusing the content language system.
167
168
    if (isset($form_state['values']['langcode'])) {
      form_set_value($element['langcode'], $form_state['values']['langcode'], $form_state);
169
    }
170
171
172
173
174
175

    $path = $form_state['values']['path'];

    // Ensure that the submitted alias does not exist yet.
    $query = db_select('url_alias')
      ->condition('alias', $path['alias'])
176
      ->condition('langcode', $path['langcode']);
177
178
179
180
181
182
    if (!empty($path['source'])) {
      $query->condition('source', $path['source'], '<>');
    }
    $query->addExpression('1');
    $query->range(0, 1);
    if ($query->execute()->fetchField()) {
183
      form_error($element, t('The alias is already in use.'));
184
    }
185
186
  }
}
Steven Wittens's avatar
Steven Wittens committed
187

188
/**
189
 * Implements hook_node_insert().
190
 */
191
function path_node_insert(Node $node) {
192
193
194
195
196
197
198
  if (isset($node->path)) {
    $path = $node->path;
    $path['alias'] = trim($path['alias']);
    // Only save a non-empty alias.
    if (!empty($path['alias'])) {
      // Ensure fields for programmatic executions.
      $path['source'] = 'node/' . $node->nid;
199
      $path['langcode'] = isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED;
200
      path_save($path);
201
202
203
    }
  }
}
Steven Wittens's avatar
Steven Wittens committed
204

205
/**
206
 * Implements hook_node_update().
207
 */
208
function path_node_update(Node $node) {
209
210
211
212
213
214
  if (isset($node->path)) {
    $path = $node->path;
    $path['alias'] = trim($path['alias']);
    // Delete old alias if user erased it.
    if (!empty($path['pid']) && empty($path['alias'])) {
      path_delete($path['pid']);
215
    }
216
217
218
219
    // Only save a non-empty alias.
    if (!empty($path['alias'])) {
      // Ensure fields for programmatic executions.
      $path['source'] = 'node/' . $node->nid;
220
      $path['langcode'] = isset($node->langcode) ? $node->langcode : LANGUAGE_NOT_SPECIFIED;
221
      path_save($path);
222
    }
223
224
  }
}
Steven Wittens's avatar
Steven Wittens committed
225

226
/**
227
 * Implements hook_node_predelete().
228
 */
229
function path_node_predelete(Node $node) {
230
231
  // Delete all aliases associated with this node.
  path_delete(array('source' => 'node/' . $node->nid));
Steven Wittens's avatar
Steven Wittens committed
232
233
}

234
/**
235
 * Implements hook_form_FORM_ID_alter() for taxonomy_term_form().
236
 */
237
function path_form_taxonomy_term_form_alter(&$form, $form_state) {
238
239
  // Make sure this does not show up on the delete confirmation form.
  if (empty($form_state['confirm_delete'])) {
240
241
    $term = $form_state['controller']->getEntity($form_state);
    $path = (isset($term->tid) ? path_load('taxonomy/term/' . $term->tid) : array());
242
243
    if ($path === FALSE) {
      $path = array();
244
    }
245
246
    $path += array(
      'pid' => NULL,
247
      'source' => isset($term->tid) ? 'taxonomy/term/' . $term->tid : NULL,
248
      'alias' => '',
249
      'langcode' => LANGUAGE_NOT_SPECIFIED,
250
    );
251
    $form['path'] = array(
252
253
254
255
      '#access' => user_access('create url aliases') || user_access('administer url aliases'),
      '#tree' => TRUE,
      '#element_validate' => array('path_form_element_validate'),
    );
256
    $form['path']['alias'] = array(
257
258
      '#type' => 'textfield',
      '#title' => t('URL alias'),
259
      '#default_value' => $path['alias'],
260
261
262
263
      '#maxlength' => 255,
      '#weight' => 0,
      '#description' => t("Optionally specify an alternative URL by which this term can be accessed. Use a relative path and don't add a trailing slash or the URL alias won't work."),
    );
264
265
    $form['path']['pid'] = array('#type' => 'value', '#value' => $path['pid']);
    $form['path']['source'] = array('#type' => 'value', '#value' => $path['source']);
266
    $form['path']['langcode'] = array('#type' => 'value', '#value' => $path['langcode']);
267
268
269
270
  }
}

/**
271
 * Implements hook_taxonomy_term_insert().
272
 */
273
function path_taxonomy_term_insert(Term $term) {
274
275
276
277
278
279
280
  if (isset($term->path)) {
    $path = $term->path;
    $path['alias'] = trim($path['alias']);
    // Only save a non-empty alias.
    if (!empty($path['alias'])) {
      // Ensure fields for programmatic executions.
      $path['source'] = 'taxonomy/term/' . $term->tid;
281
      $path['langcode'] = LANGUAGE_NOT_SPECIFIED;
282
      path_save($path);
283
284
285
286
287
    }
  }
}

/**
288
 * Implements hook_taxonomy_term_update().
289
 */
290
function path_taxonomy_term_update(Term $term) {
291
292
293
294
295
296
297
298
299
300
301
  if (isset($term->path)) {
    $path = $term->path;
    $path['alias'] = trim($path['alias']);
    // Delete old alias if user erased it.
    if (!empty($path['pid']) && empty($path['alias'])) {
      path_delete($path['pid']);
    }
    // Only save a non-empty alias.
    if (!empty($path['alias'])) {
      // Ensure fields for programmatic executions.
      $path['source'] = 'taxonomy/term/' . $term->tid;
302
      $path['langcode'] = LANGUAGE_NOT_SPECIFIED;
303
304
      path_save($path);
    }
305
306
307
  }
}

Steven Wittens's avatar
Steven Wittens committed
308
/**
309
 * Implements hook_taxonomy_term_delete().
Steven Wittens's avatar
Steven Wittens committed
310
 */
311
function path_taxonomy_term_delete(Term $term) {
312
313
  // Delete all aliases associated with this term.
  path_delete(array('source' => 'taxonomy/term/' . $term->tid));
Steven Wittens's avatar
Steven Wittens committed
314
}
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334

/**
 * Implements hook_library_info().
 */
function path_library_info() {
  $libraries['drupal.path'] = array(
    'title' => 'Path',
    'version' => VERSION,
    'js' => array(
      drupal_get_path('module', 'path') . '/path.js' => array(),
    ),
    'dependencies' => array(
      array('system', 'jquery'),
      array('system', 'drupal'),
      array('system', 'drupal.form'),
    ),
  );

  return $libraries;
}