path.module 12.7 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>
Dries's avatar
Dries committed
34
', 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-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;
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
  $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.'));
185
  $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
      case 'load':
        $path = "node/$node->nid";
219 220 221 222 223
        // 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
224 225 226
        }
        break;

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

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

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

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


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

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

300
  $destination = drupal_get_destination();
Dries's avatar
Dries committed
301
  while ($data = db_fetch_object($result)) {
302
    $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
303 304
  }

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

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

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

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

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

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

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

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

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

353