path.module 13.1 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
      $output .= t('<p>You can</p>
<ul>
<li>set the path for a post with the path module.</li>
29
30
31
32
<li>add a URL alias: <a href="@admin-path-add">administer &gt;&gt; site building &gt;&gt; url aliases &gt;&gt; add alias</a>.</li>
<li>administer the list of URL aliases: <a href="@admin-path">administer &gt;&gt; site building &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-clean-url-settings">administer &gt;&gt; site configuration &gt;&gt; clean URLs</a>.</li>
33
</ul>
34
35
', 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')));
      $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/build/path':
38
      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>");
39
    case 'admin/build/path/add':
40
      return t('<p>Enter the path you wish to create the alias for, followed by the name of the new alias.</p>');
41
42
  }
}
Dries's avatar
   
Dries committed
43

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

  if ($may_cache) {
51
52
    $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
53
54
      'callback' => 'path_admin',
      'access' => user_access('administer url aliases'));
55
    $items[] = array('path' => 'admin/build/path/edit', 'title' => t('edit alias'),
56
57
      'callback' => 'drupal_get_form',
      'callback arguments' => array('path_admin_edit'),
Dries's avatar
   
Dries committed
58
59
      'access' => user_access('administer url aliases'),
      'type' => MENU_CALLBACK);
60
    $items[] = array('path' => 'admin/build/path/delete', 'title' => t('delete alias'),
61
62
      'callback' => 'drupal_get_form',
      'callback arguments' => array('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'),
68
69
      'callback' => 'drupal_get_form',
      'callback arguments' => array('path_admin_edit'),
Dries's avatar
   
Dries committed
70
71
72
73
      'access' => user_access('administer url aliases'),
      'type' => MENU_LOCAL_TASK);
  }

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

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

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

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

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

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

134
135


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

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

181
182
183
184
/**
 * Return a form for editing or creating an individual URL alias.
 */
function path_form($edit = '') {
185
  $form['#base'] = 'path_form';
Dries's avatar
Dries committed
186

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  $form['src'] = array(
    '#type' => 'textfield',
    '#title' => t('Existing system path'),
    '#default_value' => $edit['src'],
    '#maxlength' => 64,
    '#size' => 45,
    '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1+2.'),
    '#field_prefix' => url(NULL, NULL, NULL, TRUE) . (variable_get('clean_url', 0) ? '' : '?q=')
  );
  $form['dst'] = array(
    '#type' => 'textfield',
    '#default_value' => $edit['dst'],
    '#maxlength' => 64,
    '#size' => 45,
    '#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.'),
    '#field_prefix' => url(NULL, NULL, NULL, TRUE) . (variable_get('clean_url', 0) ? '' : '?q=')
  );
Dries's avatar
Dries committed
204

205
  if ($edit['pid']) {
206
207
    $form['pid'] = array('#type' => 'hidden', '#value' => $edit['pid']);
    $form['submit'] = array('#type' => 'submit', '#value' => t('Update alias'));
Dries's avatar
   
Dries committed
208
209
  }
  else {
210
    $form['submit'] = array('#type' => 'submit', '#value' => t('Create new alias'));
Dries's avatar
   
Dries committed
211
212
  }

213
  return $form;
Dries's avatar
Dries committed
214
215
}

Dries's avatar
   
Dries committed
216
/**
217
218
219
220
 * 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
221
 */
Dries's avatar
   
Dries committed
222
function path_nodeapi(&$node, $op, $arg) {
223
  if (user_access('create url aliases') || user_access('administer url aliases')) {
Dries's avatar
   
Dries committed
224
    switch ($op) {
225
226
227
228
229
230
231
232
233
234
      case 'validate':
        $node->path = trim($node->path);
        if ($node->path && !valid_url($node->path)) {
          form_set_error('path', t('The path is invalid.'));
        }
        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.'));
        }
        break;

Dries's avatar
   
Dries committed
235
236
      case 'load':
        $path = "node/$node->nid";
237
238
239
240
241
        // 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
242
243
244
        }
        break;

245
246
247
248
249
250
251
      case 'insert':
        // Don't try to insert if path is NULL. We may have already set
        // the alias ahead of time.
        if ($node->path) {
          path_set_alias("node/$node->nid", $node->path);
        }
        break;
Dries's avatar
   
Dries committed
252

253
254
255
      case 'update':
        path_set_alias("node/$node->nid", $node->path, $node->pid);
        break;
256

257
258
259
260
261
262
263
      case 'delete':
        $path = "node/$node->nid";
        if (drupal_get_path_alias($path) != $path) {
          path_set_alias($path);
        }
        break;
    }
264
  }
Dries's avatar
Dries committed
265
266
}

267
268
269
270
/**
 * Implementation of hook_form_alter().
 */
function path_form_alter($form_id, &$form) {
271
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
272
273
274
275
276
    $path = $form['#node']->path;
    $form['path'] = array(
      '#type' => 'fieldset',
      '#title' => t('URL path settings'),
      '#collapsible' => TRUE,
277
      '#collapsed' => empty($path),
278
      '#access' => user_access('create url aliases'),
279
      '#weight' => 30,
280
281
282
283
284
285
286
    );
    $form['path']['path'] = array(
      '#type' => 'textfield',
      '#default_value' => $path,
      '#maxlength' => 250,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
287
      '#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.'),
288
    );
289
    if ($path) {
290
291
292
293
294
295
296
297
298
      $form['path']['pid'] = array(
        '#type' => 'value',
        '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s'", $path))
      );
    }
  }
}


299
300
301
/**
 * Implementation of hook_perm().
 */
Dries's avatar
Dries committed
302
function path_perm() {
303
  return array('create url aliases', 'administer url aliases');
Dries's avatar
Dries committed
304
305
}

306
307
308
/**
 * Return a listing of all defined URL aliases.
 */
Dries's avatar
Dries committed
309
function path_overview() {
310
  $sql = 'SELECT * FROM {url_alias}';
Dries's avatar
Dries committed
311
  $header = array(
Dries's avatar
   
Dries committed
312
313
    array('data' => t('Alias'), 'field' => 'dst', 'sort' => 'asc'),
    array('data' => t('System'), 'field' => 'src'),
Dries's avatar
   
Dries committed
314
    array('data' => t('Operations'), 'colspan' => '2')
Dries's avatar
Dries committed
315
316
317
318
  );
  $sql .= tablesort_sql($header);
  $result = pager_query($sql, 50);

319
  $destination = drupal_get_destination();
Dries's avatar
Dries committed
320
  while ($data = db_fetch_object($result)) {
321
    $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
322
323
  }

Dries's avatar
   
Dries committed
324
  if (!$rows) {
325
    $rows[] = array(array('data' => t('No URL aliases available.'), 'colspan' => '4'));
Dries's avatar
   
Dries committed
326
327
  }

328
  $output = theme('table', $header, $rows);
329
  $output .= theme('pager', NULL, 50, 0);
330
  return $output;
Dries's avatar
Dries committed
331
332
}

333
334
335
/**
 * Fetch a specific URL alias from the database.
 */
Dries's avatar
   
Dries committed
336
function path_load($pid) {
337
  return db_fetch_array(db_query('SELECT * FROM {url_alias} WHERE pid = %d', $pid));
Dries's avatar
   
Dries committed
338
}
Dries's avatar
Dries committed
339

340
/**
341
 * Verify that a new URL alias is valid
342
 */
343
function path_form_validate($form_id, $edit) {
344
345
346
  $src = $edit['src'];
  $dst = $edit['dst'];
  $pid = $edit['pid'];
Dries's avatar
Dries committed
347

Dries's avatar
   
Dries committed
348
  if (!valid_url($src)) {
349
    form_set_error('src', t('The system path %path is invalid.', array('%path' => $src)));
Dries's avatar
Dries committed
350
351
  }

Dries's avatar
   
Dries committed
352
  if (!valid_url($dst)) {
353
    form_set_error('dst', t('The alias %alias is invalid.', array('%alias' => $dst)));
Dries's avatar
Dries committed
354
355
  }

356
  if (db_result(db_query("SELECT COUNT(dst) FROM {url_alias} WHERE pid != %d AND dst = '%s'", $pid, $dst))) {
357
    form_set_error('dst', t('The alias %alias is already in use.', array('%alias' => $dst)));
Dries's avatar
   
Dries committed
358
  }
Dries's avatar
Dries committed
359
360
}

361
362
363
364
365
/**
 * Save a new URL alias to the database.
 */
function path_form_submit($form_id, $edit) {
  path_set_alias($edit['src'], $edit['dst'], $edit['pid']);
366

367
368
369
  drupal_set_message(t('The alias has been saved.'));
  return 'admin/build/path';
}