Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.396
diff -u -p -r1.396 bootstrap.inc
--- includes/bootstrap.inc	10 Jun 2010 06:51:38 -0000	1.396
+++ includes/bootstrap.inc	10 Jun 2010 17:57:42 -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	10 Jun 2010 17:57:43 -0000
@@ -152,6 +152,30 @@ define('DRUPAL_CACHE_PER_PAGE', 0x0004);
 define('DRUPAL_CACHE_GLOBAL', 0x0008);
 
 /**
+ * Indicates a URI represented as an array with 'path' and 'options' keys,
+ * matching the signature of url().
+ */
+define('URI_FORMAT_NESTED_ARRAY', 1);
+
+/**
+ * Indicates a URI represented as a flat array of options keys along with a
+ * 'path' key for the Drupal path.
+ */
+define('URI_FORMAT_FLAT_ARRAY', 2);
+
+/**
+ * Indicates a URI represented as a flat array of options keys along with a
+ * 'href' key for the Drupal path, as used by theme_links().
+ */
+define('URI_FORMAT_FLAT_ARRAY_HREF', 3);
+
+/**
+ * Indicates a URI represented as a string matching RFC 2396
+ * (http://www.ietf.org/rfc/rfc2396.txt).
+ */
+define('URI_FORMAT_STRING', 4);
+
+/**
  * Add content to a specified region.
  *
  * @param $region
@@ -6447,7 +6471,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,23 +6596,114 @@ function entity_prepare_view($entity_typ
 }
 
 /**
- * Returns the uri elements of an entity.
+ * Returns the URI elements or the assembled URI of an entity.
+ *
+ * The terms URI (Uniform Resource Identifier) and URL (Uniform Resource
+ * Locator) are often used interchangeably. They are similar, but not strictly
+ * the same (http://www.w3.org/TR/uri-clarification/). Because URI is a more
+ * general term than URL, web specifications increasingly refer to URIs and
+ * leave the term URL as an informal concept only. All Drupal entity URIs are
+ * also URLs.
+ *
+ * All Drupal code that outputs links to an entity or otherwise needs the URI
+ * of an entity should call this function, either directly, or indirectly as
+ * part of entity_link(). This function is a replacement for any code that
+ * would otherwise hard-code a Drupal path. Calling this function enables
+ * modules to control an entity's URI, 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
+ * would run for every call to url(), need to parse the path to determine if it
+ * is for an entity that needs a custom URI, and then reload the entity if it
+ * is. An example of a module that needs to control the URI 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.
+ *
+ * To redirect the user to a node page from a form submission handler:
+ * @code
+ *   // Incorrect
+ *   $form_state['redirect'] = 'node/' . $node->nid;
+ *
+ *   // Correct
+ *   $form_state['redirect'] = entity_uri('node', $node);
+ * @endcode
+ *
+ * To expose the absolute URI of the node as a template variable:
+ * @code
+ *   // Incorrect
+ *   $variables['node_uri_absolute'] = url('node/' . $node->nid, array('absolute' => TRUE));
+ *
+ *   // Correct
+ *   $variables['node_uri_absolute'] = entity_uri('node', $node, array('absolute' => TRUE), URI_FORMAT_STRING);
+ * @endcode
+ *
+ * To add a "Read more" link to the node's teaser content:
+ * @code
+ *   if ($view_mode == 'teaser') {
+ *     // Incorrect
+ *     $node->content['links']['node']['#links']['node-readmore'] = array(
+ *       'title' => t('Read more'),
+ *       'href' => 'node/' . $node->nid,
+ *     );
+ *
+ *     // Correct
+ *     $node->content['links']['node']['#links']['node-readmore'] = array(
+ *       'title' => t('Read more'),
+ *     ) + entity_uri('node', $node, NULL, URI_FORMAT_FLAT_ARRAY_HREF);
+ *   }
+ * @endcode
+ *
+ * To output a link to the node as part of a theme function:
+ * @code
+ *   // Incorrect
+ *   $output .= l($node->title, 'node/' . $node->nid);
+ *
+ *   // Correct (entity_link() calls entity_uri())
+ *   $output .= entity_link($node->title, 'node', $node);
+ * @endcode
  *
  * @param $entity_type
  *   The entity type; e.g. 'node' or 'user'.
  * @param $entity
- *   The entity for which to generate a path.
+ *   The entity for which to generate a URI.
+ * @param $extra_options
+ *   A keyed array of additional options to affect the URI. These are merged
+ *   with the options that are intrinsically part of the entity URI, and the
+ *   merged options are eventually passed to the url() function's $options
+ *   parameter when the URI needs to be assembled. For example, if a URI for
+ *   a language-specific representation of the entity is needed, a value for
+ *   the 'language' key can be added to this parameter.
+ * @param $format
+ *   A constant indicating how to return the URI information. One of:
+ *   - URI_FORMAT_NESTED_ARRAY: (default) An array containing the 'path' and
+ *     'options' keys is returned. The URI can be assembled by passing these two
+ *     values individually to url().
+ *   - URI_FORMAT_FLAT_ARRAY: A flat array containing the 'path' key and a key
+ *     for each option is returned. This array can be passed as the $path
+ *     parameter to confirm_form().
+ *   - URI_FORMAT_FLAT_ARRAY_HREF: A flat array containing the Drupal path as
+ *     the value of the 'href' key and a key for each option is returned. This
+ *     array can be merged with an array containing a 'title' key to form a link
+ *     item within the $links variable used by theme_links().
+ *   - URI_FORMAT_STRING: A string is returned. This string follows the RFC 2396
+ *     standard (http://www.ietf.org/rfc/rfc2396.txt) defining URI syntax. By
+ *     default, this string will be a relative URI. To retrieve an absolute URI,
+ *     include 'absolute' => TRUE as part of $extra_options. 
+ *
  * @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.
+ *   An array or string, depending on the value for the $format parameter.
+ *
+ * @see url()
+ * @see entity_link()
  */
-function entity_uri($entity_type, $entity) {
+function entity_uri($entity_type, $entity, $extra_options = NULL, $format = URI_FORMAT_NESTED_ARRAY) {
   // This check enables the URI 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)) {
+  // 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->uri_elements[$entity_type])) {
     $info = entity_get_info($entity_type);
     list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
 
@@ -6605,19 +6720,70 @@ function entity_uri($entity_type, $entit
     }
 
     // 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
+    // 'uri_elements' 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();
-      }
+      $entity->uri_elements[$entity_type] = $uri_callback($entity) + array('options' => array());
     }
     else {
-      $entity->uri = FALSE;
+      $entity->uri_elements[$entity_type] = FALSE;
+    }
+  }
+
+  // If the entity has a URI, merge the extra options and return the URI
+  // information in the requested format. Otherwise, return NULL.
+  if ($uri_elements = $entity->uri_elements[$entity_type]) {
+    if (isset($extra_options)) {
+      $uri_elements['options'] = drupal_array_merge_deep($uri_elements['options'], $extra_options);
+    }
+    switch ($format) {
+      case URI_FORMAT_NESTED_ARRAY:
+        // 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' => $uri_elements['path'], 'options' => $uri_elements['options']);
+
+      case URI_FORMAT_FLAT_ARRAY:
+        return array('path' => $uri_elements['path']) + $uri_elements['options'];
+
+      case URI_FORMAT_FLAT_ARRAY_HREF:
+        return array('href' => $uri_elements['path']) + $uri_elements['options'];
+
+      case URI_FORMAT_STRING:
+        // All Drupal entity URIs are also URLs, so it is correct to call url()
+        // to assemble the URI.
+        // @todo For Drupal 8, evaluate whether the term URL should be fully
+        //   deprecated, and if so, rename the url() function to uri().
+        return url($uri_elements['path'], $uri_elements['options']);
     }
   }
-  return $entity->uri ? $entity->uri : NULL;
+}
+
+/**
+ * Formats an entity link as an HTML anchor tag.
+ *
+ * @param $text
+ *   The link text for the anchor tag.
+ * @param $entity_type
+ *   The entity type; e.g. 'node' or 'user'.
+ * @param $entity
+ *   The entity for which to generate a link.
+ * @param $options
+ *   Options to pass to l() in addition to the ones returned by entity_uri().
+ *
+ * @return
+ *   An HTML anchor tag as returned by l().
+ *
+ * @see entity_uri()
+ * @see l()
+ */
+function entity_link($text, $entity_type, $entity, $options = NULL) {
+  // While there is no functional difference between passing $options as a
+  // 3rd parameter to entity_uri() vs. merging within this function, we choose
+  // the latter, because $options may contain keys like 'attributes' that affect
+  // the link, but not the URI, so passing them to entity_uri() is inconsistent
+  // with the semantic intent of that function.
+  $uri_elements = entity_uri($entity_type, $entity);
+  return l($text, $uri_elements['path'], isset($options) ? drupal_array_merge_deep($uri_elements['options'], $options) : $uri_elements['options']);
 }
 
 /**
@@ -6743,3 +6909,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	10 Jun 2010 17:57:44 -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_uri('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	10 Jun 2010 17:57:45 -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_link(t('view'), 'node', $node));
       }
     }
   }
Index: modules/book/book.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/book/book.pages.inc,v
retrieving revision 1.25
diff -u -p -r1.25 book.pages.inc
--- modules/book/book.pages.inc	13 Mar 2010 06:55:50 -0000	1.25
+++ modules/book/book.pages.inc	10 Jun 2010 17:57:45 -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_uri('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_uri('node', $node, NULL, URI_FORMAT_FLAT_ARRAY), $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_uri('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	10 Jun 2010 17:57:45 -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_uri_elements = entity_uri('comment', $comment);
+    $node_uri_elements = entity_uri('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_uri_elements['path'],
+          '#options' => $comment_uri_elements['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_uri_elements['path'],
+          '#options' => $node_uri_elements['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_uri('node', node_load($comment->nid), NULL, URI_FORMAT_FLAT_ARRAY),
     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_uri('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	10 Jun 2010 17:57:45 -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_link($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_uri('node', $node, array('fragment' => 'comments', 'absolute' => TRUE), URI_FORMAT_STRING),
         );
       }
     }
@@ -615,22 +615,17 @@ function comment_node_view($node, $view_
         if (!empty($node->comment_count)) {
           $links['comment-comments'] = array(
             'title' => format_plural($node->comment_count, '1 comment', '@count comments'),
-            'href' => "node/$node->nid",
             'attributes' => array('title' => t('Jump to the first comment of this posting.')),
-            'fragment' => 'comments',
             'html' => TRUE,
-          );
+          ) + entity_uri('node', $node, array('fragment' => 'comments'), URI_FORMAT_FLAT_ARRAY_HREF);
 
           $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",
-              '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,
-            );
+            ) + entity_uri('node', $node, array('query' => comment_new_page_count($node->comment_count, $new, $node), 'fragment' => 'new'), URI_FORMAT_FLAT_ARRAY_HREF);
           }
         }
         else {
@@ -668,7 +663,7 @@ function comment_node_view($node, $view_
             $links['comment-add']['href'] = "comment/reply/$node->nid";
           }
           else {
-            $links['comment-add']['href'] = "node/$node->nid";
+            $links['comment-add'] += entity_uri('node', $node, NULL, URI_FORMAT_FLAT_ARRAY_HREF);
           }
         }
         else {
@@ -2147,7 +2142,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_link(t('view'), 'comment', $comment));
 
     // Explain the approval queue if necessary.
     if ($comment->status == COMMENT_NOT_PUBLISHED) {
@@ -2165,13 +2160,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_uri('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_uri('node', $node);
   }
   unset($form_state['rebuild']);
   $form_state['redirect'] = $redirect;
@@ -2198,9 +2193,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_link($comment->subject, 'comment', $comment);
+  $variables['permalink'] = entity_link('#', 'comment', $comment);
 
   // Preprocess fields.
   field_attach_preprocess('comment', $comment, $variables['elements'], $variables);
@@ -2271,7 +2265,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");
+        $uri_elements = entity_uri('node', $node);
+        $destination = array('destination' => $uri_elements['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	10 Jun 2010 17:57:45 -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_link($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_uri('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_uri('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_uri('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_uri('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_uri('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_uri('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	10 Jun 2010 17:57:45 -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_uri('comment', $comment, $url_options, URI_FORMAT_STRING);
           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	10 Jun 2010 17:57:45 -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_uri('user', $params['sender'], array('absolute' => TRUE, 'language' => $language), URI_FORMAT_STRING) : $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	10 Jun 2010 17:57:45 -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_uri('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	10 Jun 2010 17:57:46 -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_link($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_uri('node', $topic, array('query' => comment_new_page_count($topic->comment_count, $topic->new_replies, $topic), 'fragment' => 'new'), URI_FORMAT_STRING);
       }
 
     }
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	10 Jun 2010 17:57:46 -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);
+    $uri_elements = entity_uri($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(
+      $uri_elements = 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($uri_elements) ? $uri_elements : '',
     );
   }
 
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	10 Jun 2010 17:57:47 -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_link($node->title, 'node', $node);
 
     // Update our progress information.
     $context['sandbox']['progress']++;
@@ -464,13 +464,14 @@ function node_admin_nodes() {
   $options = array();
   foreach ($nodes as $node) {
     $l_options = $node->language != LANGUAGE_NONE ? array('language' => $languages[$node->language]) : array();
+    $uri_elements = entity_uri('node', $node, $l_options);
     $options[$node->nid] = array(
       'title' => array(
         'data' => array(
           '#type' => 'link',
           '#title' => $node->title,
-          '#href' => 'node/' . $node->nid,
-          '#options' => $l_options,
+          '#href' => $uri_elements['path'],
+          '#options' => $uri_elements['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	10 Jun 2010 17:57:47 -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_link($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;
   }
 
@@ -1277,9 +1277,8 @@ function node_build_content($node, $view
   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))
-    );
+      'attributes' => array('rel' => 'tag', 'title' => strip_tags($node->title)),
+    ) + entity_uri('node', $node, NULL, URI_FORMAT_FLAT_ARRAY_HREF);
   }
   $node->content['links']['node'] = array(
     '#theme' => 'links__node',
@@ -1348,8 +1347,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_uri('node', $node, NULL, URI_FORMAT_STRING);
   $variables['title']     = check_plain($node->title);
   $variables['page']      = node_is_page($node);
 
@@ -1575,9 +1573,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_uri('node', $node, array('absolute' => TRUE), URI_FORMAT_STRING),
       'type' => check_plain(node_type_get_name($node)),
       'title' => $node->title,
       'user' => theme('username', array('account' => $node)),
@@ -2179,7 +2176,7 @@ function theme_node_recent_content($vari
   $node = $variables['node'];
 
   $output = '<div class="node-title">';
-  $output .= l($node->title, 'node/' . $node->nid);
+  $output .= entity_link($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 +2375,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_uri('node', $node, array('absolute' => TRUE), URI_FORMAT_STRING);
     $node->rss_namespaces = array();
     $node->rss_elements = array(
       array('key' => 'pubDate', 'value' => gmdate('r', $node->created)),
@@ -2503,11 +2500,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_uri('node', $node, NULL, URI_FORMAT_STRING)), 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_uri('node', $node, array('alias' => TRUE), URI_FORMAT_STRING)), 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	10 Jun 2010 17:57:47 -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_link(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_uri('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_uri('node', $node, NULL, URI_FORMAT_FLAT_ARRAY),
     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_link(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	10 Jun 2010 17:57:47 -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_uri('node', $node, $url_options, URI_FORMAT_STRING);
           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	10 Jun 2010 17:57:47 -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_link($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	10 Jun 2010 17:57:47 -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_link(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	10 Jun 2010 17:57:48 -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_uri('node', entity_create_stub_entity('node', array($comment->nid, NULL, $comment->node_type)), NULL, URI_FORMAT_STRING);
     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_uri('comment', entity_create_stub_entity('comment', array($comment->pid, NULL, $comment->node_type)), NULL, URI_FORMAT_STRING);
     }
   }
 }
@@ -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_uri('user', $account, NULL, URI_FORMAT_STRING);
   }
   // 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_uri('user', $account, array('fragment' => 'me'), URI_FORMAT_STRING),
       'typeof' => array('foaf:Person'),
       'rel' => array('foaf:account'),
-      'resource' => url($uri['path'], $uri['options']),
+      'resource' => entity_uri('user', $account, NULL, URI_FORMAT_STRING),
     ),
   );
   // Adds the markup for username.
   $username_meta = array(
     '#tag' => 'meta',
     '#attributes' => array(
-      'about' => url($uri['path'], $uri['options']),
+      'about' => entity_uri('user', $account, NULL, URI_FORMAT_STRING),
       '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_uri('user', $variables['account'], NULL, URI_FORMAT_STRING);
   }
 
   $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_uri('comment', $comment, NULL, URI_FORMAT_STRING);
     $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_uri('taxonomy_term', $term, NULL, URI_FORMAT_STRING),
         '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	10 Jun 2010 17:57:49 -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_uri('node', $node, array('absolute' => TRUE), URI_FORMAT_STRING),
       '%node_type' => node_type_get_name($node),
       '%title' => $node->title,
       '%teaser' => $node->teaser,
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	10 Jun 2010 17:57:50 -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");
+    $uri_elements = entity_uri('taxonomy_term', $term);
+    $form[$key]['view'] = array(
+      '#type' => 'link',
+      '#title' => $term->name,
+      '#href' => $uri_elements['path'],
+      '#options' => $uri_elements['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	10 Jun 2010 17:57:50 -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_uri('taxonomy_term', $term, NULL, URI_FORMAT_STRING);
   $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);
+        $uri_elements = entity_uri('taxonomy_term', $term);
         $element[$delta] = array(
           '#type' => 'link',
           '#title' => $term->name,
-          '#href' => $uri['path'],
-          '#options' => $uri['options'],
+          '#href' => $uri_elements['path'],
+          '#options' => $uri_elements['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	10 Jun 2010 17:57:50 -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_link($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_uri('taxonomy_term', $term, array('absolute' => TRUE), URI_FORMAT_STRING);
   $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	10 Jun 2010 17:57:50 -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_uri('taxonomy_term', $term, array('absolute' => TRUE), URI_FORMAT_STRING);
           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	10 Jun 2010 17:57:50 -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_link(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_link($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	10 Jun 2010 17:57:50 -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_link($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.1174
diff -u -p -r1.1174 user.module
--- modules/user/user.module	10 Jun 2010 06:57:20 -0000	1.1174
+++ modules/user/user.module	10 Jun 2010 17:57:51 -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_uri('user', $account, array('absolute' => TRUE), URI_FORMAT_STRING));
     }
   }
   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_uri('user', $account, array('absolute' => TRUE), URI_FORMAT_STRING));
     }
   }
   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_link($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_uri('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_uri('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_uri('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_uri('user', $account, NULL, URI_FORMAT_STRING), '%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_uri('user', $account, NULL, URI_FORMAT_STRING), '%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	10 Jun 2010 17:57:51 -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_uri('user', $account, NULL, URI_FORMAT_FLAT_ARRAY),
     $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_uri('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	10 Jun 2010 17:57:51 -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_uri('user', $account, $url_options, URI_FORMAT_STRING);
           break;
 
         case 'edit-url':
