Index: handlers/views_handler_field.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/handlers/views_handler_field.inc,v
retrieving revision 1.24
diff -u -p -r1.24 views_handler_field.inc
--- handlers/views_handler_field.inc	30 Jun 2009 19:09:54 -0000	1.24
+++ handlers/views_handler_field.inc	1 Jul 2009 20:39:52 -0000
@@ -285,6 +285,8 @@ class views_handler_field extends views_
         $options[t('Arguments')]['%' . ++$count] = $handler->ui_name();
       }
 
+      $this->document_self_tokens($options[t('Fields')]);
+
       // Default text.
       $output = t('<p>You must add some additional fields to this display before using this field. These fields may be marked as <em>Exclude from display</em> if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.</p>');
       // We have some options, so make a list.
@@ -415,25 +417,30 @@ class views_handler_field extends views_
    * text-replacement rendering is necessary.
    */
   function advanced_render($values) {
-    $this->last_render = $value = $this->render($values);
-    $this->original_value = $value;
+    if ($this->allow_advanced_render() && method_exists($this, 'render_item')) {
+      $raw_items = $this->get_items($values);
+    }
+    else {
+      $this->last_render = $value = $this->render($values);
+      $this->original_value = $value;
+    }
 
     if ($this->allow_advanced_render()) {
       $tokens = NULL;
-      if (!empty($this->options['alter']['alter_text']) && !empty($this->options['alter']['text'])) {
-        $tokens = $this->get_render_tokens();
-        $value = $this->render_altered($tokens);
-      }
-
-      if (!empty($this->options['alter']['trim']) && !empty($this->options['alter']['max_length'])) {
-        $value = $this->render_trim_text($value);
-      }
+      if (method_exists($this, 'render_item')) {
+        $items = array();
+        foreach ($raw_items as $count => $item) {
+          $this->last_render = $this->render_item($count, $item);
+          $this->original_value = $this->last_render;
 
-      if (!empty($this->options['alter']['make_link']) && !empty($this->options['alter']['path'])) {
-        if (!isset($tokens)) {
-         $tokens = $this->get_render_tokens();
+          $alter = $item + $this->options['alter'];
+          $items[] = $this->render_text($alter);       
         }
-        $value = $this->render_as_link($value, $tokens);
+
+        $value = $this->render_items($items);
+      }
+      else {
+        $value = $this->render_text($this->options['alter']);
       }
 
       // This happens here so that render_as_link can get the unaltered value of
@@ -445,11 +452,38 @@ class views_handler_field extends views_
   }
 
   /**
+   * Perform an advanced text render for the item.
+   *
+   * This is separated out as some fields may render lists, and this allows
+   * each item to be handled individually.
+   */
+  function render_text($alter) {
+    $value = $this->last_render;
+    if (!empty($alter['alter_text']) && !empty($alter['text'])) {
+      $tokens = $this->get_render_tokens($alter);
+      $value = $this->render_altered($alter, $tokens);
+    }
+
+    if (!empty($alter['trim']) && !empty($alter['max_length'])) {
+      $value = $this->render_trim_text($alter, $value);
+    }
+
+    if (!empty($alter['make_link']) && !empty($alter['path'])) {
+      if (!isset($tokens)) {
+       $tokens = $this->get_render_tokens($alter);
+      }
+      $value = $this->render_as_link($alter, $value, $tokens);
+    }
+
+    return $value;
+  }
+  
+  /**
    * Render this field as altered text, from a fieldset set by the user.
    */
-  function render_altered($tokens) {
+  function render_altered($alter, $tokens) {
     // Filter this right away as our substitutions are already sanitized.
-    $value = filter_xss_admin($this->options['alter']['text']);
+    $value = filter_xss_admin($alter['text']);
     $value = strtr($value, $tokens);
 
     return $value;
@@ -458,8 +492,8 @@ class views_handler_field extends views_
   /**
    * Trim the field down to the specified length.
    */
-  function render_trim_text($value) {
-    if (!empty($this->options['alter']['strip_tags'])) {
+  function render_trim_text($alter, $value) {
+    if (!empty($alter['strip_tags'])) {
       $value = strip_tags($value);
       // NOTE: It's possible that some external fields might override the
       // element type so if someone from, say, CCK runs into a bug here,
@@ -467,13 +501,13 @@ class views_handler_field extends views_
       $this->definition['element type'] = 'span';
     }
 
-    if (drupal_strlen($value) <= $this->options['alter']['max_length']) {
+    if (drupal_strlen($value) <= $alter['max_length']) {
       return $value;
     }
 
-    $value = drupal_substr($value, 0, $this->options['alter']['max_length']);
+    $value = drupal_substr($value, 0, $alter['max_length']);
 
-    if (!empty($this->options['alter']['word_boundary'])) {
+    if (!empty($alter['word_boundary'])) {
       if (preg_match("/(.*)\b.+/us", $value, $matches)) {
         $value = $matches[1];
       }
@@ -481,11 +515,11 @@ class views_handler_field extends views_
     // Remove scraps of HTML entities from the end of a strings
     $value = rtrim(preg_replace('/(?:<(?!.+>)|&(?!.+;)).*$/us', '', $value));
 
-    if (!empty($this->options['alter']['ellipsis'])) {
+    if (!empty($alter['ellipsis'])) {
       $value .= '...';
     }
 
-    if (!empty($this->options['alter']['html'])) {
+    if (!empty($alter['html'])) {
       $value = _filter_htmlcorrector($value);
     }
 
@@ -496,11 +530,11 @@ class views_handler_field extends views_
    * Render this field as a link, with the info from a fieldset set by
    * the user.
    */
-  function render_as_link($text, $tokens) {
+  function render_as_link($alter, $text, $tokens) {
     $value = '';
 
-    if (!empty($this->options['alter']['prefix'])) {
-      $value .= filter_xss_admin($this->options['alter']['prefix']);
+    if (!empty($alter['prefix'])) {
+      $value .= filter_xss_admin(strtr($alter['prefix'], $tokens));
     }
 
     $options = array(
@@ -509,7 +543,7 @@ class views_handler_field extends views_
 
     // $path will be run through check_url() by l() so we do not need to
     // sanitize it ourselves.
-    $path = $this->options['alter']['path'];
+    $path = $alter['path'];
 
     // Use strip tags as there should never be HTML in the path.
     $path = strip_tags(strtr($path, $tokens));
@@ -525,35 +559,34 @@ class views_handler_field extends views_
       $options['fragment'] = $url['fragment'];
     }
 
-    $alt = $this->options['alter']['alt'];
-    $alt = strtr($alt, $tokens);
+    $alt = strtr($alter['alt'], $tokens);
     if ($alt) {
       $options['attributes']['title'] = $alt;
     }
 
-    $class = $this->options['alter']['link_class'];
+    $class = strtr($alter['link_class'], $tokens);
     if ($class) {
       $options['attributes']['class'] = $class;
     }
 
-    $target = check_plain(trim($this->options['alter']['target']));
+    $target = check_plain(trim($alter['target']));
     if (!empty($target)) {
       $options['attributes']['target'] = $target;
     }
 
     // If the query and fragment were programatically assigned overwrite any
     // parsed values.
-    if (isset($this->options['alter']['query'])) {
-      $options['query'] = $this->options['alter']['query'];
+    if (isset($alter['query'])) {
+      $options['query'] = strtr($alter['query'], $tokens);
     }
-    if (isset($this->options['alter']['fragment'])) {
-      $options['fragment'] = $this->options['alter']['fragment'];
+    if (isset($alter['fragment'])) {
+      $options['fragment'] = strtr($alter['fragment'], $tokens);
     }
 
     $value .= l($text, $path, $options);
 
-    if (!empty($this->options['alter']['suffix'])) {
-      $value .= filter_xss_admin($this->options['alter']['suffix']);
+    if (!empty($alter['suffix'])) {
+      $value .= filter_xss_admin(strtr($alter['suffix'], $tokens));
     }
 
     return $value;
@@ -566,7 +599,7 @@ class views_handler_field extends views_
    * are available and gets their values. This will then be
    * used in one giant str_replace().
    */
-  function get_render_tokens() {
+  function get_render_tokens($item) {
     $tokens = array();
     if (!empty($this->view->build_info['substitutions'])) {
       $tokens = $this->view->build_info['substitutions'];
@@ -591,12 +624,34 @@ class views_handler_field extends views_
       if ($field == $this->options['id']) {
         break;
       }
+
+      $this->add_self_tokens($tokens, $item);
     }
 
     return $tokens;
   }
 
   /**
+   * Add any special tokens this field might use for itself.
+   *
+   * This method is intended to be overridden by items that generate
+   * fields as a list. For example, the field that displays all terms
+   * on a node might have tokens for the tid and the term.
+   *
+   * By convention, tokens should follow the format of [token-subtoken]
+   * where token is the field ID and subtoken is the field. If the
+   * field ID is terms, then the tokens might be [terms-tid] and [terms-name].
+   */
+  function add_self_tokens(&$tokens, $item) { }
+
+  /**
+   * Document any special tokens this field might use for itself.
+   *
+   * @see add_self_tokens() for details.
+   */
+  function document_self_tokens(&$tokens) { }
+
+  /**
    * Call out to the theme() function, which probably just calls render() but
    * allows sites to override output fairly easily.
    */
Index: handlers/views_handler_field_prerender_list.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/handlers/views_handler_field_prerender_list.inc,v
retrieving revision 1.1
diff -u -p -r1.1 views_handler_field_prerender_list.inc
--- handlers/views_handler_field_prerender_list.inc	3 Sep 2008 19:21:28 -0000	1.1
+++ handlers/views_handler_field_prerender_list.inc	1 Jul 2009 20:39:52 -0000
@@ -51,6 +51,13 @@ class views_handler_field_prerender_list
     );
   }
 
+  /**
+   * Render the field.
+   *
+   * This function is deprecated, but left in for older systems that have not
+   * yet or won't update their prerender list fields. If a render_item method
+   * exists, this will not get used by advanced_render.
+   */
   function render($values) {
     $field = $values->{$this->field_alias};
     if (!empty($this->items[$field])) {
@@ -65,4 +72,55 @@ class views_handler_field_prerender_list
       return $this->options['empty'];
     }
   }
+
+  /**
+   * Render all items in this field together.
+   *
+   * When using advanced render, each possible item in the list is rendered
+   * individually. Then the items are all pasted together.
+   */
+  function render_items($items) {
+    if (!empty($items)) {
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    }
+    else if (!empty($this->options['empty'])) {
+      return $this->options['empty'];
+    }
+  }
+
+  /**
+   * Return an array of items for the field.
+   *
+   * Items should be stored in the result array, if possible, as an array 
+   * with 'value' as the actual displayable value of the item, plus
+   * any items that might be found in the 'alter' options array for
+   * creating links, such as 'path', 'fragment', 'query' etc, such a thing
+   * is to be made. Additionally, items that might be turned into tokens
+   * should also be in this array.
+   */
+  function get_items($values) {
+    $field = $values->{$this->field_alias};
+    if (!empty($this->items[$field])) {
+      return $this->items[$field];
+    }
+    
+    return array();
+  }
+
+  /**
+   * Determine if advanced rendering is allowed.
+   *
+   * By default, advanced rendering will NOT be allowed if the class
+   * inheriting from this does not implement a 'render_items' method.
+   */
+  function allow_advanced_render() {
+    // Note that the advanced render bits also use the presence of
+    // this method to determine if it needs to render items as a list.
+    return method_exists($this, 'render_item');
+  }
 }
Index: modules/upload.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/upload.views.inc,v
retrieving revision 1.15
diff -u -p -r1.15 upload.views.inc
--- modules/upload.views.inc	24 Nov 2008 19:58:31 -0000	1.15
+++ modules/upload.views.inc	1 Jul 2009 20:39:54 -0000
@@ -150,7 +150,7 @@ function upload_views_handlers() {
         'parent' => 'views_handler_field_prerender_list',
       ),
       'views_handler_field_upload_description' => array(
-        'parent' => 'views_handler_field_prerender_list',
+        'parent' => 'views_handler_field',
       ),
       'views_handler_filter_upload_fid' => array(
         'parent' => 'views_handler_filter_boolean_operator',
Index: modules/profile/views_handler_field_profile_list.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/profile/views_handler_field_profile_list.inc,v
retrieving revision 1.2
diff -u -p -r1.2 views_handler_field_profile_list.inc
--- modules/profile/views_handler_field_profile_list.inc	1 Jul 2009 16:21:44 -0000	1.2
+++ modules/profile/views_handler_field_profile_list.inc	1 Jul 2009 20:39:54 -0000
@@ -7,14 +7,25 @@ class views_handler_field_profile_list e
   /**
    * Break up our field into a proper list.
    */
-  function render($value) {
+  function pre_render($values) {
     $field = $value->{$this->field_alias};
     $this->items[$field] = array();
     foreach (split("[\n\r]", $field) as $item) {
       if ($item != '' && $item !== NULL) {
-        $this->items[$field][] = $item;
+        $this->items[$field]['item']] = $item;
       }
     }
-    return parent::render($value);
+  }
+
+  function render_item($count, $item) {
+    return $item['item'];
+  }
+
+  function document_self_tokens(&$tokens) {
+    $tokens['[' . $this->options['id'] . '-item' . ']'] = t('The text of the profile item.');
+  }
+
+  function add_self_tokens(&$tokens, $item) {
+    $tokens['[' . $this->options['id'] . '-item' . ']'] = $item['item'];
   }
 }
Index: modules/taxonomy/views_handler_field_term_node_tid.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/taxonomy/views_handler_field_term_node_tid.inc,v
retrieving revision 1.3
diff -u -p -r1.3 views_handler_field_term_node_tid.inc
--- modules/taxonomy/views_handler_field_term_node_tid.inc	7 Apr 2009 22:02:40 -0000	1.3
+++ modules/taxonomy/views_handler_field_term_node_tid.inc	1 Jul 2009 20:39:55 -0000
@@ -82,17 +82,38 @@ class views_handler_field_term_node_tid 
         $voc = " AND td.vid IN (" . implode(', ', array_keys(array_filter($this->options['vids']))) . ")";
       }
 
-      $result = db_query("SELECT tn.vid AS node_vid, td.* FROM {term_data} td INNER JOIN {term_node} tn ON td.tid = tn.tid WHERE tn.vid IN (" . implode(', ', $vids) . ")$voc ORDER BY td.weight, td.name");
+      $result = db_query("SELECT tn.vid AS node_vid, td.*, v.name as vocabulary FROM {term_data} td INNER JOIN {term_node} tn ON td.tid = tn.tid INNER JOIN {vocabulary} v ON v.vid = td.vid WHERE tn.vid IN (" . implode(', ', $vids) . ")$voc ORDER BY td.weight, td.name");
 
       while ($term = db_fetch_object($result)) {
-        if (empty($this->options['link_to_taxonomy'])) {
-          $this->items[$term->node_vid][$term->tid] = check_plain($term->name);
-        }
-        else {
-          $this->items[$term->node_vid][$term->tid] = l($term->name, taxonomy_term_path($term));
+        $this->items[$term->node_vid][$term->tid]['name'] = check_plain($term->name);
+        $this->items[$term->node_vid][$term->tid]['tid'] = $term->tid;
+        $this->items[$term->node_vid][$term->tid]['vid'] = $term->vid;
+        $this->items[$term->node_vid][$term->tid]['vocabulary'] = check_plain($term->vocabulary);
+        
+        if (!empty($this->options['link_to_taxonomy'])) {
+          $this->items[$term->node_vid][$term->tid]['make_link'] = TRUE;
+          $this->items[$term->node_vid][$term->tid]['path'] = taxonomy_term_path($term);
         }
       }
     }
   }
+
+  function render_item($count, $item) {
+    return $item['name'];
+  }
+
+  function document_self_tokens(&$tokens) {
+    $tokens['[' . $this->options['id'] . '-tid' . ']'] = t('The taxonomy term ID for the term.');
+    $tokens['[' . $this->options['id'] . '-name' . ']'] = t('The taxonomy term name for the term.');
+    $tokens['[' . $this->options['id'] . '-vid' . ']'] = t('The vocabulary ID for the vocabulary the term belongs to.');
+    $tokens['[' . $this->options['id'] . '-vocabulary' . ']'] = t('The name for the vocabulary the term belongs to.');
+  }
+
+  function add_self_tokens(&$tokens, $item) {
+    $tokens['[' . $this->options['id'] . '-tid' . ']'] = $item['tid'];
+    $tokens['[' . $this->options['id'] . '-name' . ']'] = $item['name'];
+    $tokens['[' . $this->options['id'] . '-vid' . ']'] = $item['vid'];
+    $tokens['[' . $this->options['id'] . '-vocabulary' . ']'] = $item['vocabulary'];
+  }
 }
 
Index: modules/upload/views_handler_field_upload_description.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/upload/views_handler_field_upload_description.inc,v
retrieving revision 1.3
diff -u -p -r1.3 views_handler_field_upload_description.inc
--- modules/upload/views_handler_field_upload_description.inc	30 Jan 2009 00:01:42 -0000	1.3
+++ modules/upload/views_handler_field_upload_description.inc	1 Jul 2009 20:39:55 -0000
@@ -4,7 +4,7 @@
 /**
  * Field handler to provide a list of roles.
  */
-class views_handler_field_upload_description extends views_handler_field_prerender_list {
+class views_handler_field_upload_description extends views_handler_field {
   function init(&$view, &$options) {
     parent::init($view, $options);
     if (!empty($options['link_to_file'])) {
@@ -43,7 +43,6 @@ class views_handler_field_upload_descrip
     }
 
     if ($fids) {
-      // Support "only listed files" option.
       $result = db_query("SELECT f.fid, f.filepath FROM {files} f WHERE f.fid IN (" . implode(', ', $fids) . ")");
       while ($file = db_fetch_object($result)) {
         $this->items[$file->fid] = $file;
Index: modules/upload/views_handler_field_upload_fid.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/upload/views_handler_field_upload_fid.inc,v
retrieving revision 1.6
diff -u -p -r1.6 views_handler_field_upload_fid.inc
--- modules/upload/views_handler_field_upload_fid.inc	7 Apr 2009 22:48:03 -0000	1.6
+++ modules/upload/views_handler_field_upload_fid.inc	1 Jul 2009 20:39:55 -0000
@@ -8,10 +8,6 @@ class views_handler_field_upload_fid ext
     parent::construct();
   }
 
-  function allow_advanced_render() {
-    return FALSE;
-  }
-
   function option_definition() {
     $options = parent::option_definition();
     $options['link_to_file'] = array('default' => FALSE);
@@ -48,22 +44,41 @@ class views_handler_field_upload_fid ext
       if (!empty($this->options['only_listed'])) {
         $where = " AND u.list <> 0";
       }
-      $result = db_query("SELECT u.vid, u.fid, f.filepath, u.description FROM {upload} u LEFT JOIN {files} f ON f.fid = u.fid WHERE u.vid IN (" . implode(', ', $vids) . ")$where ORDER BY u.weight");
-      while ($file = db_fetch_object($result)) {
-        $this->items[$file->vid][$file->fid] = $this->render_link(check_plain($file->description), $file);
+      $result = db_query("SELECT u.vid, u.fid, f.filename, f.filepath, f.filesize, f.filemime, u.description FROM {upload} u LEFT JOIN {files} f ON f.fid = u.fid WHERE u.vid IN (" . implode(', ', $vids) . ")$where ORDER BY u.weight");
+      while ($file = db_fetch_array($result)) {
+        $file['filename'] = check_plain($file['filename']);
+        $file['filemime'] = check_plain($file['filemime']);
+        $file['description'] = check_plain($file['description']);
+        $file['filesize'] = format_size($file['filesize']);
+        $file['filepath'] = file_create_url($file['filepath']);
+        if (!empty($this->options['link_to_file']) ) {
+          $file['make_link'] = TRUE;
+          $file['path'] = $file['filepath'];
+        }
+        $this->items[$file['vid']][$file['fid']] = $file;
       }
     }
   }
 
-  /**
-   * Render whatever the data is as a link to the file.
-   *
-   * Data should be made XSS safe prior to calling this function.
-   */
-  function render_link($data, $values) {
-    if (!empty($this->options['link_to_file']) && $data !== NULL && $data !== '') {
-      return l($data, file_create_url($values->filepath), array('html' => TRUE));
-    }
-    return $data;
+  function render_item($count, $item) {
+    return $item['description'];
+  }
+
+  function document_self_tokens(&$tokens) {
+    $tokens['[' . $this->options['id'] . '-fid' . ']'] = t('The file ID for the file.');
+    $tokens['[' . $this->options['id'] . '-name' . ']'] = t('The name of the attached file.');
+    $tokens['[' . $this->options['id'] . '-type' . ']'] = t('The MIME type of the attached file.');
+    $tokens['[' . $this->options['id'] . '-description' . ']'] = t('The name of the attached file.');
+    $tokens['[' . $this->options['id'] . '-path' . ']'] = t('The path of the attached file.');
+    $tokens['[' . $this->options['id'] . '-size' . ']'] = t('The size of the attached file.');
+  }
+
+  function add_self_tokens(&$tokens, $item) {
+    $tokens['[' . $this->options['id'] . '-fid' . ']'] = $item['fid'];
+    $tokens['[' . $this->options['id'] . '-name' . ']'] = $item['filename'];
+    $tokens['[' . $this->options['id'] . '-type' . ']'] = $item['filemime'];
+    $tokens['[' . $this->options['id'] . '-description' . ']'] = $item['description'];
+    $tokens['[' . $this->options['id'] . '-path' . ']'] = $item['filepath'];
+    $tokens['[' . $this->options['id'] . '-size' . ']'] = format_size($item['filesize']);
   }
 }
Index: modules/user/views_handler_field_user_roles.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/user/views_handler_field_user_roles.inc,v
retrieving revision 1.1
diff -u -p -r1.1 views_handler_field_user_roles.inc
--- modules/user/views_handler_field_user_roles.inc	3 Sep 2008 19:21:30 -0000	1.1
+++ modules/user/views_handler_field_user_roles.inc	1 Jul 2009 20:39:55 -0000
@@ -25,8 +25,23 @@ class views_handler_field_user_roles ext
     if ($uids) {
       $result = db_query("SELECT u.uid, u.rid, r.name FROM {role} r INNER JOIN {users_roles} u ON u.rid = r.rid WHERE u.uid IN (" . implode(', ', $uids) . ") ORDER BY r.name");
       while ($role = db_fetch_object($result)) {
-        $this->items[$role->uid][$role->rid] = check_plain($role->name);
+        $this->items[$role->uid][$role->rid]['role'] = check_plain($role->name);
+        $this->items[$role->uid][$role->rid]['rid'] = $role->rid;
       }
     }
   }
+
+  function render_item($count, $item) {
+    return $item['role'];
+  }
+
+  function document_self_tokens(&$tokens) {
+    $tokens['[' . $this->options['id'] . '-role' . ']'] = t('The name of the role.');
+    $tokens['[' . $this->options['id'] . '-rid' . ']'] = t('The role ID of the role.');
+  }
+
+  function add_self_tokens(&$tokens, $item) {
+    $tokens['[' . $this->options['id'] . '-role' . ']'] = $item['role'];
+    $tokens['[' . $this->options['id'] . '-rid' . ']'] = $item['rid'];
+  }
 }
