path.module 12.8 KB
Newer Older
Dries's avatar
Dries committed
1
<?php
Dries's avatar
Dries committed
2
// $Id$
Dries's avatar
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * Enables users to rename URLs.
 */

9
10
11
12
13
/**
 * Implementation of hook_help().
 */
function path_help($section) {
  switch ($section) {
14
    case 'admin/help#path':
15
      $output = '<p>'. t('The path module allows you to specify aliases for Drupal URLs. Such aliases improve readability of URLs for your users and may help internet search engines to index your content more effectively. More than one alias may be created for a given page.') .'</p>';
16
17
18
19
20
21
22
23
      $output .= t('<p>Some examples of URL aliases are:</p>
<ul>
<li>user/login =&gt; login</li>
<li>image/tid/16 =&gt; store</li>
<li>taxonomy/term/7+19+20+21 =&gt; store/products/whirlygigs</li>
<li>node/3 =&gt; contact</li>
</ul>
');
24
25
      $output .= '<p>'. t('The path module enables an extra field for aliases in all node input and editing forms (when users have the appropriate permissions). It also provides an interface to view and edit all URL aliases. The two permissions are related to URL aliasing are "administer a list of URL aliases" and "add url aliases". ') .'</p>';
      $output .= '<p>'. t('This module also comes with user-defined mass URL aliasing capabilities, which is useful if you wish to uniformly use URLs different from the default. For example, you may want to have your URLs presented in a different language. Access to the Drupal source code on the web server is required to set up these kinds of aliases. ') .'</p>';
26
27
28
29
30
31
      $output .= t('<p>You can</p>
<ul>
<li>set the path for a post with the path module.</li>
<li>add a URL alias: <a href="%admin-path-add">administer &gt;&gt; url aliases &gt;&gt; add alias</a>.</li>
<li>administer the list of URL aliases: <a href="%admin-path">administer &gt;&gt; url aliases</a>.</li>
<li>read how to <a href="%external-http-drupal-org-node-15365">configure clean URLs</a> for your webserver.
Dries's avatar
Dries committed
32
<li>enable clean url\'s to remove the =? at <a href="%admin-clean-url-settings">administer &gt;&gt; settings &gt;&gt; clean URLs</a>.</li>
33
</ul>
34
', array('%admin-path-add' => url('admin/build/path/add'), '%admin-path' => url('admin/build/path'), '%external-http-drupal-org-node-15365' => 'http://drupal.org/node/15365', '%admin-clean-url-settings' => url('admin/settings/clean-urls')));
35
      $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="%path">Path page</a>.', array('%path' => 'http://drupal.org/handbook/modules/path/')) .'</p>';
36
      return $output;
37
    case 'admin/settings/modules#description':
38
      return t('Allows users to rename URLs.');
39
    case 'admin/build/path':
40
      return t("<p>Drupal provides users complete control over URLs through aliasing. This feature is typically used to make URLs human-readable or easy to remember. For example, one could map the relative URL 'node/1' onto 'about'. Each system path can have multiple aliases.</p>");
41
    case 'admin/build/path/add':
42
      return t('<p>Enter the path you wish to create the alias for, followed by the name of the new alias.</p>');
43
44
  }
}
Dries's avatar
   
Dries committed
45

46
/**
Dries's avatar
   
Dries committed
47
 * Implementation of hook_menu().
48
 */
Dries's avatar
   
Dries committed
49
function path_menu($may_cache) {
Dries's avatar
   
Dries committed
50
  $items = array();
Dries's avatar
   
Dries committed
51
52

  if ($may_cache) {
53
54
    $items[] = array('path' => 'admin/build/path', 'title' => t('url aliases'),
      'description' => t('Change your site\'s URL paths by aliasing them.'),
Dries's avatar
   
Dries committed
55
56
      'callback' => 'path_admin',
      'access' => user_access('administer url aliases'));
57
    $items[] = array('path' => 'admin/build/path/edit', 'title' => t('edit alias'),
Dries's avatar
   
Dries committed
58
59
60
      'callback' => 'path_admin_edit',
      'access' => user_access('administer url aliases'),
      'type' => MENU_CALLBACK);
61
    $items[] = array('path' => 'admin/build/path/delete', 'title' => t('delete alias'),
62
      'callback' => 'path_admin_delete_confirm',
Dries's avatar
   
Dries committed
63
64
      'access' => user_access('administer url aliases'),
      'type' => MENU_CALLBACK);
65
    $items[] = array('path' => 'admin/build/path/list', 'title' => t('list'),
Dries's avatar
   
Dries committed
66
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
67
    $items[] = array('path' => 'admin/build/path/add', 'title' => t('add alias'),
Dries's avatar
   
Dries committed
68
69
70
71
72
      'callback' => 'path_admin_edit',
      'access' => user_access('administer url aliases'),
      'type' => MENU_LOCAL_TASK);
  }

Dries's avatar
   
Dries committed
73
  return $items;
74
}
Dries's avatar
   
Dries committed
75

76
77
78
79
/**
 * Menu callback; presents an overview of all URL aliases.
 */
function path_admin() {
Dries's avatar
   
Dries committed
80
  return path_overview();
81
82
83
84
85
86
}

/**
 * Menu callback; handles pages for creating and editing URL aliases.
 */
function path_admin_edit($pid = 0) {
87
  if ($pid) {
Dries's avatar
   
Dries committed
88
    $alias = path_load($pid);
89
    drupal_set_title($alias['dst']);
Dries's avatar
   
Dries committed
90
    $output = path_form(path_load($pid));
Dries's avatar
Dries committed
91
  }
92
  else {
Dries's avatar
   
Dries committed
93
    $output = path_form();
94
  }
Dries's avatar
   
Dries committed
95

Dries's avatar
   
Dries committed
96
  return $output;
97
}
Dries's avatar
   
Dries committed
98

99
/**
100
101
102
103
 * Menu callback; confirms deleting an URL alias
 **/
function path_admin_delete_confirm($pid) {
  $path = path_load($pid);
104
  if (user_access('administer url aliases')) {
105
106
107
    $form['pid'] = array('#type' => 'value', '#value' => $pid);
    $output = confirm_form('path_admin_delete_confirm', $form,
  t('Are you sure you want to delete path alias %title?', array('%title' => theme('placeholder', $path['dst']))),
108
   $_GET['destination'] ? $_GET['destination'] : 'admin/build/path', t('This action cannot be undone.'),
109
110
111
112
113
114
115
116
117
118
119
120
  t('Delete'), t('Cancel') );
  }

  return $output;
}

/**
 * Execute URL alias deletion
 **/
function path_admin_delete_confirm_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    path_admin_delete($form_values['pid']);
121
    return 'admin/build/path';
122
123
124
125
126
  }
}

/**
 * Post-confirmation; delete an URL alias.
127
128
 */
function path_admin_delete($pid = 0) {
Dries's avatar
Dries committed
129
  db_query('DELETE FROM {url_alias} WHERE pid = %d', $pid);
Dries's avatar
   
Dries committed
130
  drupal_set_message(t('The alias has been deleted.'));
131
132
}

133
134


135
136
137
/**
 * Set an aliased path for a given Drupal path, preventing duplicates.
 */
Dries's avatar
   
Dries committed
138
function path_set_alias($path = NULL, $alias = NULL, $pid = NULL) {
Dries's avatar
   
Dries committed
139
  if ($path && !$alias) {
Dries's avatar
   
Dries committed
140
    db_query("DELETE FROM {url_alias} WHERE src = '%s'", $path);
141
    drupal_clear_path_cache();
Dries's avatar
   
Dries committed
142
143
  }
  else if (!$path && $alias) {
Dries's avatar
   
Dries committed
144
    db_query("DELETE FROM {url_alias} WHERE dst = '%s'", $alias);
145
    drupal_clear_path_cache();
Dries's avatar
   
Dries committed
146
147
  }
  else if ($path && $alias) {
148
    $path = urldecode($path);
Dries's avatar
   
Dries committed
149
    $path_count = db_result(db_query("SELECT COUNT(src) FROM {url_alias} WHERE src = '%s'", $path));
150
    $alias = urldecode($alias);
Dries's avatar
   
Dries committed
151
    $alias_count = db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s'", $alias));
Dries's avatar
   
Dries committed
152
153
154

    // We have an insert:
    if ($path_count == 0 && $alias_count == 0) {
155
      db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
156
      drupal_clear_path_cache();
Dries's avatar
   
Dries committed
157
    }
Dries's avatar
   
Dries committed
158
159
160
161
162
163
164
    else if ($path_count >= 1 && $alias_count == 0) {
      if ($pid) {
        db_query("UPDATE {url_alias} SET dst = '%s', src = '%s' WHERE pid = %d", $alias, $path, $pid);
      }
      else {
        db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
      }
165
      drupal_clear_path_cache();
Dries's avatar
   
Dries committed
166
167
    }
    else if ($path_count == 0 && $alias_count == 1) {
Dries's avatar
   
Dries committed
168
      db_query("UPDATE {url_alias} SET src = '%s' WHERE dst = '%s'", $path, $alias);
169
      drupal_clear_path_cache();
Dries's avatar
   
Dries committed
170
171
172
173
174
175
176
177
    }
    else if ($path_count == 1 && $alias_count == 1) {
      // This will delete the path that alias was originally pointing to:
      path_set_alias(NULL, $alias);
      path_set_alias($path);
      path_set_alias($path, $alias);
    }
  }
Dries's avatar
Dries committed
178
179
}

180
181
182
183
/**
 * Return a form for editing or creating an individual URL alias.
 */
function path_form($edit = '') {
Dries's avatar
Dries committed
184

185
  $form['src'] = array('#type' => 'textfield', '#title' => t('Existing system path'), '#default_value' => $edit['src'], '#maxlength' => 64, '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1+2.'));
186
  $form['dst'] = array('#type' => 'textfield', '#default_value' => $edit['dst'], '#maxlength' => 64, '#description' => t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'));
Dries's avatar
Dries committed
187

188
  if ($edit['pid']) {
189
190
    $form['pid'] = array('#type' => 'hidden', '#value' => $edit['pid']);
    $form['submit'] = array('#type' => 'submit', '#value' => t('Update alias'));
Dries's avatar
   
Dries committed
191
192
  }
  else {
193
    $form['submit'] = array('#type' => 'submit', '#value' => t('Create new alias'));
Dries's avatar
   
Dries committed
194
195
  }

196
  return drupal_get_form('path_form', $form);
Dries's avatar
Dries committed
197
198
}

Dries's avatar
   
Dries committed
199
/**
200
201
202
203
 * Implementation of hook_nodeapi().
 *
 * Allows URL aliases for nodes to be specified at node edit time rather
 * than through the administrative interface.
Dries's avatar
   
Dries committed
204
 */
Dries's avatar
   
Dries committed
205
function path_nodeapi(&$node, $op, $arg) {
206
  if (user_access('create url aliases') || user_access('administer url aliases')) {
Dries's avatar
   
Dries committed
207
    switch ($op) {
208
      case 'validate':
Dries's avatar
   
Dries committed
209
210
211
        $node->path = trim($node->path);
        if ($node->path && !valid_url($node->path)) {
          form_set_error('path', t('The path is invalid.'));
Dries's avatar
   
Dries committed
212
        }
Dries's avatar
   
Dries committed
213
214
        else if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE dst = '%s' AND src != '%s'", $node->path, "node/$node->nid"))) {
          form_set_error('path', t('The path is already in use.'));
Dries's avatar
   
Dries committed
215
216
        }
        break;
Dries's avatar
Dries committed
217

Dries's avatar
   
Dries committed
218
219
      case 'load':
        $path = "node/$node->nid";
220
221
222
223
224
        // We don't use drupal_get_path_alias() to avoid custom rewrite functions.
        // We only care about exact aliases.
        $result = db_query("SELECT dst FROM {url_alias} WHERE src = '%s'", $path);
        if (db_num_rows($result)) {
          $node->path = db_result($result);
Dries's avatar
   
Dries committed
225
226
227
        }
        break;

228
      case 'insert':
229
        // Don't try to insert if path is NULL. We may have already set
230
        // the alias ahead of time.
Dries's avatar
   
Dries committed
231
        if ($node->path) {
Dries's avatar
   
Dries committed
232
          path_set_alias("node/$node->nid", $node->path);
Dries's avatar
   
Dries committed
233
234
        }
        break;
Dries's avatar
   
Dries committed
235

236
      case 'update':
Dries's avatar
   
Dries committed
237
        path_set_alias("node/$node->nid", $node->path, $node->pid);
Dries's avatar
   
Dries committed
238
239
        break;

240
      case 'delete':
Dries's avatar
   
Dries committed
241
        $path = "node/$node->nid";
242
243
        if (drupal_get_path_alias($path) != $path) {
          path_set_alias($path);
Dries's avatar
   
Dries committed
244
245
246
        }
        break;
    }
Dries's avatar
Dries committed
247
248
249
  }
}

250
251
252
253
/**
 * Implementation of hook_form_alter().
 */
function path_form_alter($form_id, &$form) {
254
  if (user_access('create url aliases') && isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
255
256
257
258
259
    $path = $form['#node']->path;
    $form['path'] = array(
      '#type' => 'fieldset',
      '#title' => t('URL path settings'),
      '#collapsible' => TRUE,
260
261
      '#collapsed' => empty($path),
      '#weight' => 30,
262
263
264
265
266
267
268
    );
    $form['path']['path'] = array(
      '#type' => 'textfield',
      '#default_value' => $path,
      '#maxlength' => 250,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
269
      '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
270
    );
271
    if ($path) {
272
273
274
275
276
277
278
279
280
      $form['path']['pid'] = array(
        '#type' => 'value',
        '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", $path))
      );
    }
  }
}


281
282
283
/**
 * Implementation of hook_perm().
 */
Dries's avatar
Dries committed
284
function path_perm() {
285
  return array('create url aliases', 'administer url aliases');
Dries's avatar
Dries committed
286
287
}

288
289
290
/**
 * Return a listing of all defined URL aliases.
 */
Dries's avatar
Dries committed
291
function path_overview() {
292
  $sql = 'SELECT * FROM {url_alias}';
Dries's avatar
Dries committed
293
  $header = array(
Dries's avatar
   
Dries committed
294
295
    array('data' => t('Alias'), 'field' => 'dst', 'sort' => 'asc'),
    array('data' => t('System'), 'field' => 'src'),
Dries's avatar
   
Dries committed
296
    array('data' => t('Operations'), 'colspan' => '2')
Dries's avatar
Dries committed
297
298
299
300
  );
  $sql .= tablesort_sql($header);
  $result = pager_query($sql, 50);

301
  $destination = drupal_get_destination();
Dries's avatar
Dries committed
302
  while ($data = db_fetch_object($result)) {
303
    $rows[] = array($data->dst, $data->src, l(t('edit'), "admin/build/path/edit/$data->pid", array(), $destination), l(t('delete'), "admin/build/path/delete/$data->pid", array(), $destination));
Dries's avatar
Dries committed
304
305
  }

Dries's avatar
   
Dries committed
306
  if (!$rows) {
307
    $rows[] = array(array('data' => t('No URL aliases available.'), 'colspan' => '4'));
Dries's avatar
   
Dries committed
308
309
  }

310
  $output = theme('table', $header, $rows);
311
  $output .= theme('pager', NULL, 50, 0);
312
  return $output;
Dries's avatar
Dries committed
313
314
}

315
316
317
/**
 * Fetch a specific URL alias from the database.
 */
Dries's avatar
   
Dries committed
318
function path_load($pid) {
319
  return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid));
Dries's avatar
   
Dries committed
320
}
Dries's avatar
Dries committed
321

322
323
324
/**
 * Verify that a new URL alias is valid, and save it to the database.
 */
325
function path_form_submit() {
326
  $edit = $GLOBALS['form_values'];
327
328
329
  $src = $edit['src'];
  $dst = $edit['dst'];
  $pid = $edit['pid'];
Dries's avatar
Dries committed
330

Dries's avatar
   
Dries committed
331
  if (!valid_url($src)) {
332
    form_set_error('src', t('The system path %path is invalid.', array('%path' => theme('placeholder', $src))));
Dries's avatar
Dries committed
333
334
  }

Dries's avatar
   
Dries committed
335
  if (!valid_url($dst)) {
336
    form_set_error('dst', t('The alias %alias is invalid.', array('%alias' => theme('placeholder', $dst))));
Dries's avatar
Dries committed
337
338
  }

339
  if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s'", $pid, $dst))) {
340
    form_set_error('dst', t('The alias %alias is already in use.', array('%alias' => theme('placeholder', $dst))));
Dries's avatar
   
Dries committed
341
  }
Dries's avatar
Dries committed
342

Dries's avatar
   
Dries committed
343
344
  if (form_get_errors()) {
    return path_form($edit);
Dries's avatar
Dries committed
345
  }
Dries's avatar
   
Dries committed
346
  else {
Dries's avatar
   
Dries committed
347
    path_set_alias($src, $dst, $pid);
Dries's avatar
Dries committed
348

Dries's avatar
   
Dries committed
349
    drupal_set_message(t('The alias has been saved.'));
350
    return 'admin/build/path';
Dries's avatar
   
Dries committed
351
  }
Dries's avatar
Dries committed
352
353
}

354