Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.397
diff -u -p -r1.397 bootstrap.inc
--- includes/bootstrap.inc	11 Jun 2010 11:00:18 -0000	1.397
+++ includes/bootstrap.inc	11 Jun 2010 22:17:38 -0000
@@ -1238,7 +1238,7 @@ function drupal_unpack($obj, $field = 'd
  * - !variable: Indicates that the text should be inserted as-is. This is
  *   useful for inserting variables into things like e-mail. Example:
  *   @code
- *     $message[] = t("If you don't want to receive such e-mails, you can change your settings at !url.", array('!url' => url("user/$account->uid", array('absolute' => TRUE))));
+ *     $message[] = t("If you don't want to receive such e-mails, you can change your settings at !url.", array('!url' => url("user/$account->uid/edit", array('absolute' => TRUE))));
  *   @endcode
  * - @variable: Indicates that the text should be run through check_plain(), to
  *   escape HTML characters. Use this for any output that is displayed within a
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1176
diff -u -p -r1.1176 common.inc
--- includes/common.inc	9 Jun 2010 14:55:30 -0000	1.1176
+++ includes/common.inc	11 Jun 2010 22:17:39 -0000
@@ -1920,6 +1920,9 @@ function format_username($account) {
  * When creating links in modules, consider whether l() could be a better
  * alternative than url().
  *
+ * For generating entity URLs, use entity_url() instead of passing a hard-coded
+ * path to this function.
+ *
  * @param $path
  *   The internal path or external URL being linked to, such as "node/34" or
  *   "http://example.com/foo". A few notes:
@@ -2161,6 +2164,9 @@ function drupal_attributes(array $attrib
  * internal links output by modules should be generated by this function if
  * possible.
  *
+ * For generating links to entities, use the entity_l() function instead of
+ * passing a hard-coded path to this function.
+ *
  * @param $text
  *   The link text for the anchor tag.
  * @param $path
@@ -6447,7 +6453,7 @@ function entity_extract_ids($entity_type
   $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL;
   // If no bundle key provided, then we assume a single bundle, named after the
   // entity type.
-  $bundle = $info['entity keys']['bundle'] ? $entity->{$info['entity keys']['bundle']} : $entity_type;
+  $bundle = ($info['entity keys']['bundle'] && isset($entity->{$info['entity keys']['bundle']})) ? $entity->{$info['entity keys']['bundle']} : $entity_type;
   return array($id, $vid, $bundle);
 }
 
@@ -6572,52 +6578,217 @@ function entity_prepare_view($entity_typ
 }
 
 /**
- * Returns the uri elements of an entity.
+ * Deprecated alias of entity_url_arguments().
+ *
+ * @todo This function is retained in Drupal 7 to preserve compatibility with
+ *   Drupal 7 modules authored prior to the Drupal 7 beta release. It does not,
+ *   however, return a URI, and should not be confused with entity_url(), which
+ *   does return a URL. For Drupal 8, evaluate whether all usage of the term URL
+ *   should be replaced with URI, and if so, rename all related functions
+ *   accordingly.
+ *
+ * @see entity_url_arguments()
+ */
+function entity_uri($entity_type, $entity, $extra_options = NULL) {
+  return entity_url_arguments($entity_type, $entity, $extra_options);
+}
+
+/**
+ * Returns the 'path' and 'options' of an entity for passing to url().
+ *
+ * All Drupal code that outputs links to an entity or otherwise needs the URL of
+ * an entity should call this function, either directly, or indirectly as part
+ * of entity_url(), entity_l(), or entity_link_item(), rather than relying on a
+ * hard-coded Drupal path.
+ *
+ * This function enables modules to control an entity's URL, and therefore, the
+ * page callback function to display the entity, via the 'uri callback' of
+ * hook_entity_info() and hook_entity_info_alter(), without resorting to using
+ * hook_url_outbound_alter(), which would negatively impact performance as that
+ * hook runs for every call to url(). An example of a module that needs to
+ * control the URL of some entities is the Forum module, where the path of
+ * taxonomy terms that are in the Forums vocabulary needs to be forum/TERM_ID
+ * instead of taxonomy/term/TERM_ID.
+ *
+ * Note that while the terms URL (Uniform Resource Locator) and URI (Uniform
+ * Resource Identifier) aren't strictly synonymous
+ * (http://www.w3.org/TR/uri-clarification/), they are interchangeable in the
+ * context of Drupal entities, because all Drupal entity URIs are also URLs. Web
+ * specifications increasingly refer to URIs rather than URLs, so the callback
+ * function for entity modules to set within hook_entity_info() is named 'uri
+ * callback'. However, this function is named entity_url_arguments(), because
+ * what this function returns is arguments to pass to the url() function.
+ *
+ * This function is often called implicitly as part of entity_url(), entity_l(),
+ * or entity_link_item(). Please see the documentation of those functions for
+ * examples of their use. A common use-case for calling this function directly
+ * is for redirecting a user to an entity page from a form submission handler.
+ *
+ * Example:
+ * @code
+ *   // Incorrect
+ *   $form_state['redirect'] = 'node/' . $node->nid;
+ *
+ *   // Correct
+ *   $form_state['redirect'] = entity_url_arguments('node', $node);
+ * @endcode
  *
  * @param $entity_type
  *   The entity type; e.g. 'node' or 'user'.
  * @param $entity
- *   The entity for which to generate a path.
- * @return
- *   An array containing the 'path' and 'options' keys used to build the uri of
- *   the entity, and matching the signature of url(). NULL if the entity has no
- *   uri of its own.
+ *   The entity for which to retrieve the 'path' and 'options'.
+ * @param $extra_options
+ *   (Optional) A keyed array of additional options to merge with the options
+ *   that are intrinsically part of the entity. See url() for a description of
+ *   supported option keys. For example, comment entities intrinsically have a
+ *   'fragment' option, since by default, comments appear primarily as part of a
+ *   node page rather than on their own page. However, most entities do not
+ *   intrinsically specify a 'language' option. To get the URL of an entity page
+ *   in a specific language, the 'language' option can be included within this
+ *   parameter. Values for scalar options, like 'fragment', that are included in
+ *   this parameter override the ones intrinsic to the entity, while values for
+ *   array options, like 'query', are merged with the entity's intrinsic values. 
+ *
+ * @return
+ *   An array containing the 'path' and 'options' keys matching the signature of
+ *   url(). NULL if the entity has no URL of its own.
+ *
+ * @see url()
+ * @see entity_url()
+ * @see entity_l()
+ * @see entity_link_item()
  */
-function entity_uri($entity_type, $entity) {
-  // This check enables the URI of an entity to be easily overridden from what
+function entity_url_arguments($entity_type, $entity, $extra_options = NULL) {
+  // This check enables the URL of an entity to be easily overridden from what
   // the callback for the entity type or bundle would return, and it helps
-  // minimize performance overhead when entity_uri() is called multiple times
-  // for the same entity.
-  if (!isset($entity->uri)) {
+  // minimize performance overhead when this function is called multiple times
+  // for the same entity. The check is entity type specific, because in some
+  // cases, code uses an entity object of one type as a mock entity of another
+  // type (for example, node and comment entities are passed to
+  // theme('username') which treats the passed entity as a mock user entity).
+  if (!isset($entity->url_arguments[$entity_type])) {
     $info = entity_get_info($entity_type);
     list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
 
     // A bundle-specific callback takes precedence over the generic one for the
     // entity type.
     if (isset($info['bundles'][$bundle]['uri callback'])) {
-      $uri_callback = $info['bundles'][$bundle]['uri callback'];
+      $callback = $info['bundles'][$bundle]['uri callback'];
     }
     elseif (isset($info['uri callback'])) {
-      $uri_callback = $info['uri callback'];
-    }
-    else {
-      $uri_callback = NULL;
+      $callback = $info['uri callback'];
     }
 
-    // Invoke the callback to get the URI. If there is no callback, set the
-    // entity's 'uri' property to FALSE to indicate that it is known to not have
-    // a URI.
-    if (isset($uri_callback) && function_exists($uri_callback)) {
-      $entity->uri = $uri_callback($entity);
-      if (!isset($entity->uri['options'])) {
-        $entity->uri['options'] = array();
-      }
+    // Invoke the entity type's or bundle's callback. If there is no callback,
+    // set the 'url_arguments' property to FALSE to indicate that it is known to
+    // not have its own URL.
+    if (isset($callback) && function_exists($callback)) {
+      $entity->url_arguments[$entity_type] = $callback($entity) + array('options' => array());
     }
     else {
-      $entity->uri = FALSE;
+      $entity->url_arguments[$entity_type] = FALSE;
+    }
+  }
+
+  // If the entity has a URL, merge the extra options and return the 'path' and
+  // 'options'. Otherwise, return NULL.
+  if ($url_arguments = $entity->url_arguments[$entity_type]) {
+    if (isset($extra_options)) {
+      // Many entities do not specify any intrinsic option values, so only call
+      // drupal_array_merge_deep() when necessary.
+      $url_arguments['options'] = $url_arguments['options'] ? drupal_array_merge_deep($url_arguments['options'], $extra_options) : $extra_options;
     }
+    // Ensure the returned array has 'path' first and 'options' second, so that
+    // it can be used as the value of $form_state['redirect'].
+    return array('path' => $url_arguments['path'], 'options' => $url_arguments['options']);
+  }
+}
+
+/**
+ * Returns the URL of an entity.
+ *
+ * This function should be called instead of url() when generating the URL of an
+ * entity. See entity_url_arguments() for more details about why this is
+ * important.
+ *
+ * Example:
+ * @code
+ *   // Incorrect
+ *   $variables['node_uri_absolute'] = url('node/' . $node->nid, array('absolute' => TRUE));
+ *
+ *   // Correct
+ *   $variables['node_uri_absolute'] = entity_url('node', $node, array('absolute' => TRUE));
+ * @endcode
+ *
+ * @see entity_url_arguments()
+ * @see url()
+ */
+function entity_url($entity_type, $entity, $extra_options = NULL) {
+  if ($url_arguments = entity_url_arguments($entity_type, $entity, $extra_options)) {
+    return url($url_arguments['path'], $url_arguments['options']);
+  }
+}
+
+/**
+ * Formats an entity link as an HTML anchor tag.
+ *
+ * This function should be called instead of l() when generating a link to an
+ * entity. See entity_url_arguments() for more details about why this is
+ * important.
+ *
+ * Example:
+ * @code
+ *   // Incorrect
+ *   $output .= l($node->title, 'node/' . $node->nid);
+ *
+ *   // Correct
+ *   $output .= entity_l($node->title, 'node', $node);
+ * @endcode
+ *
+ * @see entity_url_arguments()
+ * @see l()
+ */
+function entity_l($text, $entity_type, $entity, $extra_options = NULL) {
+  // $extra_options may include options like 'attributes' that only affect the
+  // link and not the URL. See the l() function for details.
+  if ($l_arguments = entity_url_arguments($entity_type, $entity, $extra_options)) {
+    return l($text, $l_arguments['path'], $l_arguments['options']);
+  }
+}
+
+/**
+ * Returns an array defining a link that can be added as an item of the $links array used by theme_links().
+ *
+ * This function should be called when adding link items to be rendered by
+ * theme('links'). See entity_url_arguments() for more details about why this is
+ * important.
+ *
+ * Example:
+ * @code
+ *   if ($view_mode == 'teaser') {
+ *     // Incorrect
+ *     $node->content['links']['node']['#links']['node-readmore'] = array(
+ *       'title' => t('Read more'),
+ *       'href' => 'node/' . $node->nid,
+ *       'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)),
+ *     );
+ *
+ *     // Correct
+ *     $node->content['links']['node']['#links']['node-readmore'] = entity_link_item(t('Read more'), 'node', $node, array(
+ *       'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)),
+ *     ));
+ *   }
+ * @endcode
+ *
+ * @see entity_url_arguments()
+ * @see theme_links()
+ */
+function entity_link_item($text, $entity_type, $entity, $extra_options = NULL) {
+  // $extra_options may include options like 'attributes' that only affect the
+  // link and not the URL. See the l() function for details.
+  if ($l_arguments = entity_url_arguments($entity_type, $entity, $extra_options)) {
+    return array('title' => $text, 'href' => $l_arguments['path']) + $l_arguments['options'];
   }
-  return $entity->uri ? $entity->uri : NULL;
 }
 
 /**
@@ -6743,3 +6914,100 @@ function drupal_get_updaters() {
   }
   return $updaters;
 }
+
+/**
+ * Merges multiple arrays, recursively, and returns the merged array.
+ *
+ * This function is similar to PHP's array_merge_recursive() function, but it
+ * handles non-array values differently. When merging values that are not both
+ * arrays, the latter value replaces the former rather than merging with it.
+ *
+ * Example:
+ * @code
+ * $link_options_1 = array(
+ *   'fragment' => 'x',
+ *   'attributes' => array(
+ *     'title' => t('X'),
+ *     'class' => array('a', 'b'),
+ *   ),
+ * );
+ * $link_options_2 = array(
+ *   'fragment' => 'y',
+ *   'attributes' => array(
+ *     'title' => t('Y'),
+ *     'class' => array('c', 'd'),
+ *   ),
+ * );
+ *
+ * // This results in array(
+ * //   'fragment' => array('x', 'y'),
+ * //   'attributes' => array(
+ * //     'title' => array(t('X'), t('Y')),
+ * //     'class' => array('a', 'b', 'c', 'd'),
+ * //   ),
+ * // ).
+ * $incorrect = array_merge_recursive($link_options_1, $link_options_2);
+ *
+ * // This results in array(
+ * //   'fragment' => 'y',
+ * //   'attributes' => array(
+ * //     'title' => t('Y'),
+ * //     'class' => array('a', 'b', 'c', 'd'),
+ * //   ),
+ * // ).
+ * $correct = drupal_array_merge_deep($link_options_1, $link_options_2);
+ * @endcode
+ *
+ * @param ...
+ *   Arrays to merge.
+ *
+ * @return
+ *   The merged array.
+ *
+ * @see drupal_array_merge_deep_array()
+ */
+function drupal_array_merge_deep() {
+  return drupal_array_merge_deep_array(func_get_args());
+}
+
+/**
+ * Merges multiple arrays, recursively, and returns the merged array.
+ *
+ * This function is equivalent to drupal_array_merge_deep(), except the input
+ * arrays are passed as a single array parameter rather than a variable
+ * parameter list.
+ *
+ * The following are equivalent:
+ * - drupal_array_merge_deep($a, $b);
+ * - drupal_array_merge_deep_array(array($a, $b));
+ *
+ * The following are also equivalent:
+ * - call_user_func_array('drupal_array_merge_deep', $arrays_to_merge);
+ * - drupal_array_merge_deep_array($arrays_to_merge);
+ *
+ * @see drupal_array_merge_deep()
+ */
+function drupal_array_merge_deep_array($arrays) {
+  $result = array();
+
+  foreach ($arrays as $array) {
+    foreach ($array as $key => $value) {
+      // Renumber integer keys as array_merge_recursive() does. PHP
+      // automatically converts array keys that are integer strings (e.g., '1')
+      // to integers.
+      if (is_integer($key)) {
+        $result[] = $value;
+      }
+      // Recurse when both values are arrays.
+      elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
+        $result[$key] = drupal_array_merge_deep_array(array($result[$key], $value));
+      }
+      // Otherwise, use the latter value, overriding any previous value.
+      else {
+        $result[$key] = $value;
+      }
+    }
+  }
+
+  return $result;
+}
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.599
diff -u -p -r1.599 theme.inc
--- includes/theme.inc	7 Jun 2010 06:38:09 -0000	1.599
+++ includes/theme.inc	11 Jun 2010 22:17:40 -0000
@@ -2506,7 +2506,7 @@ function template_preprocess_username(&$
   if ($variables['uid'] && $variables['profile_access']) {
     // We are linking to a local user.
     $variables['link_attributes'] = array('title' => t('View user profile.'));
-    $variables['link_path'] = 'user/' . $variables['uid'];
+    list($variables['link_path'], $variables['link_options']) = array_values(entity_url_arguments('user', $account));
   }
   elseif (!empty($account->homepage)) {
     // Like the 'class' attribute, the 'rel' attribute can hold a
Index: modules/book/book.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.admin.inc,v
retrieving revision 1.35
diff -u -p -r1.35 book.admin.inc
--- modules/book/book.admin.inc	1 May 2010 08:12:22 -0000	1.35
+++ modules/book/book.admin.inc	11 Jun 2010 22:17:41 -0000
@@ -132,7 +132,7 @@ function book_admin_edit_submit($form, &
         $node->log = t('Title changed from %original to %current.', array('%original' => $node->title, '%current' => $values['title']));
 
         node_save($node);
-        watchdog('content', 'book: updated %title.', array('%title' => $node->title), WATCHDOG_NOTICE, l(t('view'), 'node/' . $node->nid));
+        watchdog('content', 'book: updated %title.', array('%title' => $node->title), WATCHDOG_NOTICE, entity_l(t('view'), 'node', $node));
       }
     }
   }
Index: modules/book/book.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.pages.inc,v
retrieving revision 1.26
diff -u -p -r1.26 book.pages.inc
--- modules/book/book.pages.inc	11 Jun 2010 11:06:37 -0000	1.26
+++ modules/book/book.pages.inc	11 Jun 2010 22:17:41 -0000
@@ -154,7 +154,7 @@ function book_remove_button_submit($form
  */
 function book_outline_form_submit($form, &$form_state) {
   $node = $form['#node'];
-  $form_state['redirect'] = "node/" . $node->nid;
+  $form_state['redirect'] = entity_url_arguments('node', $node);
   $book_link = $form_state['values']['book'];
   if (!$book_link['bid']) {
     drupal_set_message(t('No changes were made'));
@@ -197,7 +197,7 @@ function book_remove_form($form, &$form_
     $description = t('%title may be added to hierarchy again using the Outline tab.', $title);
   }
 
-  return confirm_form($form, t('Are you sure you want to remove %title from the book hierarchy?', $title), 'node/' . $node->nid, $description, t('Remove'));
+  return confirm_form($form, t('Are you sure you want to remove %title from the book hierarchy?', $title), entity_url_arguments('node', $node), $description, t('Remove'));
 }
 
 /**
@@ -215,7 +215,7 @@ function book_remove_form_submit($form, 
       ->execute();
     drupal_set_message(t('The post has been removed from the book.'));
   }
-  $form_state['redirect'] = 'node/' . $node->nid;
+  $form_state['redirect'] = entity_url_arguments('node', $node);
 }
 
 /**
Index: modules/comment/comment.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.admin.inc,v
retrieving revision 1.46
diff -u -p -r1.46 comment.admin.inc
--- modules/comment/comment.admin.inc	31 Mar 2010 11:49:50 -0000	1.46
+++ modules/comment/comment.admin.inc	11 Jun 2010 22:17:41 -0000
@@ -90,6 +90,15 @@ function comment_admin_overview($form, &
   }
   $comments = comment_load_multiple($cids);
 
+  // Load all the nodes for which there are comments being listed, in order to
+  // add links to them in the "posted_in" column.
+  $nids = array();
+  foreach ($comments as $comment) {
+    $nids[] = $comment->nid;
+  }
+  $nids = array_unique($nids);
+  $nodes = node_load_multiple($nids);
+
   // Build a table listing the appropriate comments.
   $options = array();
   $destination = drupal_get_destination();
@@ -98,13 +107,15 @@ function comment_admin_overview($form, &
     // Remove the first node title from the node_titles array and attach to
     // the comment.
     $comment->node_title = array_shift($node_titles);
+    $comment_url_arguments = entity_url_arguments('comment', $comment);
+    $node_url_arguments = entity_url_arguments('node', $nodes[$comment->nid]);
     $options[$comment->cid] = array(
       'subject' => array(
         'data' => array(
           '#type' => 'link',
           '#title' => $comment->subject,
-          '#href' => 'comment/' . $comment->cid,
-          '#options' => array('attributes' => array('title' => truncate_utf8($comment->comment_body[LANGUAGE_NONE][0]['value'], 128)), 'fragment' => 'comment-' . $comment->cid),
+          '#href' => $comment_url_arguments['path'],
+          '#options' => $comment_url_arguments['options'] + array('attributes' => array('title' => truncate_utf8($comment->comment_body[LANGUAGE_NONE][0]['value'], 128))),
         ),
       ),
       'author' => theme('username', array('account' => $comment)),
@@ -112,7 +123,8 @@ function comment_admin_overview($form, &
         'data' => array(
           '#type' => 'link',
           '#title' => $comment->node_title,
-          '#href' => 'node/' . $comment->nid,
+          '#href' => $node_url_arguments['path'],
+          '#options' => $node_url_arguments['options'],
         ),
       ),
       'changed' => format_date($comment->changed, 'short'),
@@ -260,7 +272,7 @@ function comment_confirm_delete($form, &
   return confirm_form(
     $form,
     t('Are you sure you want to delete the comment %title?', array('%title' => $comment->subject)),
-    'node/' . $comment->nid,
+    entity_url_arguments('node', node_load($comment->nid)),
     t('Any replies to this comment will be lost. This action cannot be undone.'),
     t('Delete'),
     t('Cancel'),
@@ -279,5 +291,5 @@ function comment_confirm_delete_submit($
   // Clear the cache so an anonymous user sees that his comment was deleted.
   cache_clear_all();
 
-  $form_state['redirect'] = "node/$comment->nid";
+  $form_state['redirect'] = entity_url_arguments('node', node_load($comment->nid));
 }
Index: modules/comment/comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.881
diff -u -p -r1.881 comment.module
--- modules/comment/comment.module	10 Jun 2010 06:57:20 -0000	1.881
+++ modules/comment/comment.module	11 Jun 2010 22:17:41 -0000
@@ -580,7 +580,7 @@ function theme_comment_block() {
   $items = array();
   $number = variable_get('comment_block_count', 10);
   foreach (comment_get_recent($number) as $comment) {
-    $items[] = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)) .'<span>'. t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->changed))) .'</span>';
+    $items[] = entity_l($comment->subject, 'comment', $comment) .'<span>'. t('@time ago', array('@time' => format_interval(REQUEST_TIME - $comment->changed))) .'</span>';
   }
 
   if ($items) {
@@ -603,7 +603,7 @@ function comment_node_view($node, $view_
         // Add a comments RSS element which is a URL to the comments of this node.
         $node->rss_elements[] = array(
           'key' => 'comments',
-          'value' => url('node/' . $node->nid, array('fragment' => 'comments', 'absolute' => TRUE))
+          'value' => entity_url('node', $node, array('fragment' => 'comments', 'absolute' => TRUE)),
         );
       }
     }
@@ -613,24 +613,20 @@ function comment_node_view($node, $view_
       // is open to new comments, and there currently are none.
       if (user_access('access comments')) {
         if (!empty($node->comment_count)) {
-          $links['comment-comments'] = array(
-            'title' => format_plural($node->comment_count, '1 comment', '@count comments'),
-            'href' => "node/$node->nid",
+          $links['comment-comments'] = entity_link_item(format_plural($node->comment_count, '1 comment', '@count comments'), 'node', $node, array(
             'attributes' => array('title' => t('Jump to the first comment of this posting.')),
             'fragment' => 'comments',
             'html' => TRUE,
-          );
+          ));
 
           $new = comment_num_new($node->nid);
           if (!$new) {
-            $links['comment-new-comments'] = array(
-              'title' => format_plural($new, '1 new comment', '@count new comments'),
-              'href' => "node/$node->nid",
+            $links['comment-new-comments'] = entity_link_item(format_plural($new, '1 new comment', '@count new comments'), 'node', $node, array(
               'query' => comment_new_page_count($node->comment_count, $new, $node),
               'attributes' => array('title' => t('Jump to the first new comment of this posting.')),
               'fragment' => 'new',
               'html' => TRUE,
-            );
+            ));
           }
         }
         else {
@@ -668,7 +664,9 @@ function comment_node_view($node, $view_
             $links['comment-add']['href'] = "comment/reply/$node->nid";
           }
           else {
-            $links['comment-add']['href'] = "node/$node->nid";
+            $url_arguments = entity_url_arguments('node', $node);
+            $links['comment-add']['href'] = $url_arguments['path'];
+            $links['comment-add'] += $url_arguments['options'];
           }
         }
         else {
@@ -2147,7 +2145,7 @@ function comment_form_submit($form, &$fo
     $form_state['values']['cid'] = $comment->cid;
 
     // Add an entry to the watchdog log.
-    watchdog('content', 'Comment posted: %subject.', array('%subject' => $comment->subject), WATCHDOG_NOTICE, l(t('view'), 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)));
+    watchdog('content', 'Comment posted: %subject.', array('%subject' => $comment->subject), WATCHDOG_NOTICE, entity_l(t('view'), 'comment', $comment));
 
     // Explain the approval queue if necessary.
     if ($comment->status == COMMENT_NOT_PUBLISHED) {
@@ -2165,13 +2163,13 @@ function comment_form_submit($form, &$fo
       $query['page'] = $page;
     }
     // Redirect to the newly posted comment.
-    $redirect = array('node/' . $node->nid, array('query' => $query, 'fragment' => 'comment-' . $comment->cid));
+    $redirect = entity_url_arguments('comment', $comment);
   }
   else {
     watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject), WATCHDOG_WARNING);
     drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject)), 'error');
     // Redirect the user to the node they are commenting on.
-    $redirect = 'node/' . $node->nid;
+    $redirect = entity_url_arguments('node', $node);
   }
   unset($form_state['rebuild']);
   $form_state['redirect'] = $redirect;
@@ -2198,9 +2196,8 @@ function template_preprocess_comment(&$v
   $variables['picture']   = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', array('account' => $comment)) : '';
   $variables['signature'] = $comment->signature;
 
-  $uri = entity_uri('comment', $comment);
-  $variables['title']     = l($comment->subject, $uri['path'], $uri['options']);
-  $variables['permalink'] = l('#', $uri['path'], $uri['options']);
+  $variables['title']     = entity_l($comment->subject, 'comment', $comment);
+  $variables['permalink'] = entity_l('#', 'comment', $comment);
 
   // Preprocess fields.
   field_attach_preprocess('comment', $comment, $variables['elements'], $variables);
@@ -2271,7 +2268,8 @@ function theme_comment_post_forbidden($v
         $destination = array('destination' => "comment/reply/$node->nid#comment-form");
       }
       else {
-        $destination = array('destination' => "node/$node->nid#comment-form");
+        $url_arguments = entity_url_arguments('node', $node);
+        $destination = array('destination' => $url_arguments['path'] . '#comment-form');
       }
 
       if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) {
Index: modules/comment/comment.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.pages.inc,v
retrieving revision 1.39
diff -u -p -r1.39 comment.pages.inc
--- modules/comment/comment.pages.inc	10 Jun 2010 06:57:20 -0000	1.39
+++ modules/comment/comment.pages.inc	11 Jun 2010 22:17:41 -0000
@@ -29,7 +29,7 @@
  */
 function comment_reply($node, $pid = NULL) {
   // Set the breadcrumb trail.
-  drupal_set_breadcrumb(array(l(t('Home'), NULL), l($node->title, 'node/' . $node->nid)));
+  drupal_set_breadcrumb(array(l(t('Home'), NULL), entity_l($node->title, 'node', $node)));
   $op = isset($_POST['op']) ? $_POST['op'] : '';
   $build = array();
 
@@ -41,7 +41,7 @@ function comment_reply($node, $pid = NUL
       }
       else {
         drupal_set_message(t('You are not authorized to post comments.'), 'error');
-        drupal_goto("node/$node->nid");
+        call_user_func_array('drupal_goto', entity_url_arguments('node', $node));
       }
     }
     else {
@@ -58,7 +58,7 @@ function comment_reply($node, $pid = NUL
           if ($comment->nid != $node->nid) {
             // Attempting to reply to a comment not belonging to the current nid.
             drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
-            drupal_goto("node/$node->nid");
+            call_user_func_array('drupal_goto', entity_url_arguments('node', $node));
           }
           // Display the parent comment
           $comment->node_type = 'comment_node_' . $node->type;
@@ -68,7 +68,7 @@ function comment_reply($node, $pid = NUL
         }
         else {
           drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
-          drupal_goto("node/$node->nid");
+          call_user_func_array('drupal_goto', entity_url_arguments('node', $node));
         }
       }
       // This is the case where the comment is in response to a node. Display the node.
@@ -79,7 +79,7 @@ function comment_reply($node, $pid = NUL
       // Should we show the reply box?
       if ($node->comment != COMMENT_NODE_OPEN) {
         drupal_set_message(t("This discussion is closed: you can't post new comments."), 'error');
-        drupal_goto("node/$node->nid");
+        call_user_func_array('drupal_goto', entity_url_arguments('node', $node));
       }
       elseif (user_access('post comments')) {
         $edit = array('nid' => $node->nid, 'pid' => $pid);
@@ -87,13 +87,13 @@ function comment_reply($node, $pid = NUL
       }
       else {
         drupal_set_message(t('You are not authorized to post comments.'), 'error');
-        drupal_goto("node/$node->nid");
+        call_user_func_array('drupal_goto', entity_url_arguments('node', $node));
       }
     }
   }
   else {
     drupal_set_message(t('You are not authorized to view comments.'), 'error');
-    drupal_goto("node/$node->nid");
+    call_user_func_array('drupal_goto', entity_url_arguments('node', $node));
   }
 
   return $build;
Index: modules/comment/comment.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.tokens.inc,v
retrieving revision 1.11
diff -u -p -r1.11 comment.tokens.inc
--- modules/comment/comment.tokens.inc	20 Apr 2010 09:48:06 -0000	1.11
+++ modules/comment/comment.tokens.inc	11 Jun 2010 22:17:41 -0000
@@ -187,8 +187,7 @@ function comment_tokens($type, $tokens, 
 
         // Comment related URLs.
         case 'url':
-          $url_options['fragment']  = 'comment-' . $comment->cid;
-          $replacements[$original] = url('comment/' . $comment->cid, $url_options);
+          $replacements[$original] = entity_url('comment', $comment, $url_options);
           break;
 
         case 'edit-url':
Index: modules/contact/contact.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.module,v
retrieving revision 1.147
diff -u -p -r1.147 contact.module
--- modules/contact/contact.module	13 Apr 2010 15:13:41 -0000	1.147
+++ modules/contact/contact.module	11 Jun 2010 22:17:41 -0000
@@ -178,7 +178,7 @@ function contact_mail($key, &$message, $
     '!category' => isset($params['category']['category']) ? $params['category']['category'] : '',
     '!form-url' => url($_GET['q'], array('absolute' => TRUE, 'language' => $language)),
     '!sender-name' => format_username($params['sender']),
-    '!sender-url' => $params['sender']->uid ? url('user/' . $params['sender']->uid, array('absolute' => TRUE, 'language' => $language)) : $params['sender']->mail,
+    '!sender-url' => $params['sender']->uid ? entity_url('user', $params['sender'], array('absolute' => TRUE, 'language' => $language)) : $params['sender']->mail,
   );
 
   switch ($key) {
Index: modules/contact/contact.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/contact/contact.pages.inc,v
retrieving revision 1.43
diff -u -p -r1.43 contact.pages.inc
--- modules/contact/contact.pages.inc	24 Apr 2010 14:49:13 -0000	1.43
+++ modules/contact/contact.pages.inc	11 Jun 2010 22:17:41 -0000
@@ -286,5 +286,5 @@ function contact_personal_form_submit($f
 
   // Jump to the contacted user's profile page.
   drupal_set_message(t('Your message has been sent.'));
-  $form_state['redirect'] = user_access('access user profiles') ? 'user/' . $values['recipient']->uid : '';
+  $form_state['redirect'] = user_access('access user profiles') ? entity_url_arguments('user', $values['recipient']) : '';
 }
Index: modules/forum/forum.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/forum/forum.module,v
retrieving revision 1.566
diff -u -p -r1.566 forum.module
--- modules/forum/forum.module	29 May 2010 07:53:44 -0000	1.566
+++ modules/forum/forum.module	11 Jun 2010 22:17:42 -0000
@@ -1102,7 +1102,7 @@ function template_preprocess_forum_topic
       }
       else {
         $variables['topics'][$id]->moved = FALSE;
-        $variables['topics'][$id]->title = l($topic->title, "node/$topic->nid");
+        $variables['topics'][$id]->title = entity_l($topic->title, 'node', $topic);
         $variables['topics'][$id]->message = '';
       }
       $topic->uid = $topic->last_comment_uid ? $topic->last_comment_uid : $topic->uid;
@@ -1113,7 +1113,7 @@ function template_preprocess_forum_topic
       $variables['topics'][$id]->new_url = '';
       if ($topic->new_replies) {
         $variables['topics'][$id]->new_text = format_plural($topic->new_replies, '1 new', '@count new');
-        $variables['topics'][$id]->new_url = url("node/$topic->nid", array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
+        $variables['topics'][$id]->new_url = entity_url('node', $topic, array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'));
       }
 
     }
Index: modules/image/image.field.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/image/image.field.inc,v
retrieving revision 1.21
diff -u -p -r1.21 image.field.inc
--- modules/image/image.field.inc	30 Apr 2010 12:53:47 -0000	1.21
+++ modules/image/image.field.inc	11 Jun 2010 22:17:42 -0000
@@ -471,7 +471,7 @@ function image_field_formatter_view($ent
 
   // Check if the formatter involves a link.
   if (strpos($display['type'], 'image_link_content') === 0) {
-    $uri = entity_uri($entity_type, $entity);
+    $url_arguments = entity_url_arguments($entity_type, $entity);
   }
   elseif (strpos($display['type'], 'image_link_file') === 0) {
     $link_file = TRUE;
@@ -479,7 +479,7 @@ function image_field_formatter_view($ent
 
   foreach ($items as $delta => $item) {
     if (isset($link_file)) {
-      $uri = array(
+      $url_arguments = array(
         'path' => file_create_url($item['uri']),
         'options' => array(),
       );
@@ -488,7 +488,7 @@ function image_field_formatter_view($ent
       '#theme' => 'image_formatter',
       '#item' => $item,
       '#image_style' => isset($image_style) ? $image_style : '',
-      '#path' => isset($uri) ? $uri : '',
+      '#path' => isset($url_arguments) ? $url_arguments : '',
     );
   }
 
Index: modules/node/node.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v
retrieving revision 1.94
diff -u -p -r1.94 node.admin.inc
--- modules/node/node.admin.inc	24 Apr 2010 14:49:14 -0000	1.94
+++ modules/node/node.admin.inc	11 Jun 2010 22:17:43 -0000
@@ -344,7 +344,7 @@ function _node_mass_update_batch_process
     $node = _node_mass_update_helper($nid, $updates);
 
     // Store result for post-processing in the finished callback.
-    $context['results'][] = l($node->title, 'node/' . $node->nid);
+    $context['results'][] = entity_l($node->title, 'node', $node);
 
     // Update our progress information.
     $context['sandbox']['progress']++;
@@ -463,14 +463,14 @@ function node_admin_nodes() {
   $destination = drupal_get_destination();
   $options = array();
   foreach ($nodes as $node) {
-    $l_options = $node->language != LANGUAGE_NONE ? array('language' => $languages[$node->language]) : array();
+    $url_arguments = entity_url_arguments('node', $node, $node->language != LANGUAGE_NONE ? array('language' => $languages[$node->language]) : NULL);
     $options[$node->nid] = array(
       'title' => array(
         'data' => array(
           '#type' => 'link',
           '#title' => $node->title,
-          '#href' => 'node/' . $node->nid,
-          '#options' => $l_options,
+          '#href' => $url_arguments['path'],
+          '#options' => $url_arguments['options'],
           '#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))),
         ),
       ),
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.1277
diff -u -p -r1.1277 node.module
--- modules/node/node.module	31 May 2010 08:02:33 -0000	1.1277
+++ modules/node/node.module	11 Jun 2010 22:17:43 -0000
@@ -289,7 +289,7 @@ function node_title_list($result, $title
   $items = array();
   $num_rows = FALSE;
   foreach ($result as $node) {
-    $items[] = l($node->title, 'node/' . $node->nid, !empty($node->comment_count) ? array('attributes' => array('title' => format_plural($node->comment_count, '1 comment', '@count comments'))) : array());
+    $items[] = entity_l($node->title, 'node', $node, !empty($node->comment_count) ? array('attributes' => array('title' => format_plural($node->comment_count, '1 comment', '@count comments'))) : array());
     $num_rows = TRUE;
   }
 
@@ -1275,11 +1275,9 @@ function node_build_content($node, $view
   // to know when a teaser view is different than a full view.
   $links = array();
   if ($view_mode == 'teaser') {
-    $links['node-readmore'] = array(
-      'title' => t('Read more'),
-      'href' => 'node/' . $node->nid,
-      'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title))
-    );
+    $links['node-readmore'] = entity_link_item(t('Read more'), 'node', $node, array(
+      'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)),
+    ));
   }
   $node->content['links']['node'] = array(
     '#theme' => 'links__node',
@@ -1348,8 +1346,7 @@ function template_preprocess_node(&$vari
   $variables['date']      = format_date($node->created);
   $variables['name']      = theme('username', array('account' => $node));
 
-  $uri = entity_uri('node', $node);
-  $variables['node_url']  = url($uri['path'], $uri['options']);
+  $variables['node_url']  = entity_url('node', $node);
   $variables['title']     = check_plain($node->title);
   $variables['page']      = node_is_page($node);
 
@@ -1575,9 +1572,8 @@ function node_search_execute($keys = NUL
 
     $extra = module_invoke_all('node_search_result', $node);
 
-    $uri = entity_uri('node', $node);
     $results[] = array(
-      'link' => url($uri['path'], array_merge($uri['options'], array('absolute' => TRUE))),
+      'link' => entity_url('node', $node, array('absolute' => TRUE)),
       'type' => check_plain(node_type_get_name($node)),
       'title' => $node->title,
       'user' => theme('username', array('account' => $node)),
@@ -2179,7 +2175,7 @@ function theme_node_recent_content($vari
   $node = $variables['node'];
 
   $output = '<div class="node-title">';
-  $output .= l($node->title, 'node/' . $node->nid);
+  $output .= entity_l($node->title, 'node', $node);
   $output .= theme('mark', array('type' => node_mark($node->nid, $node->changed)));
   $output .= '</div><div class="node-author">';
   $output .= theme('username', array('account' => user_load($node->uid)));
@@ -2378,7 +2374,7 @@ function node_feed($nids = FALSE, $chann
   foreach ($nodes as $node) {
     $item_text = '';
 
-    $node->link = url("node/$node->nid", array('absolute' => TRUE));
+    $node->link = entity_url('node', $node, array('absolute' => TRUE));
     $node->rss_namespaces = array();
     $node->rss_elements = array(
       array('key' => 'pubDate', 'value' => gmdate('r', $node->created)),
@@ -2503,11 +2499,10 @@ function node_page_default() {
  */
 function node_page_view($node) {
   drupal_set_title($node->title);
-  $uri = entity_uri('node', $node);
   // Set the node path as the canonical URL to prevent duplicate content.
-  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE);
+  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => entity_url('node', $node)), TRUE);
   // Set the non-aliased path as a default shortlink.
-  drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE);
+  drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => entity_url('node', $node, array('alias' => TRUE))), TRUE);
   return node_show($node);
 }
 
Index: modules/node/node.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.pages.inc,v
retrieving revision 1.126
diff -u -p -r1.126 node.pages.inc
--- modules/node/node.pages.inc	10 May 2010 06:34:39 -0000	1.126
+++ modules/node/node.pages.inc	11 Jun 2010 22:17:43 -0000
@@ -385,7 +385,7 @@ function node_form_submit($form, &$form_
   $node = node_form_submit_build_node($form, $form_state);
   $insert = empty($node->nid);
   node_save($node);
-  $node_link = l(t('view'), 'node/' . $node->nid);
+  $node_link = entity_l(t('view'), 'node', $node);
   $watchdog_args = array('@type' => $node->type, '%title' => $node->title);
   $t_args = array('@type' => node_type_get_name($node), '%title' => $node->title);
 
@@ -401,7 +401,7 @@ function node_form_submit($form, &$form_
     unset($form_state['rebuild']);
     $form_state['values']['nid'] = $node->nid;
     $form_state['nid'] = $node->nid;
-    $form_state['redirect'] = 'node/' . $node->nid;
+    $form_state['redirect'] = entity_url_arguments('node', $node);
   }
   else {
     // In the unlikely case something went wrong on save, the node will be
@@ -438,7 +438,7 @@ function node_delete_confirm($form, &$fo
   $form['nid'] = array('#type' => 'value', '#value' => $node->nid);
   return confirm_form($form,
     t('Are you sure you want to delete %title?', array('%title' => $node->title)),
-    'node/' . $node->nid,
+    entity_url_arguments('node', $node),
     t('This action cannot be undone.'),
     t('Delete'),
     t('Cancel')
@@ -483,7 +483,7 @@ function node_revision_overview($node) {
     $operations = array();
 
     if ($revision->current_vid > 0) {
-      $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision))))
+      $row[] = array('data' => t('!date by !username', array('!date' => entity_l(format_date($revision->timestamp, 'short'), 'node', $node), '!username' => theme('username', array('account' => $revision))))
                                . (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
                      'class' => array('revision-current'));
       $operations[] = array('data' => drupal_placeholder(array('text' => t('current revision'))), 'class' => array('revision-current'), 'colspan' => 2);
Index: modules/node/node.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.tokens.inc,v
retrieving revision 1.14
diff -u -p -r1.14 node.tokens.inc
--- modules/node/node.tokens.inc	20 Apr 2010 09:48:06 -0000	1.14
+++ modules/node/node.tokens.inc	11 Jun 2010 22:17:43 -0000
@@ -157,7 +157,7 @@ function node_tokens($type, $tokens, arr
           break;
 
         case 'url':
-          $replacements[$original] = url('node/' . $node->nid, $url_options);
+          $replacements[$original] = entity_url('node', $node, $url_options);
           break;
 
         case 'edit-url':
Index: modules/poll/poll.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/poll/poll.pages.inc,v
retrieving revision 1.27
diff -u -p -r1.27 poll.pages.inc
--- modules/poll/poll.pages.inc	9 Jan 2010 21:54:01 -0000	1.27
+++ modules/poll/poll.pages.inc	11 Jun 2010 22:17:43 -0000
@@ -39,7 +39,7 @@ function poll_page() {
 
   $output = '<ul>';
   foreach ($queried_nodes as $node) {
-    $output .= '<li>' . l($node->title, "node/$node->nid") . ' - ' . format_plural($node->votes, '1 vote', '@count votes') . ' - ' . ($node->active ? t('open') : t('closed')) . '</li>';
+    $output .= '<li>' . entity_l($node->title, 'node', $node) . ' - ' . format_plural($node->votes, '1 vote', '@count votes') . ' - ' . ($node->active ? t('open') : t('closed')) . '</li>';
   }
   $output .= '</ul>';
   $output .= theme("pager", array('tags' => NULL));
Index: modules/profile/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v
retrieving revision 1.291
diff -u -p -r1.291 profile.module
--- modules/profile/profile.module	29 May 2010 11:37:33 -0000	1.291
+++ modules/profile/profile.module	11 Jun 2010 22:17:43 -0000
@@ -199,7 +199,7 @@ function profile_block_view($delta = '')
       }
 
       if (isset($use_fields['user_profile']) && $use_fields['user_profile']) {
-        $output .= '<div>' . l(t('View full user profile'), 'user/' . $account->uid) . '</div>';
+        $output .= '<div>' . entity_l(t('View full user profile'), 'user', $account) . '</div>';
       }
     }
 
Index: modules/rdf/rdf.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/rdf/rdf.module,v
retrieving revision 1.40
diff -u -p -r1.40 rdf.module
--- modules/rdf/rdf.module	5 May 2010 15:49:04 -0000	1.40
+++ modules/rdf/rdf.module	11 Jun 2010 22:17:43 -0000
@@ -399,9 +399,22 @@ function rdf_entity_load($entities, $typ
 function rdf_comment_load($comments) {
   foreach ($comments as $comment) {
     $comment->rdf_data['date'] = rdf_rdfa_attributes($comment->rdf_mapping['created'], $comment->created);
-    $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid);
+    // We don't want to execute a potentially expensive node_load() every time
+    // we load a comment, just to add its URI to the RDF output. Instead, we
+    // create a stub entity that is sufficient for determining the URI, at least
+    // for the default node_uri() callback. If a module implements a custom
+    // 'uri callback' for nodes, and that callback requires more information
+    // than what is available in the stub entity, it may need to implement
+    // hook_comment_load() or hook_preprocess_comment() to fix this variable.
+    $comment->rdf_data['nid_uri'] = entity_url('node', entity_create_stub_entity('node', array($comment->nid, NULL, $comment->node_type)));
     if ($comment->pid) {
-      $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid, array('fragment' => 'comment-' . $comment->pid));
+      // Rather than use a database query to load the parent comment, we assume
+      // that it is for the same node, and therefore, the same node type. As
+      // explained above for the nid_uri, if a module implements a custom
+      // 'uri callback' for comments for which a stub entity is insufficient, it
+      // may need to implement hook_comment_load() or hook_preprocess_comment()
+      // to fix this variable.
+      $comment->rdf_data['pid_uri'] = entity_url('comment', entity_create_stub_entity('comment', array($comment->pid, NULL, $comment->node_type)));
     }
   }
 }
@@ -564,30 +577,29 @@ function rdf_preprocess_field(&$variable
  */
 function rdf_preprocess_user_profile(&$variables) {
   $account = $variables['elements']['#account'];
-  $uri = entity_uri('user', $account);
 
   // Adds RDFa markup to the user profile page. Fields displayed in this page
   // will automatically describe the user.  
   if (!empty($account->rdf_mapping['rdftype'])) {
     $variables['attributes_array']['typeof'] = $account->rdf_mapping['rdftype'];
-    $variables['attributes_array']['about'] = url($uri['path'], $uri['options']);
+    $variables['attributes_array']['about'] = entity_url('user', $account);
   }
   // Adds the relationship between the sioc:UserAccount and the foaf:Person who
   // holds the account.
   $account_holder_meta = array(
     '#tag' => 'meta',
     '#attributes' => array(
-      'about' => url($uri['path'], array_merge($uri['options'], array('fragment' => 'me'))),
+      'about' => entity_url('user', $account, array('fragment' => 'me')),
       'typeof' => array('foaf:Person'),
       'rel' => array('foaf:account'),
-      'resource' => url($uri['path'], $uri['options']),
+      'resource' => entity_url('user', $account),
     ),
   );
   // Adds the markup for username.
   $username_meta = array(
     '#tag' => 'meta',
     '#attributes' => array(
-      'about' => url($uri['path'], $uri['options']),
+      'about' => entity_url('user', $account),
       'property' => $account->rdf_mapping['name']['predicates'],
       'content' => $account->name,
     )
@@ -625,7 +637,7 @@ function rdf_preprocess_username(&$varia
   // a user profile URI for it (only a homepage which cannot be used as user
   // profile in RDF).
   if ($variables['uid'] > 0) {
-    $variables['attributes_array']['about'] = url('user/' . $variables['uid']);
+    $variables['attributes_array']['about'] = entity_url('user', $variables['account']);
   }
 
   $attributes = array();
@@ -660,8 +672,7 @@ function rdf_preprocess_comment(&$variab
     // Adds RDFa markup to the comment container. The about attribute specifies
     // the URI of the resource described within the HTML element, while the
     // typeof attribute indicates its RDF type (e.g. sioc:Post, etc.).
-    $uri = entity_uri('comment', $comment);
-    $variables['attributes_array']['about'] = url($uri['path'], $uri['options']);
+    $variables['attributes_array']['about'] = entity_url('comment', $comment);
     $variables['attributes_array']['typeof'] = $comment->rdf_mapping['rdftype'];
   }
 
@@ -709,7 +720,7 @@ function rdf_preprocess_taxonomy_term(&$
   $term_label_meta = array(
       '#tag' => 'meta',
       '#attributes' => array(
-        'about' => url('taxonomy/term/' . $term->tid),
+        'about' => entity_url('taxonomy_term', $term),
         'typeof' => $term->rdf_mapping['rdftype'],
         'property' => $term->rdf_mapping['name']['predicates'],
         'content' => $term->name,
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.168
diff -u -p -r1.168 system.api.php
--- modules/system/system.api.php	23 May 2010 19:10:23 -0000	1.168
+++ modules/system/system.api.php	11 Jun 2010 22:17:45 -0000
@@ -2003,7 +2003,7 @@ function hook_mail($key, &$message, $par
     $node = $params['node'];
     $variables += array(
       '%uid' => $node->uid,
-      '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
+      '%node_url' => entity_url('node', $node, array('absolute' => TRUE)),
       '%node_type' => node_type_get_name($node),
       '%title' => $node->title,
       '%teaser' => $node->teaser,
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.933
diff -u -p -r1.933 system.module
--- modules/system/system.module	20 May 2010 08:47:00 -0000	1.933
+++ modules/system/system.module	11 Jun 2010 22:17:45 -0000
@@ -2672,6 +2672,8 @@ function _system_sort_requirements($a, $
  * @param $path
  *   The page to go to if the user cancels the action. This can be either:
  *   - A string containing a Drupal path.
+ *   - An associative array with 'path' and 'options' keys. The value for the
+ *     'options' key is passed as the $options parameter to l().
  *   - An associative array with a 'path' key. Additional array values are
  *     passed as the $options parameter to l().
  *   If the 'destination' query parameter is set in the URL when viewing a
@@ -2698,7 +2700,7 @@ function confirm_form($form, $question, 
     $options = drupal_parse_url(urldecode($_GET['destination']));
   }
   elseif (is_array($path)) {
-    $options = $path;
+    $options = isset($path['options']) ? (array('path' => $path['path']) + $path['options']) : $path;
   }
   else {
     $options = array('path' => $path);
Index: modules/taxonomy/taxonomy.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v
retrieving revision 1.105
diff -u -p -r1.105 taxonomy.admin.inc
--- modules/taxonomy/taxonomy.admin.inc	13 May 2010 07:53:02 -0000	1.105
+++ modules/taxonomy/taxonomy.admin.inc	11 Jun 2010 22:17:45 -0000
@@ -358,7 +358,13 @@ function taxonomy_overview_terms($form, 
       unset($form[$key]['#term']['parents'], $term->parents);
     }
 
-    $form[$key]['view'] = array('#type' => 'link', '#title' => $term->name, '#href' => "taxonomy/term/$term->tid");
+    $url_arguments = entity_url_arguments('taxonomy_term', $term);
+    $form[$key]['view'] = array(
+      '#type' => 'link',
+      '#title' => $term->name,
+      '#href' => $url_arguments['path'],
+      '#options' => $url_arguments['options'],
+    );
     if ($vocabulary->hierarchy < 2 && count($tree) > 1) {
       $form['#parent_fields'] = TRUE;
       $form[$key]['tid'] = array(
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.594
diff -u -p -r1.594 taxonomy.module
--- modules/taxonomy/taxonomy.module	4 Jun 2010 20:34:44 -0000	1.594
+++ modules/taxonomy/taxonomy.module	11 Jun 2010 22:17:45 -0000
@@ -620,8 +620,7 @@ function template_preprocess_taxonomy_te
   $variables['term'] = $variables['elements']['#term'];
   $term = $variables['term'];
 
-  $uri = entity_uri('taxonomy_term', $term);
-  $variables['term_url']  = url($uri['path'], $uri['options']);
+  $variables['term_url']  = entity_url('taxonomy_term', $term);
   $variables['term_name'] = check_plain($term->name);
   $variables['page']      = taxonomy_term_is_page($term);
 
@@ -1221,12 +1220,12 @@ function taxonomy_field_formatter_view($
     case 'taxonomy_term_reference_link':
       foreach ($items as $delta => $item) {
         $term = $item['taxonomy_term'];
-        $uri = entity_uri('taxonomy_term', $term);
+        $url_arguments = entity_url_arguments('taxonomy_term', $term);
         $element[$delta] = array(
           '#type' => 'link',
           '#title' => $term->name,
-          '#href' => $uri['path'],
-          '#options' => $uri['options'],
+          '#href' => $url_arguments['path'],
+          '#options' => $url_arguments['options'],
         );
       }
       break;
Index: modules/taxonomy/taxonomy.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.pages.inc,v
retrieving revision 1.51
diff -u -p -r1.51 taxonomy.pages.inc
--- modules/taxonomy/taxonomy.pages.inc	10 Feb 2010 06:28:10 -0000	1.51
+++ modules/taxonomy/taxonomy.pages.inc	11 Jun 2010 22:17:45 -0000
@@ -22,7 +22,7 @@ function taxonomy_term_page($term) {
   $breadcrumb = array();
   while ($parents = taxonomy_get_parents($current->tid)) {
     $current = array_shift($parents);
-    $breadcrumb[] = l($current->name, 'taxonomy/term/' . $current->tid);
+    $breadcrumb[] = entity_l($current->name, 'taxonomy_term', $current);
   }
   $breadcrumb[] = l(t('Home'), NULL);
   $breadcrumb = array_reverse($breadcrumb);
@@ -60,7 +60,7 @@ function taxonomy_term_page($term) {
  *   The taxonomy term.
  */
 function taxonomy_term_feed($term) {
-  $channel['link'] = url('taxonomy/term/' . $term->tid, array('absolute' => TRUE));
+  $channel['link'] = entity_url('taxonomy_term', $term, array('absolute' => TRUE));
   $channel['title'] = variable_get('site_name', 'Drupal') . ' - ' . $term->name;
   // Only display the description if we have a single term, to avoid clutter and confusion.
   // HTML will be removed from feed description.
Index: modules/taxonomy/taxonomy.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.tokens.inc,v
retrieving revision 1.6
diff -u -p -r1.6 taxonomy.tokens.inc
--- modules/taxonomy/taxonomy.tokens.inc	29 Jan 2010 22:56:54 -0000	1.6
+++ modules/taxonomy/taxonomy.tokens.inc	11 Jun 2010 22:17:46 -0000
@@ -119,7 +119,7 @@ function taxonomy_tokens($type, $tokens,
           break;
 
         case 'url':
-          $replacements[$original] = url('taxonomy/term/' . $term->tid, array('absolute' => TRUE));
+          $replacements[$original] = entity_url('taxonomy_term', $term, array('absolute' => TRUE));
           break;
 
         case 'node-count':
Index: modules/tracker/tracker.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/tracker/tracker.pages.inc,v
retrieving revision 1.31
diff -u -p -r1.31 tracker.pages.inc
--- modules/tracker/tracker.pages.inc	14 Jan 2010 06:23:40 -0000	1.31
+++ modules/tracker/tracker.pages.inc	11 Jun 2010 22:17:46 -0000
@@ -55,13 +55,13 @@ function tracker_page($account = NULL, $
 
         if ($new = comment_num_new($node->nid)) {
           $comments .= '<br />';
-          $comments .= l(format_plural($new, '1 new', '@count new'), 'node/'. $node->nid, array('fragment' => 'new'));
+          $comments .= entity_l(format_plural($new, '1 new', '@count new'), 'node', $node, array('fragment' => 'new'));
         }
       }
 
       $row = array(
         'type' => check_plain(node_type_get_name($node->type)),
-        'title' => array('data' => l($node->title, 'node/' . $node->nid) . ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed)))),
+        'title' => array('data' => entity_l($node->title, 'node', $node) . ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed)))),
         'author' => array('data' => theme('username', array('account' => $node))),
         'replies' => array('class' => array('replies'), 'data' => $comments),
         'last updated' => array('data' => t('!time ago', array('!time' => format_interval(REQUEST_TIME - $node->last_activity)))),
Index: modules/translation/translation.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/translation/translation.pages.inc,v
retrieving revision 1.15
diff -u -p -r1.15 translation.pages.inc
--- modules/translation/translation.pages.inc	30 Jan 2010 07:59:26 -0000	1.15
+++ modules/translation/translation.pages.inc	11 Jun 2010 22:17:46 -0000
@@ -33,7 +33,7 @@ function translation_node_overview($node
       // Existing translation in the translation set: display status.
       // We load the full node to check whether the user can edit it.
       $translation_node = node_load($translations[$language->language]->nid);
-      $title = l($translation_node->title, 'node/' . $translation_node->nid);
+      $title = entity_l($translation_node->title, 'node', $translation_node);
       if (node_access('update', $translation_node)) {
         $options[] = l(t('edit'), "node/$translation_node->nid/edit");
       }
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1175
diff -u -p -r1.1175 user.module
--- modules/user/user.module	11 Jun 2010 11:12:13 -0000	1.1175
+++ modules/user/user.module	11 Jun 2010 22:17:46 -0000
@@ -892,12 +892,12 @@ function user_search_execute($keys = NUL
     ->execute();
   if (user_access('administer users')) {
     foreach ($result as $account) {
-      $find[] = array('title' => $account->name . ' (' . $account->mail . ')', 'link' => url('user/' . $account->uid, array('absolute' => TRUE)));
+      $find[] = array('title' => $account->name . ' (' . $account->mail . ')', 'link' => entity_url('user', $account, array('absolute' => TRUE)));
     }
   }
   else {
     foreach ($result as $account) {
-      $find[] = array('title' => $account->name, 'link' => url('user/' . $account->uid, array('absolute' => TRUE)));
+      $find[] = array('title' => $account->name, 'link' => entity_url('user', $account, array('absolute' => TRUE)));
     }
   }
   return $find;
@@ -1415,8 +1415,8 @@ function template_preprocess_user_pictur
         $variables['user_picture'] = theme('image', array('path' => $filepath, 'alt' => $alt, 'title' => $alt, 'attributes' => array(), 'getsize' => FALSE));
       }
       if (!empty($account->uid) && user_access('access user profiles')) {
-        $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
-        $variables['user_picture'] = l($variables['user_picture'], "user/$account->uid", $attributes);
+        $options = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
+        $variables['user_picture'] = entity_l($variables['user_picture'], 'user', $account, $options);
       }
     }
   }
@@ -1895,7 +1895,7 @@ function user_login($form, &$form_state)
 
   // If we are already logged on, go to the user page instead.
   if ($user->uid) {
-    drupal_goto('user/' . $user->uid);
+    call_user_func_array('drupal_goto', entity_url_arguments('user', $user));
   }
 
   // Display login form:
@@ -2103,7 +2103,7 @@ function user_login_finalize(&$edit = ar
 function user_login_submit($form, &$form_state) {
   global $user;
   $user = user_load($form_state['uid']);
-  $form_state['redirect'] = 'user/' . $user->uid;
+  $form_state['redirect'] = entity_url_arguments('user', $user);
 
   user_login_finalize($form_state);
 }
@@ -3425,7 +3425,7 @@ function user_register_form($form, &$for
 
   // If we aren't admin but already logged on, go to the user page instead.
   if (!$admin && $user->uid) {
-    drupal_goto('user/' . $user->uid);
+    call_user_func_array('drupal_goto', entity_url_arguments('user', $user));
   }
 
   $form['#user'] = drupal_anonymous_user();
@@ -3512,9 +3512,8 @@ function user_register_submit($form, &$f
   $account->password = $pass;
 
   // New administrative account without notification.
-  $uri = entity_uri('user', $account);
   if ($admin && !$notify) {
-    drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+    drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => entity_url('user', $account), '%name' => $account->name)));
   }
   // No e-mail verification required; log in user immediately.
   elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
@@ -3529,7 +3528,7 @@ function user_register_submit($form, &$f
     $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
     _user_mail_notify($op, $account);
     if ($notify) {
-      drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+      drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => entity_url('user', $account), '%name' => $account->name)));
     }
     else {
       drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
Index: modules/user/user.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v
retrieving revision 1.72
diff -u -p -r1.72 user.pages.inc
--- modules/user/user.pages.inc	6 May 2010 05:59:31 -0000	1.72
+++ modules/user/user.pages.inc	11 Jun 2010 22:17:46 -0000
@@ -393,7 +393,7 @@ function user_cancel_confirm_form($form,
   $form['uid'] = array('#type' => 'value', '#value' => $account->uid);
   return confirm_form($form,
     $question,
-    'user/' . $account->uid,
+    entity_url_arguments('user', $account),
     $description . ' ' . t('This action cannot be undone.'),
     t('Cancel account'), t('Cancel'));
 }
@@ -428,7 +428,7 @@ function user_cancel_confirm_form_submit
     drupal_set_message(t('A confirmation request to cancel your account has been sent to your e-mail address.'));
     watchdog('user', 'Sent account cancellation request to %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
 
-    $form_state['redirect'] = "user/$account->uid";
+    $form_state['redirect'] = entity_url_arguments('user', $account);
   }
 }
 
Index: modules/user/user.tokens.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.tokens.inc,v
retrieving revision 1.6
diff -u -p -r1.6 user.tokens.inc
--- modules/user/user.tokens.inc	20 Apr 2010 09:48:06 -0000	1.6
+++ modules/user/user.tokens.inc	11 Jun 2010 22:17:46 -0000
@@ -94,7 +94,7 @@ function user_tokens($type, $tokens, arr
           break;
 
         case 'url':
-          $replacements[$original] = url("user/$account->uid", $url_options);
+          $replacements[$original] = entity_url('user', $account, $url_options);
           break;
 
         case 'edit-url':
