path.module 12.5 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
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    case 'admin/help#path':
      $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>';
      $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>
');
      $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>';
      $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.
<li>enable clean url\'s to remove the =? at <a href="%admin-settings">administer &gt;&gt; settings</a>.</li>
</ul>
', array('%admin-path-add' => url('admin/path/add'), '%admin-path' => url('admin/path'), '%external-http-drupal-org-node-15365' => 'http://drupal.org/node/15365', '%admin-settings' => url('admin/settings')));
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;
Dries's avatar
   
Dries committed
37
    case 'admin/modules#description':
38
      return t('Allows users to rename URLs.');
39
    case 'admin/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/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
53
54
55
56
57
58
59
60

  if ($may_cache) {
    $items[] = array('path' => 'admin/path', 'title' => t('url aliases'),
      'callback' => 'path_admin',
      'access' => user_access('administer url aliases'));
    $items[] = array('path' => 'admin/path/edit', 'title' => t('edit alias'),
      'callback' => 'path_admin_edit',
      'access' => user_access('administer url aliases'),
      'type' => MENU_CALLBACK);
    $items[] = array('path' => 'admin/path/delete', 'title' => t('delete alias'),
61
      'callback' => 'path_admin_delete_confirm',
Dries's avatar
   
Dries committed
62
63
64
65
      'access' => user_access('administer url aliases'),
      'type' => MENU_CALLBACK);
    $items[] = array('path' => 'admin/path/list', 'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
Dries's avatar
   
Dries committed
66
    $items[] = array('path' => 'admin/path/add', 'title' => t('add alias'),
Dries's avatar
   
Dries committed
67
68
69
70
71
      'callback' => 'path_admin_edit',
      'access' => user_access('administer url aliases'),
      'type' => MENU_LOCAL_TASK);
  }

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

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

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

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

98
/**
99
100
101
102
 * Menu callback; confirms deleting an URL alias
 **/
function path_admin_delete_confirm($pid) {
  $path = path_load($pid);
103
  if (user_access('administer url aliases')) {
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    $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']))),
   $_GET['destination'] ? $_GET['destination'] : 'admin/path', t('This action cannot be undone.'),
  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']);
120
    return 'admin/path';
121
122
123
124
125
  }
}

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

132
133


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

    // We have an insert:
    if ($path_count == 0 && $alias_count == 0) {
154
      db_query("INSERT INTO {url_alias} (src, dst) VALUES ('%s', '%s')", $path, $alias);
155
      drupal_clear_path_cache();
Dries's avatar
   
Dries committed
156
    }
Dries's avatar
   
Dries committed
157
158
159
160
161
162
163
    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);
      }
164
      drupal_clear_path_cache();
Dries's avatar
   
Dries committed
165
166
    }
    else if ($path_count == 0 && $alias_count == 1) {
Dries's avatar
   
Dries committed
167
      db_query("UPDATE {url_alias} SET src = '%s' WHERE dst = '%s'", $path, $alias);
168
      drupal_clear_path_cache();
Dries's avatar
   
Dries committed
169
170
171
172
173
174
175
176
    }
    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
177
178
}

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

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.'));
  $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
186

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

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

Dries's avatar
   
Dries committed
198
/**
199
200
201
202
 * 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
203
 */
Dries's avatar
   
Dries committed
204
function path_nodeapi(&$node, $op, $arg) {
205
  if (user_access('create url aliases') || user_access('administer url aliases')) {
Dries's avatar
   
Dries committed
206
    switch ($op) {
207
      case 'validate':
Dries's avatar
   
Dries committed
208
209
210
        $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
211
        }
Dries's avatar
   
Dries committed
212
213
        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
214
215
        }
        break;
Dries's avatar
Dries committed
216

Dries's avatar
   
Dries committed
217
218
219
220
221
222
223
224
      case 'load':
        $path = "node/$node->nid";
        $alias = drupal_get_path_alias($path);
        if ($alias != $path) {
          $node->path = $alias;
        }
        break;

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

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

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

247
248
249
250
/**
 * Implementation of hook_form_alter().
 */
function path_form_alter($form_id, &$form) {
251
  if (user_access('create url aliases') && isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
252
253
254
255
256
    $path = $form['#node']->path;
    $form['path'] = array(
      '#type' => 'fieldset',
      '#title' => t('URL path settings'),
      '#collapsible' => TRUE,
257
258
      '#collapsed' => empty($path),
      '#weight' => 30,
259
260
261
262
263
264
265
266
267
    );
    $form['path']['path'] = array(
      '#type' => 'textfield',
      '#default_value' => $path,
      '#maxlength' => 250,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#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.'),
    );
268
    if ($path) {
269
270
271
272
273
274
275
276
277
      $form['path']['pid'] = array(
        '#type' => 'value',
        '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", $path))
      );
    }
  }
}


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

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

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

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

307
308
309
  $output = theme('table', $header, $rows);
  $output .= theme('pager', NULL, 50, 0, tablesort_pager());
  return $output;
Dries's avatar
Dries committed
310
311
}

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

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

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

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

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

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

Dries's avatar
   
Dries committed
346
    drupal_set_message(t('The alias has been saved.'));
347
    return 'admin/path';
Dries's avatar
   
Dries committed
348
  }
Dries's avatar
Dries committed
349
350
}

351