? views_calc.patch
Index: views_customfield.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views_customfield/Attic/views_customfield.views.inc,v
retrieving revision 1.1.2.6
diff -u -p -r1.1.2.6 views_customfield.views.inc
--- views_customfield.views.inc	15 Jun 2009 06:16:25 -0000	1.1.2.6
+++ views_customfield.views.inc	13 Sep 2009 19:24:25 -0000
@@ -68,3 +68,39 @@ function views_customfield_views_data() 
   
   return $data;
 }
+
+/**
+ * Implementation of hook_views_plugins().
+ */
+function views_customfield_views_plugins() {
+  if (!module_exists('views_calc')) return array();
+
+  return array(
+    'style' => array(
+      'views_customfield_calc_parent' => array(
+        // this isn't really a display but is necessary so the file can
+        // be included.
+        'no ui' => TRUE,
+        'handler' => 'views_calc_table',
+        'path' => drupal_get_path('module', 'views_calc'),
+        'parent' => '',
+      ),
+      'views_customfield_calc' => array(
+        'title' => t('Views Custom Field Calc Table'),
+        'help' => t('Creates a table with column calculations (including custom fields).'),
+        'handler' => 'views_customfield_calc_table',
+        'path' => drupal_get_path('module', 'views_customfield') .'/includes',
+        'parent' => 'views_calc',
+        'theme' => 'views_calc_table',
+        'theme file' => 'theme.inc',
+        'theme path' => drupal_get_path('module', 'views_calc'),
+        'uses row plugin' => FALSE,
+        'uses fields' => TRUE,
+        'uses options' => TRUE,
+        'type' => 'normal',
+        'even empty' => FALSE,
+      ),
+    ),
+  );
+}
+
Index: includes/views_customfield_calc_table.inc
===================================================================
RCS file: includes/views_customfield_calc_table.inc
diff -N includes/views_customfield_calc_table.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/views_customfield_calc_table.inc	13 Sep 2009 19:24:25 -0000
@@ -0,0 +1,114 @@
+<?php
+// $Id$
+
+class views_customfield_calc_table extends views_calc_table {
+  function render() {
+    if (!empty($this->view->totals)) {
+      $totals =& $this->view->totals;
+      $i = 0;
+      foreach ($this->get_calc_fields() as $calc => $fields) {
+        foreach ($fields as $field) {
+          $view_field = $this->view->field[$field];
+          if ($view_field->table == 'customfield') {
+            $total = $this->customfield_totals($view_field, $calc);
+            $totals[$i]->{$view_field->field_alias} = $total;
+          }
+        }
+        $i++;
+      }
+    }
+    return parent::render();
+  }
+
+  function customfield_totals($field, $calc) {
+    static $totals = array();
+    if (!isset($totals[$field->field_alias])) {
+      $totals[$field->field_alias] = array(
+        'SUM' => 0.0,
+        'COUNT' => count($this->view->result),
+        'MIN' => $this->view->result[0]->{$field->field_alias}, 
+        'MAX' => $this->view->result[0]->{$field->field_alias},
+        'AVG' => 0.0,
+      );
+      if (!empty($this->view->result)) {
+        foreach ($this->view->result as $row) {
+          if (!isset($row->{$field->field_alias})) {
+            $totals[$field->field_alias]['COUNT']--;
+          }
+          else {
+            $value = $row->{$field->field_alias};
+            $totals[$field->field_alias]['SUM'] += $value;
+            if ($totals[$field->field_alias]['MAX'] < $value) $totals[$field->field_alias]['MAX'] = $value;
+            if ($totals[$field->field_alias]['MIN'] > $value) $totals[$field->field_alias]['MIN'] = $value;
+          }
+        }
+        $totals[$field->field_alias]['AVG'] = $totals[$field->field_alias]['SUM'] / $totals[$field->field_alias]['COUNT'];
+      }
+    }
+    return $totals[$field->field_alias][$calc];
+  }
+
+  function query_sub_total() {
+    // Create summary rows.
+    $calc_fields = $this->get_calc_fields();
+    $calc = $this->view->views_calc_calculation;
+    $fields = $calc_fields[$calc];
+    
+    // Empty out any fields that have been added to the query,
+    // we don't need them for the summary totals.
+    $this->view->query->fields = array();
+    foreach ($this->view->field as $field) {
+      if ($field->table == 'customfield') continue;
+
+      $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table .'.'. $field->field;
+      $query_alias = $field->field_alias;
+      if (in_array($field->field, $fields)) {
+        // Calculated fields.
+        $this->view->query->add_field(NULL, "$calc($query_field)", $query_alias);
+      }
+      else {
+        // Empty fields that have no calculations.
+        $this->view->query->add_field(NULL, "MAX('')", $query_alias);
+      }
+      // Add a dummy field for the groupby.
+      $this->view->query->add_field(NULL, "MAX('". $calc ."')", "TOTAL_". $calc);
+    }
+    // TODO This won't work right with relationships, need a fix here.
+    if (!empty($this->view->views_calc_nids)) {
+      $this->view->query->add_where(NULL, "node.nid IN (%s)", implode(',', $this->view->views_calc_nids));
+    }
+  }
+  
+  function query_total() {
+    // Create summary rows.
+    $calc_fields = $this->get_calc_fields();
+    $calc = $this->view->views_calc_calculation;
+    $fields = $calc_fields[$calc];
+    
+    // Empty out any fields that have been added to the query,
+    // we don't need them for the summary totals.
+    $this->view->query->fields = array();
+    // Clear out any sorting and grouping, it can create unexpected results
+    // when Views adds aggregation values for the sorts.
+    $this->view->query->orderby = array();
+    $this->view->query->groupby = array();
+
+    foreach ($this->view->field as $field) {
+      if ($field->table == 'customfield') continue;
+
+      $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table .'.'. $field->field;
+      $query_alias = $field->field_alias;
+      if (!empty($fields) && in_array($field->field, $fields)) {
+        // Calculated fields.
+        $this->view->query->add_field(NULL, "$calc($query_field)", $query_alias);
+      }
+      else {
+        // Empty fields that have no calculations.
+        $this->view->query->add_field(NULL, "MAX('')", $query_alias);
+      }
+      // Add a dummy field for the groupby.
+      $this->view->query->add_field(NULL, "MAX('". $calc ."')", "TOTAL_". $calc);
+    }
+  }
+}
+
Index: includes/views_customfield_handler_field_phpcode.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views_customfield/includes/Attic/views_customfield_handler_field_phpcode.inc,v
retrieving revision 1.1.2.7
diff -u -p -r1.1.2.7 views_customfield_handler_field_phpcode.inc
--- includes/views_customfield_handler_field_phpcode.inc	16 Jul 2009 16:22:21 -0000	1.1.2.7
+++ includes/views_customfield_handler_field_phpcode.inc	13 Sep 2009 19:24:25 -0000
@@ -26,6 +26,7 @@ class views_customfield_handler_field_ph
   function option_definition() {
     $options = parent::option_definition();
     $options['value'] = array('default' => '');
+    $options['format'] = array('default' => '');
     $options['sortable'] = array('default' => VIEWS_CUSTOMFIELD_SORTABLE_NO);
     return $options;
   }
@@ -40,13 +41,23 @@ class views_customfield_handler_field_ph
       '#title' => t('Value'),
       '#default_value' => $this->options['value'],
       '#rows' => 5,
-      '#description' => 
-        t('The text that should be displayed.')
-        .' '.t('Include &lt;?php ?&gt; delimiters when using PHP code.')
-        .' '.t('Available variables:').'<br/>'
-        .t('$data: contains the retrieved record from the database (e.g. $data->nid).').'<br/>'
-        .t('$static: can be used to store reusable data per row.')
-      ,
+      '#description' => t('The code used to compute the value of this field, enclosed within &lt;?php ?&gt; delimiters. '.
+                          'Available variables:<ul>'.
+                          '<li><code>$data</code>: the retrieved record from the database (e.g. <code>$data->nid</code>).</li>'.
+                          '<li><code>$static</code>: can be used to store reusable data per row.</li>'.
+                          '<li><code>$this</code>: the current <code>views_customfield_handler_field_phpcode</code> object.</li></ul>' 
+                         ),
+    );
+    $form['format'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Format'),
+      '#default_value' => $this->options['format'],
+      '#rows' => 5,
+      '#description' => t('The code used to format the value computed above. Available variables:<ul>'.
+                          '<li><code>$value</code>: the computed value.</li>'.
+                          '<li><code>$static</code>: can be used to store reusable data per row.</li>'.
+                          '<li><code>$this</code>: the current <code>views_customfield_handler_field_phpcode</code> object.</li></ul>'
+                         ),
     );
     $form['sortable'] = array(
       '#type' => 'radios',
@@ -130,7 +141,8 @@ class views_customfield_handler_field_ph
   }
 
   function render($values) {
-    return $values->{$this->field_alias};
+    $value = $values->{$this->field_alias};
+    return empty($this->options['format']) ? $value : $this->render_phpcode($this->options['format'], array('static' => &$this->static, 'value' => $value));
   }
 
   /**
@@ -159,7 +171,7 @@ class views_customfield_handler_field_ph
       }
     }
     
-    return $this->render_phpcode($this->options['value'], $this->static, $values);
+    return $this->render_phpcode($this->options['value'], array('static' => &$this->static, 'data' => $values));
   }
 
   /**
@@ -172,7 +184,7 @@ class views_customfield_handler_field_ph
    * @param array $data
    * @return string
    */
-  function render_phpcode($code, &$static, $data) {
+  function render_phpcode($code, $variables) {
     global $theme_path, $theme_info, $conf;
   
     // Store current theme path.
@@ -188,27 +200,16 @@ class views_customfield_handler_field_ph
       $theme_path = dirname($theme_info->filename);
     }
   
-    $output = $this->eval_phpcode($code, $static, $data);
+    extract($variables, EXTR_REFS);
+    ob_start();
+    print eval('?>'. $code);
+    $output = ob_get_clean();
   
     // Recover original theme path.
     $theme_path = $old_theme_path;
   
     return $output;
   }
-
-  /**
-   * Evaluate a string of PHP code.
-   *
-   * @param string $code
-   * @param mixed $static
-   * @param array $data
-   * @return string
-   */
-  function eval_phpcode($code, &$static, $data) {
-    ob_start();
-    print eval('?>'. $code);
-    return ob_get_clean();
-  }
 }
 
 class views_customfield_sorter {
