annotate views_calc.module @ 3:5635080385bd tip

Merge patch with 1.x-dev
author Franck Deroche <franck@defr.org>
date Fri, 07 Aug 2009 15:20:12 +0200
parents b0a976e17cc7
children
rev   line source
franck@0 1 <?php
franck@0 2 /**
franck@0 3 * @file
franck@0 4 * This module will allow you to add calculated fields to views tables
franck@0 5 * and compute (SUM, COUNT, AVG, etc) columns of numeric data in a views table.
franck@0 6 */
franck@0 7 function views_calc_views_api() {
franck@0 8 return array(
franck@0 9 'api' => 2,
franck@0 10 'path' => drupal_get_path('module', 'views_calc'),
franck@0 11 );
franck@0 12 }
franck@0 13
franck@0 14 function views_calc_theme() {
franck@0 15 $path = drupal_get_path('module', 'views_calc');
franck@0 16 return array(
franck@0 17 'views_calc_ui_table' => array(
franck@0 18 'arguments' => array('form' => NULL),
franck@0 19 'file' => 'theme.inc',
franck@0 20 ),
franck@0 21 );
franck@0 22 }
franck@0 23
franck@0 24 /**
franck@0 25 * Implementation of hook_help().
franck@0 26 */
franck@0 27 function views_calc_help($section, $arg) {
franck@0 28 switch ($section) {
franck@0 29 case 'admin/settings/views_calc':
franck@0 30 case 'admin/settings/views_calc/fields':
franck@0 31 return t('<p>Set up calculation fields. Calculation fields will be displayed in the views fields list and can be added to any view.</p>');
franck@0 32 case 'admin/settings/views_calc/settings':
franck@0 33 return t('Put one operator on each line. To avoid the possibility of SQL injection, calculation text will only allow these values, numbers, and field names. Make sure this list includes any text other than field names that should be allowed in the calculation fields.');
franck@0 34 case 'admin/help#views_calc':
franck@0 35 return t('<ul> <li>Go to admin/settings/views_calc to create calculations.</li> <li>The \'Fields\' tab will allow you to create calculated fields that can be inserted into any view. The calculations can include the value of any Views field, combined with numbers, arithmatic operations, and common SQL functions like ROUND() or MIN(). Each available field has a shortcut name like %Node:Title. Create SQL snippets like (%Node:Field1 + 84600) or ROUND(%Node:Field2 / 3). </li> <li>The \'Columns\' tab allows you to set column calculations. The column totals are added in the view itself by using the style \'Views Calc Table\' and setting the fields in the table that should have column totals.</li> <li>The \'Settings\' tab allows you to add new functions to the list of allowable functions. </ul>');
franck@0 36 }
franck@0 37 }
franck@0 38
franck@0 39 /**
franck@0 40 * Default SQL operator alternatives.
franck@0 41 *
franck@0 42 * The ones allowed in this system are stored in the
franck@0 43 * variable views_calc_operators, and can be changed
franck@0 44 * at admin/settings/views_calc.
franck@0 45 *
franck@0 46 */
franck@0 47 function _views_calc_operators() {
franck@0 48 $default = array('+', '-', '*', '/', '(', ')', ',', "'", 'CONCAT', 'MIN', 'MAX', 'ROUND', 'NOW()');
franck@0 49 $operators = variable_get('views_calc_operators', implode("\n", $default));
franck@0 50 return explode("\n", $operators);
franck@0 51
franck@0 52 }
franck@0 53
franck@0 54 /**
franck@0 55 * Column calculation alternatives
franck@0 56 */
franck@0 57 function _views_calc_calc_options() {
franck@0 58 return array('COUNT' => 'Count', 'SUM' => 'Sum', 'AVG' => 'Average', 'MIN' => 'Minimum', 'MAX' => 'Maximum');
franck@0 59 }
franck@0 60
franck@0 61 /**
franck@0 62 * Result format options
franck@0 63 */
franck@0 64 function _views_calc_format_options() {
franck@0 65 $options = array(
franck@0 66 'none' => '',
franck@0 67 'integer' => 'intval',
franck@0 68 'decimal (1)' => 'number_format:1',
franck@0 69 'decimal (2)' => 'number_format:2',
franck@0 70 'shortdate' => 'format_date:small',
franck@0 71 'mediumdate' => 'format_date',
franck@0 72 'longdate' => 'format_date:large',
franck@0 73 'custom' => '',
franck@0 74 );
franck@0 75 return $options;
franck@0 76 }
franck@0 77
franck@0 78 /**
franck@0 79 * Implementation of hook_perm().
franck@0 80 *
franck@0 81 * The permission 'administer views calc' has rights to alter the SQL
franck@0 82 * operators that can be used in calculations.
franck@0 83 *
franck@0 84 * The permission 'create views calc' has rights to create calculated
franck@0 85 * fields and set calculation columns on views.
franck@0 86 */
franck@0 87 function views_calc_perm() {
franck@0 88 return array('create views calc', 'administer views calc');
franck@0 89 }
franck@0 90
franck@0 91 function views_calc_menu() {
franck@0 92
franck@0 93 $items = array();
franck@0 94 $items['admin/settings/views_calc'] = array(
franck@0 95 'title' => t('Views Calc'),
franck@0 96 'description' => t('Set Views Calc fields and columns.'),
franck@0 97 'type' => MENU_NORMAL_ITEM,
franck@0 98 'weight' => 10,
franck@0 99 'priority' => 1,
franck@0 100 'page callback' => 'drupal_get_form',
franck@0 101 'page arguments' => array('views_calc_fields_form'),
franck@0 102 'access arguments' => array('create views calc'),
franck@0 103 );
franck@0 104 $items['admin/settings/views_calc/fields'] = array(
franck@0 105 'title' => t('Fields'),
franck@0 106 'type' => MENU_DEFAULT_LOCAL_TASK,
franck@0 107 'weight' => 5,
franck@0 108 'priority' => 1,
franck@0 109 'page callback' => 'drupal_get_form',
franck@0 110 'page arguments' => array('views_calc_fields_form'),
franck@0 111 'access arguments' => array('create views calc'),
franck@0 112 );
franck@0 113 $items['admin/settings/views_calc/settings'] = array(
franck@0 114 'title' => t('Settings'),
franck@0 115 'type' => MENU_LOCAL_TASK,
franck@2 116 'weight' => 6,
franck@0 117 'priority' => 1,
franck@0 118 'page callback' => 'drupal_get_form',
franck@0 119 'page arguments' => array('views_calc_settings_form'),
franck@0 120 'access arguments' => array('administer views calc'),
franck@0 121 );
franck@2 122 $items['admin/settings/views_calc/export'] = array(
franck@2 123 'title' => 'Export fields',
franck@2 124 'page callback' => 'drupal_get_form',
franck@2 125 'page arguments' => array('views_calc_export_form'),
franck@2 126 'access arguments' => array('create views calc'),
franck@2 127 'type' => MENU_LOCAL_TASK,
franck@2 128 'weight' => 7,
franck@2 129 );
franck@2 130 $items['admin/settings/views_calc/import'] = array(
franck@2 131 'title' => 'Import fields',
franck@2 132 'page callback' => 'drupal_get_form',
franck@2 133 'page arguments' => array('views_calc_import_form'),
franck@2 134 'access arguments' => array('create views calc'),
franck@2 135 'type' => MENU_LOCAL_TASK,
franck@2 136 'weight' => 8,
franck@2 137 );
franck@2 138
franck@0 139 return $items;
franck@0 140 }
franck@0 141
franck@0 142 /**
franck@0 143 * Implementation of hook_settings()
franck@0 144 */
franck@0 145 function views_calc_settings_form() {
franck@0 146 drupal_set_title(t('Views Calc'));
franck@0 147 $operators = _views_calc_operators();
franck@0 148 $form['views_calc_operators'] = array(
franck@0 149 '#type' => 'textarea',
franck@0 150 '#default_value' => implode("\n", $operators),
franck@0 151 '#title' => t('Allowable functions and operators'),
franck@0 152 '#rows' => intval(sizeof($operators) + 2),
franck@0 153 );
franck@0 154 $form['submit'] = array(
franck@0 155 '#type' => 'submit',
franck@0 156 '#value' => t('Save'),
franck@0 157 );
franck@0 158 return $form;
franck@0 159 }
franck@0 160
franck@0 161 function views_calc_settings_form_submit($form, &$form_state) {
franck@0 162 $form_values = $form_state['values'];
franck@0 163 variable_set('views_calc_operators', $form_values['views_calc_operators']);
franck@0 164 }
franck@0 165
franck@0 166 /**
franck@0 167 * Views Calc Fields tab on views list.
franck@0 168 */
franck@0 169 function views_calc_fields_form() {
franck@0 170 $i = 0;
franck@2 171 $substitutions = array();
franck@2 172 $help = t('<p>The specific fields that are available in any view depend on the base table used for that view.</p>');
franck@2 173 require_once(drupal_get_path('module', 'views') .'/includes/admin.inc');
franck@2 174 $base_tables = views_fetch_base_tables();
franck@2 175 foreach ($base_tables as $base => $data) {
franck@2 176 $base_subs = _views_calc_substitutions($base);
franck@2 177 $substitutions += $base_subs;
franck@2 178 $fieldset = array(
franck@2 179 '#title' => t('Base table: !name', array('!name' => t($data['title']))),
franck@2 180 '#value' => theme('item_list', $base_subs),
franck@2 181 '#collapsible' => TRUE,
franck@2 182 '#collapsed' => TRUE,
franck@2 183 );
franck@2 184 $help .= theme('fieldset', $fieldset);
franck@2 185 }
franck@0 186
franck@0 187 // display current views calcs fields
franck@0 188 $fields = _views_calc_fields();
franck@0 189 while ($field = db_fetch_array($fields)) {
franck@0 190 $form[] = views_calc_field_form_item($i, $field, $substitutions);
franck@0 191 $i++;
franck@0 192 }
franck@0 193 // add blank fields for more calcs
franck@0 194 for ($x = $i + 1; $x < $i + 2; $x++) {
franck@0 195 $field = array();
franck@0 196 $form[] = views_calc_field_form_item($i, $field, $substitutions);
franck@0 197 }
franck@0 198 $form['#prefix'] = '<div class="views-calc-field-settings">';
franck@2 199 $form['#suffix'] = '</div><div class="views-calc-field-names"><strong>Field Substitutions</strong><div class="form-item">'. $help .'</div></div>';
franck@0 200 $form['submit'] = array(
franck@0 201 '#type' => 'submit',
franck@0 202 '#value' => t('Save'),
franck@0 203 );
franck@0 204 return $form;
franck@0 205 }
franck@0 206
franck@0 207 /**
franck@0 208 * A form element for an individual calculated field.
franck@0 209 */
franck@0 210 function views_calc_field_form_item($i, $field, $substitutions) {
franck@0 211 if (empty($field)) {
franck@0 212 $field = array('cid' => 0, 'label' => '', 'tablelist' => '', 'calc' => '', 'format' => '', 'custom' => '');
franck@0 213 }
franck@2 214 require_once(drupal_get_path('module', 'views') .'/includes/admin.inc');
franck@2 215 $options = array();
franck@2 216 $base_tables = views_fetch_base_tables();
franck@2 217 foreach ($base_tables as $base => $data) {
franck@2 218 $options[$base] = t($data['title']);
franck@2 219 }
franck@0 220 $form['group'][$i] = array(
franck@0 221 '#type' => 'fieldset',
franck@0 222 '#tree' => TRUE,
franck@0 223 '#title' => t('Field: ') . !empty($field['label']) ? $field['label'] : t('New'),
franck@0 224 '#collapsible' => TRUE,
franck@0 225 '#collapsed' => FALSE,
franck@0 226 );
franck@0 227 $form['group'][$i]['cid'] = array(
franck@0 228 '#type' => 'hidden',
franck@0 229 '#value' => intval($field['cid']),
franck@0 230 );
franck@0 231 $form['group'][$i]['tablelist'] = array(
franck@0 232 '#type' => 'hidden',
franck@0 233 '#value' => $field['tablelist'],
franck@0 234 );
franck@2 235 $form['group'][$i]['base'] = array(
franck@2 236 '#type' => 'select',
franck@2 237 '#title' => t('Base table'),
franck@2 238 '#options' => $options,
franck@2 239 '#default_value' => !empty($field['base']) && array_key_exists($field['base'], $options) ? $field['base'] : 'node',
franck@2 240 '#description' => t('The base table for this field.'),
franck@2 241 );
franck@0 242 $form['group'][$i]['label'] = array(
franck@0 243 '#type' => 'textfield',
franck@0 244 '#title' => t('Label'),
franck@0 245 '#field_prefix' => 'ViewsCalc: ',
franck@0 246 '#default_value' => str_replace('ViewsCalc: ', '', $field['label']),
franck@0 247 '#description' => t('The views field name for this field (i.e. Views Calc: My Calculation).'),
franck@0 248 );
franck@0 249 $form['group'][$i]['calc'] = array(
franck@0 250 '#type' => 'textarea',
franck@0 251 '#title' => t('Calculation'),
franck@0 252 '#default_value' => strtr($field['calc'], $substitutions),
franck@2 253 '#description' => t("<p>The query operation to be performed, using numbers, field substitutions, and ". implode(' ', _views_calc_operators()) .". Leave spaces between parentheses and field names, i.e. 'CONCAT( %field1, ' ', %field2 )'. <strong>". t('Note that all fields must be from the base table selected above! You cannot combine fields from different base tables.') ."</strong></p>"),
franck@0 254 );
franck@0 255 $form['group'][$i]['format'] = array(
franck@0 256 '#type' => 'select',
franck@0 257 '#title' => t('Format'),
franck@0 258 '#default_value' => $field['format'],
franck@0 259 '#options' => drupal_map_assoc(array_keys(_views_calc_format_options())),
franck@0 260 '#description' => t('The format of the result of this calculation.'),
franck@0 261 );
franck@0 262 $form['group'][$i]['custom'] = array(
franck@0 263 '#type' => 'textfield',
franck@0 264 '#title' => t('Custom function'),
franck@0 265 '#default_value' => $field['custom'],
franck@0 266 '#description' => t('The function to call for a custom format.'),
franck@0 267 );
franck@0 268 return $form;
franck@0 269 }
franck@0 270
franck@0 271 /**
franck@0 272 * Validate the views calc settings
franck@0 273 */
franck@0 274 function views_calc_fields_form_validate($form, &$form_state) {
franck@0 275 $form_values = $form_state['values'];
franck@0 276 $edit = $form_values;
franck@0 277 foreach ($edit as $delta => $item) {
franck@0 278 if ($item['calc'] == '' || !is_numeric($delta)) {
franck@0 279 // remove blank fields, don't save them
franck@2 280 continue;
franck@2 281 }
franck@2 282 else {
franck@0 283 // Remove all valid values from calc, if anything is left over, it is invalid.
franck@0 284
franck@0 285 // First, remove all field names.
franck@0 286 $repl = array();
franck@0 287 $patterns = array();
franck@2 288 $base = $item['base'];
franck@2 289 foreach (_views_calc_substitutions($base) as $key => $value) {
franck@0 290 $key = trim($value);
franck@0 291 $count = strlen($value);
franck@0 292 $replace = preg_quote($value);
franck@0 293 $patterns[] = "`(^|[^\\\\\\\\])". $replace ."`";
franck@0 294 $repl[] = '${1}';
franck@0 295 }
franck@0 296 $remaining = trim(preg_replace($patterns, $repl, $item['calc']));
franck@2 297
franck@0 298 // Next, remove functions and numbers.
franck@0 299 $repl = array();
franck@0 300 $patterns = array();
franck@0 301 foreach (_views_calc_replacements() as $value) {
franck@0 302 $patterns[] = "`(^|[^\\\\\\\\])". preg_quote(trim($value)) ."`";
franck@0 303 $repl[] = '${1}';
franck@0 304 }
franck@0 305 $remaining = trim(preg_replace($patterns, $repl, $remaining));
franck@0 306 if (!empty($remaining)) {
franck@0 307 form_set_error($form_values[$delta]['calc'], t('The values %remaining in %field are not allowed.', array('%remaining' => $remaining, '%field' => $item['label'])));
franck@0 308 }
franck@0 309 }
franck@0 310 }
franck@0 311 }
franck@0 312
franck@0 313 /**
franck@0 314 * Save the views calc field settings
franck@0 315 */
franck@0 316 function views_calc_fields_form_submit($form, &$form_state) {
franck@2 317 $edit = $form_state['values'];
franck@2 318 $form_values = array();
franck@0 319 foreach ($edit as $delta => $value) {
franck@2 320 // If this is some form item we don't care about, skip it.
franck@2 321 if (!is_array($value) || !is_numeric($delta)) {
franck@2 322 continue;
franck@2 323 }
franck@2 324 $value['calc'] = trim($value['calc']);
franck@2 325 if (empty($value['calc'])) {
franck@0 326 // remove blank fields, don't save them
franck@2 327 if (!empty($value['cid'])) {
franck@2 328 db_query("DELETE FROM {views_calc_fields} WHERE cid=%d", $value['cid']);
franck@2 329 }
franck@0 330
franck@0 331 }
franck@0 332 else {
franck@0 333 $tables = array();
franck@0 334 $form_values[$delta]['label'] = $value['label'];
franck@0 335 $form_values[$delta]['format'] = $value['format'];
franck@0 336 $form_values[$delta]['custom'] = $value['custom'];
franck@0 337 $form_values[$delta]['calc'] = $value['calc'];
franck@2 338 $form_values[$delta]['base'] = $value['base'];
franck@0 339
franck@0 340 // Substitute field names back into the calculation.
franck@0 341 $matches = array();
franck@2 342 $base = $value['base'];
franck@2 343 foreach (_views_calc_substitutions($base) as $key => $value) {
franck@0 344 $label_patterns[] = "`(^|[^\\\\\\\\])". preg_quote($value) ."`";
franck@0 345 $value_patterns[] = "`(^|[^\\\\\\\\])". preg_quote($key) ."`";
franck@0 346 $repl[] = '${1}'. $key;
franck@0 347 }
franck@0 348 $form_values[$delta]['calc'] = preg_replace($label_patterns, $repl, $form_values[$delta]['calc']);
franck@0 349
franck@0 350 // Extract the fields and table names from the calculation.
franck@0 351 $tables = array();
franck@0 352 $fields = array();
franck@0 353 foreach ($value_patterns as $pattern) {
franck@0 354 if (preg_match($pattern, $form_values[$delta]['calc'], $results)) {
franck@2 355 $fields[trim($results[0])] = trim($results[0]);
franck@0 356 $tmp = explode('.', trim($results[0]));
franck@0 357 if (trim($tmp[0])) {
franck@0 358 $tables[trim($tmp[0])] = trim($tmp[0]);
franck@0 359 }
franck@0 360 }
franck@0 361 }
franck@0 362 $form_values[$delta]['tablelist'] = implode(',', $tables);
franck@0 363 $form_values[$delta]['fieldlist'] = implode(',', $fields);
franck@0 364 }
franck@0 365 }
franck@2 366
franck@2 367 foreach ((array) $form_values as $delta => $value) {
franck@2 368 if (empty($value['cid'])) {
franck@0 369 drupal_write_record('views_calc_fields', $value);
franck@0 370 }
franck@0 371 else {
franck@0 372 drupal_write_record('views_calc_fields', $value, array('cid'));
franck@0 373 }
franck@0 374 }
franck@0 375 views_invalidate_cache();
franck@0 376 drupal_set_message(t('Views Calc fields were updated.'));
franck@0 377 }
franck@0 378
franck@0 379 /**
franck@0 380 * Wrapper function to make sure this function will always work.
franck@0 381 */
franck@0 382 function views_calc_views_fetch_fields($base, $type) {
franck@0 383 if (!module_exists('views')) {
franck@0 384 return array();
franck@0 385 }
franck@0 386 require_once('./'. drupal_get_path('module', 'views') .'/includes/admin.inc');
franck@0 387 return views_fetch_fields($base, $type);
franck@0 388 }
franck@0 389
franck@0 390 /**
franck@0 391 * Field substitutions for calculations.
franck@0 392 */
franck@0 393 function _views_calc_substitutions($base = 'node') {
franck@0 394 $fields = views_calc_views_fetch_fields($base, 'field');
franck@0 395 $substitutions['node.nid'] = '%Node.nid';
franck@0 396 $substitutions['node.uid'] = '%Node.uid';
franck@0 397 foreach ($fields as $key => $field) {
franck@0 398 // For now, omit calculated fields from available fields list.
franck@0 399 // Doing caculations on calculated fields will require some
franck@0 400 // complex additional logic, especially if they are nested
franck@0 401 // several levels deep.
franck@0 402 if (substr($key, 0, 4) != '.cid') {
franck@0 403 $substitutions[$key] = '%'. str_replace(' ', '', $key);
franck@0 404 }
franck@0 405 }
franck@0 406 return $substitutions;
franck@0 407 }
franck@0 408
franck@0 409 /**
franck@0 410 * Views calc fields result object
franck@0 411 */
franck@0 412 function _views_calc_fields() {
franck@0 413 return db_query("SELECT * FROM {views_calc_fields}");
franck@0 414 }
franck@0 415
franck@0 416 /**
franck@0 417 * An array of allowable calculation values.
franck@0 418 */
franck@0 419 function _views_calc_replacements() {
franck@0 420 $operators = array_filter(_views_calc_operators(), 'trim');
franck@0 421 $numbers = range(0, 9);
franck@0 422 return array_merge($operators, $numbers);
franck@2 423 }
franck@2 424
franck@2 425
franck@2 426 /**
franck@2 427 * Field export form.
franck@2 428 */
franck@2 429 function views_calc_export_form() {
franck@2 430
franck@2 431 $fields = _views_calc_fields();
franck@2 432 $string = '';
franck@2 433 while ($field = db_fetch_array($fields)) {
franck@2 434 $base = $field['base'];
franck@2 435 $substitutions = _views_calc_substitutions($base);
franck@2 436 $field['calc'] = strtr($field['calc'], $substitutions);
franck@2 437 $string .= "\$fields[] = ". var_export((array) $field, TRUE) .";\n";
franck@2 438 }
franck@2 439
franck@2 440 $form['#prefix'] = t('This form will export Views Calc custom fields.');
franck@2 441 $form['macro'] = array(
franck@2 442 '#type' => 'textarea',
franck@2 443 '#rows' => 20,
franck@2 444 '#title' => t('Export data'),
franck@2 445 '#default_value' => $string,
franck@2 446 '#description' => t('This is an export of the custom Views Calc fields. Paste this text into a Views Calc import box to import these fields into another installation. This will only work if the other installation uses the same base tables required by these fields.'),
franck@2 447 );
franck@2 448 return $form;
franck@2 449 }
franck@2 450
franck@2 451 /**
franck@2 452 * Field import form.
franck@2 453 */
franck@2 454 function views_calc_import_form(&$form_state, $type_name = '') {
franck@2 455 $form['#prefix'] = t('This form will import Views Calc custom fields.');
franck@2 456 $form['macro'] = array(
franck@2 457 '#type' => 'textarea',
franck@2 458 '#rows' => 20,
franck@2 459 '#title' => t('Import data'),
franck@2 460 '#required' => TRUE,
franck@2 461 '#description' => t('Paste the text created by a Views Calc export into this field.'),
franck@2 462 );
franck@2 463 $form['submit'] = array(
franck@2 464 '#type' => 'submit',
franck@2 465 '#value' => t('Import'),
franck@2 466 );
franck@2 467 // Read in a file if there is one and set it as the default macro value.
franck@2 468 if (isset($_REQUEST['macro_file']) && $file = file_get_contents($_REQUEST['macro_file'])) {
franck@2 469 $form['macro']['#default_value'] = $file;
franck@2 470 if (isset($_REQUEST['type_name'])) {
franck@2 471 $form['type_name']['#default_value'] = $_REQUEST['type_name'];
franck@2 472 }
franck@2 473 $form['#prefix'] .= '<p class="error">'. t('A file has been pre-loaded for import.') .'</p>';
franck@2 474 }
franck@2 475 $form['#redirect'] = 'admin/settings/views_calc';
franck@2 476 return $form;
franck@2 477 }
franck@2 478
franck@2 479 /**
franck@2 480 * Submit handler for import form.
franck@2 481 */
franck@2 482 function views_calc_import_form_submit($form, &$form_state) {
franck@2 483 $form_values = $form_state['values'];
franck@2 484 $fields = NULL;
franck@2 485
franck@2 486 // Use '@' to suppress errors about undefined constants in the macro.
franck@2 487 @eval($form_values['macro']);
franck@2 488
franck@2 489 if (empty($fields) || !is_array($fields)) {
franck@2 490 return;
franck@2 491 }
franck@2 492
franck@2 493 foreach ($fields as $delta => $field) {
franck@2 494 // Don't over-write existing fields, create new ones.
franck@2 495 $fields[$delta]['cid'] = NULL;
franck@2 496 }
franck@2 497
franck@2 498 // Run the values thru drupal_execute() so they are properly validated.
franck@2 499 $form_state = array('values' => $fields);
franck@2 500 drupal_execute('views_calc_fields_form', $form_state);
franck@2 501
franck@0 502 }