forena.common.inc 15.4 KB
Newer Older
metzlerd's avatar
metzlerd committed
1
<?php
metzlerd's avatar
metzlerd committed
2
// $Id$
metzlerd's avatar
metzlerd committed
3
4
/**
 * @file
metzlerd's avatar
metzlerd committed
5
6
 * Common functions used throughout the project but loaded in this
 * file to keep the module file lean.
metzlerd's avatar
metzlerd committed
7
 */
metzlerd's avatar
metzlerd committed
8
// Include Report renderer.
9
10
require_once 'Frx.inc';
require_once 'FrxReport.inc';
11
require_once 'FrxDocument.inc';
metzlerd's avatar
metzlerd committed
12
require_once('FrxDataSource.inc');
13
require_once('FrxReportGenerator.inc');
metzlerd's avatar
metzlerd committed
14
require_once('FrxSQLQueryBuilder.inc');
15
require_once('FrxDrupalApplication.inc');
16
require_once('FrxData.inc');
metzlerd's avatar
metzlerd committed
17

18
/**
19
 * Build cache
20
 *
metzlerd's avatar
metzlerd committed
21
22
 * @param xhtml $r_xhtml Forena XML report.
 * @return array data to be stored in the cache field for the report in the database.
23
 */
metzlerd's avatar
metzlerd committed
24
25
26
27
function forena_load_cache($r_xhtml) {
  $conf=array();
  $cache = array();
  $blocks = array();
metzlerd's avatar
metzlerd committed
28
  $repos = array();
metzlerd's avatar
metzlerd committed
29
  $nodes = array();
30
  if (is_object($r_xhtml)) {
metzlerd's avatar
metzlerd committed
31
    $head = @$r_xhtml->head;
32
33

    if ($head)  $nodes = $head->xpath('frx:menu');
metzlerd's avatar
metzlerd committed
34
    $menu = array();
35
    if ($nodes) {
metzlerd's avatar
metzlerd committed
36
37
38
39
      $m = $nodes[0];
      $attrs = $m->attributes();
      foreach ($attrs as $key => $value) {
        $menu[$key] = (string)$value;
40
      }
metzlerd's avatar
metzlerd committed
41
42
    }
    if ($menu) $cache['menu'] = $menu;
43

metzlerd's avatar
metzlerd committed
44
    $block_xml = $r_xhtml->xpath('//*[@frx:block]');
45
    // Extract all the blocks and organize by provider
metzlerd's avatar
metzlerd committed
46

47
    foreach ($block_xml as $key => $block_node) {
metzlerd's avatar
metzlerd committed
48
49
50
      $attrs = $block_node->attributes('urn:FrxReports');
      foreach ($attrs as $key => $value) {
        if ($key == 'block') {
metzlerd's avatar
metzlerd committed
51
          @list($provider, $block) = explode('/', $value, 2);
52
          $repos[$provider][] = $block;
metzlerd's avatar
metzlerd committed
53
        }
54
      }
metzlerd's avatar
metzlerd committed
55
56
    }

57
58
    if ($repos) foreach ($repos as $provider_key => $blocks) {
      $provider = Frx::RepoMan()->repository($provider_key);
metzlerd's avatar
metzlerd committed
59
60
      if (isset($provider->conf))$conf = $provider->conf;
      $access = array();
61
      foreach ($blocks as $block_name) {
metzlerd's avatar
metzlerd committed
62
        if ($provider && $block_name) {
63
          if (method_exists($provider, 'loadBlock')) {
metzlerd's avatar
metzlerd committed
64
            $conf = $provider->conf;
metzlerd's avatar
metzlerd committed
65
            $block = $provider->loadBlock($block_name);
66
67
            $obj = @$block['access'];
            if (array_search($obj, $access)===FALSE) $access[]=$obj;
68
          }
metzlerd's avatar
metzlerd committed
69
        }
70
      }
metzlerd's avatar
metzlerd committed
71

72
73
      if (isset($conf['access callback']) && $access) $cache['access'][$provider_key][$conf['access callback']]=$access;

metzlerd's avatar
metzlerd committed
74

metzlerd's avatar
metzlerd committed
75
76
    }

metzlerd's avatar
metzlerd committed
77
78


79
  }
metzlerd's avatar
metzlerd committed
80
  return $cache;
81
}
metzlerd's avatar
metzlerd committed
82

83
84
85
/**
 * Object factory for forena report
 * When called without a report name, it returns the last created report.
metzlerd's avatar
metzlerd committed
86
87
 * This static caching mechanism is used for form functions that are called
 * within a page load.
88
89
90
 *
 * @param unknown_type $report_name
 */
metzlerd's avatar
metzlerd committed
91
92
93
function forena_report_object($report='', $data='') {
  static $object = '';
  if ($report) {
94
95
    $object = new FrxReport($report, $data);
  }
metzlerd's avatar
metzlerd committed
96
  return $object;
97
98
}

99
100
101
102
103
104
105
/**
 * Enter description here...
 *
 * @param simplexml $xml
 * @param string $tag
 * @return string
 */
metzlerd's avatar
metzlerd committed
106
function forena_inner_xml($xml, $tag) {
107
108
  if (is_object($xml) && $tag) {
    $xml_data = $xml->asXML();
metzlerd's avatar
metzlerd committed
109
    $xml_data = preg_replace("/<\/?" . $tag . "(.|\s)*?>/", "", $xml_data);
110
  };
metzlerd's avatar
metzlerd committed
111
  return $xml_data;
112
113
}

114
115
/**
 * Accepts the name of the html tag, and the string the tag is in.
metzlerd's avatar
metzlerd committed
116
117
118
 *
 * Returns the string within the html tag name
 *
metzlerd's avatar
metzlerd committed
119
 */
120
function forena_get_html($tag, $r_text) {
121
122
123
124
  $open = strpos($r_text, $tag);
  $close = strpos($r_text, '>', $open);
  $next = strpos($r_text, '<', $close + 1);
  $str = substr($r_text, $close + 1, $next - ($close + 1));
metzlerd's avatar
metzlerd committed
125

126
127
128
  return $str;
}

metzlerd's avatar
metzlerd committed
129
/**
130
 * Form to edit parameters
metzlerd's avatar
metzlerd committed
131
 * Extra features:
metzlerd's avatar
metzlerd committed
132
 * In the parameters section of the report are new attributes
metzlerd's avatar
metzlerd committed
133
 * frx:parm:
metzlerd's avatar
metzlerd committed
134
135
136
137
138
 * @data_source = data block for the parameter to values from
 * @data_field = specific field of the data block to recieve values from.
 * if no field is specified then the first column is arbitrarily chosen.
 * @type = The form element that will display the values: select, radios are supported
 * default is textfield.
metzlerd's avatar
metzlerd committed
139
 *
metzlerd's avatar
metzlerd committed
140
141
 * This function will evaluate each parameter, determine its type, data_source, and data_field
 * and display those values appropriately.
metzlerd's avatar
metzlerd committed
142
 *
143
 */
144

145
146
147
function forena_get_user_reports($category = array()) {
  if (!$category) $category = array();
  $reports = Frx::File()->reportsByCategory((array)$category);
metzlerd's avatar
metzlerd committed
148
  return $reports;
metzlerd's avatar
metzlerd committed
149
150
151
152
153
154
}

/**
 * Render the my reports category block
 *
 */
metzlerd's avatar
metzlerd committed
155
function forena_my_reports_block() {
metzlerd's avatar
metzlerd committed
156
  $reports = forena_get_user_reports();
metzlerd's avatar
metzlerd committed
157
158
159
  if (!$reports) return '';
  $output = '<ul>';
  foreach ($reports as $category => $reports) {
metzlerd's avatar
metzlerd committed
160
    $output .= '<li>' . l($category, 'forena', array('fragment' => urlencode($category))) . '</li>';
metzlerd's avatar
metzlerd committed
161
162
  }
  $output .= '</ul>';
metzlerd's avatar
metzlerd committed
163
  return $output;
164
}
165
166
167
168
169
170

/**
 * Email confirmation form.   Confirms an email send based on mail merge
 * @param array $docs An array of SimpleXML email documents to send
 * @param integer $count Number of documents to send.
 */
171
function forena_confirm_email($formid, &$form_state, $docs, $count, $prompt_subject, $prompt_body) {
David Metzler's avatar
David Metzler committed
172
173
174
175
  // Make sure user has permission for email merge.
  if (!user_access('perform email merge')) {
    drupal_access_denied();
    exit();
metzlerd's avatar
metzlerd committed
176
  }
177
178
179
180
181
182
183
184
185
186
187
188
189
  // Save arguments away for rebuild
  if (!isset($form_state['storage']['args'])) {
    $form_state['storage']['args'] = array(
      'docs' => $docs,
      'count' => $count,
      'prompt_subject' => $prompt_subject,
      'prompt_body' => $prompt_body,
    );
  }
  else {
    // Retrieve arguements for rebuild case
    extract($form_state['storage']['args']);
  }
metzlerd's avatar
metzlerd committed
190
  if ($docs) {
metzlerd's avatar
metzlerd committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    $values = @$form_state['values'];
    if ($prompt_subject) {
      $form['subject'] = array(
        '#type' => 'textfield',
        '#title' => t('Subject'),
        '#default_value' => @$values['subject'],

      );
    }

    if ($prompt_body) {
      $form['body'] = array(
        '#type' => 'text_format',
        '#title' => t('Message'),
        '#default_value' => @$values['body'],
206
207
        '#format' => variable_get('forena_email_input_format', filter_default_format())

metzlerd's avatar
metzlerd committed
208
209
      );
    }
210
211


metzlerd's avatar
metzlerd committed
212
213
214
    if (!variable_get('forena_email_override', FALSE)) {
      $form['send'] = array(
        '#type' => 'radios',
215
        '#title' => t('Send Email'),
metzlerd's avatar
metzlerd committed
216
217
        '#options' => array('send' => 'email to users',
          'test' => 'emails to me (test mode)'),
metzlerd's avatar
metzlerd committed
218
        '#default_value' => 'test',
metzlerd's avatar
metzlerd committed
219
220
      );
    }
221
222
223
    $form['max'] = array(
      '#type' => 'textfield',
      '#title' => 'Only send first',
metzlerd's avatar
metzlerd committed
224
      '#description' => 'In test mode only, limits the number of messages to send',
metzlerd's avatar
metzlerd committed
225
      '#default_value' => 1,
226
227
      '#size' => 3,
    );
228

metzlerd's avatar
metzlerd committed
229
230
231
232
    $form_state['storage']['docs'] = $docs;
    $form_state['storage']['count'] = $count;
  }
  return confirm_form($form, t('Send mail to users'), 'forena', t('Send email to %count users?', array('%count' => $count)));
233
234
235
}

function forena_confirm_email_submit($form, &$form_state) {
metzlerd's avatar
metzlerd committed
236
  global $user;
237
  $test_send = @$form_state['values']['send']=='test' ? TRUE : variable_get('forena_email_override', FALSE);
238
239
  $max = (integer)$form_state['values']['max'];
  $i = 0;
David Metzler's avatar
David Metzler committed
240
  $sent = 0;
241
  if (isset($form_state['values']['body']['value'])) {
242
    $body =  check_markup($form_state['values']['body']['value'],$form_state['values']['body']['format']);
243
  }
metzlerd's avatar
metzlerd committed
244
  foreach ($form_state['storage']['docs'] as $doc) {
245
246
    $to = $test_send ? $user->mail : $doc['to'];
    $from = $doc['from'];
247
    // Replace body
248
    if (isset($form_state['values']['body'])) {
metzlerd's avatar
metzlerd committed
249
      $doc['parms']['body'] = $body;
250
    }
251
    // Replace subject
252
    if (isset($form_state['values']['subject'])) {
metzlerd's avatar
metzlerd committed
253
      $doc['parms']['subject'] = $form_state['values']['subject'];
254
    }
255
256
257
258
259
    if ($test_send) {
      $i++;
      // Remove bcc and cc
      unset($doc['parms']['headers']);
    }
David Metzler's avatar
David Metzler committed
260
261
262
263
264
265
266
267
268
269
270
    if ($i <= $max) {
      $sent++;
      drupal_mail('forena', 'mailmerge', $to, language_default(), $doc['parms'], $from, TRUE);
    }
  }
  drupal_set_message(t('Sent %count messages ', array('%count' => $sent)));
  if ($form_state['values']['send'] == 'test') {
    $form_state['rebuild'] = TRUE;
  }
  else {
    $form_state['redirect'] = 'forena';
271
272
  }
}
273
274
275
276
277
278
279

/**
 * Send report block XML
 * Enter description here ...
 * @param unknown_type $block_name
 */
function forena_block_xml($block_name='') {
metzlerd's avatar
metzlerd committed
280
281
282
283
284
285
286
287
  $block_name = str_replace('.', '/', $block_name);
  $parms = $_GET;
  unset($parms['q']);
  $xml = Frx::RepoMan()->data($block_name, $parms);
  if (is_object($xml)) {
    header('Content-Type: text/xml');
    print $xml->asXML();
  }
288
}
289
290
291
292
293
294
295
296
297
298
299
300
301
302

/**
 * Save the report file to disk
 *
 * @param string $name File name  to save report to
 * @param unknown_type $data
 */
function forena_save_report($report_name, $report, $save_file = FALSE, $altered = 0) {
  static $save_count=0;
  if ($report && !is_object($report)) {
    try {
      $report = new SimpleXMLElement($report);
    }
    catch (Exception $e) {
303
      Frx::error(t('Could not save %s because XML was malformed', htmlspecialchars($report_name)),
304
305
306
307
308
        "<p>Invalid XML</p><pre>XML:" . htmlspecialchars($report) . "\n" . $e->getMessage() . "</pre>");
      return;
    }
  }

309
  if (!_forena_verify_directory($report_name) && $save_file) {
310
311
312
313
314
315
316
317
318
319
320
321
322
    drupal_set_message(t('Error creating directory.  Check Permissions'), 'error');
    return 0;
  };
  $report_path = forena_report_path();
  $r = new FrxReport($report);
  $data['title'] = $r->title;
  $data['category'] = $r->category;
  $data['options'] = $r->options;
  $data['descriptor'] = $r->descriptor;
  $data['name'] = $report_name;
  $data['altered'] = $altered;
  $r_xml = $report->asXML();
  $name = $data['name'];
323
  $filepath = $report_name . '.frx';
324
325
326
  // If we need to save this to the file system

  if ($save_file) {
327
    Frx::File()->save($filepath, $r_xml);
328
329
330
331
332
333
  }

  // Get the security caches from the reports
  if ($report) $cache = forena_load_cache($report); else $cache='';
  $rpt_cache='';
  if ($cache) $rpt_cache = serialize($cache);
metzlerd's avatar
metzlerd committed
334

335
336
337
338
339
340
341
342
343
344
345
  // Set default interpretations of data
  $data['enabled'] = (isset($data['enabled']) && $data['enabled']) ? 1 : 0;
  if (isset($data['options']['hidden']) && (string)$data['options']['hidden']) {
    $data['hidden'] =  ($data['options']['hidden'] && $data['options']['hidden']!='N' && $data['options']['hidden']!='0') ? 1:0;
    if (!$data['category']) $data['category'] = 'All';
  }
  else {
    // Set hidden based on category
    $data['hidden'] = ($data['category'])? 0 : 1;
  }

346
  $save_count++;
347
348
349
350
351
352
353
354
355
356
  $r = NULL;
  $result = NULL;
  $r_xml = NULL;
  $report = NULL;
  $data = NULL;
  $rpt = NULL;
  $cache = NULL;
  $rpt_cache = NULL;
  // Destroy object to clear memory leaks.
  if ($r) {
metzlerd's avatar
metzlerd committed
357
358
    $r->__destruct();
    unset($r);
359
360
361
362
  }

  return $save_count;
}
363

364
365
366
367
368
369
370
371
372
/**
 * Render a field without it's containers so that we can use it in a report without the wrappers.
 */
function theme_forena_inline_field(&$variables) {
  $element = $variables['field'];
  // This is also used in the installer, pre-database setup.
  drupal_render_children($variables['field']);
}

373
374
375
376
/**
 * Render a special form with an embedded forena report.
 * @param unknown_type $variables
 */
377
function theme_forena_inline_form($variables) {
378
379
380
  $form = $variables['form'];
  $build = '';
  $output = '';
381
  $fields = array();
382
  $template = @$form['#forena_form_template'];
383
384

  // Convert interior elements into inline fields
385
386
387
388
  if ($template) {
    _forena_set_inline_theme($form, $template);
    // Render the inline fields and store them into the array.
    $build = _forena_render_inline_elements($form, $fields, $template);
metzlerd's avatar
metzlerd committed
389
    $build .= $fields['form_build_id'] . $fields['form_token'] . $fields['form_id'];
390
391
  }
  else {
metzlerd's avatar
metzlerd committed
392
393
394
    $build = $form['#children'];
  }

395
  // Return the form
396
397
398
  return $build;
}

399
400
function _forena_render_form_template($fields, $template) {
  static $teng = '';
metzlerd's avatar
metzlerd committed
401
  if (!$teng) $teng = new FrxSyntaxEngine(FRX_TOKEN_EXP, '{}');
metzlerd's avatar
metzlerd committed
402
  $o = Frx::Data()->push($fields, 'form-fields');
403
404
405
406
  $build = $teng->replace($template, TRUE);
  $o = Frx::Data()->pop();

  return $build;
407
408
}

409

410
function _forena_set_inline_theme(&$elements) {
metzlerd's avatar
metzlerd committed
411
  foreach ($elements as $key => $value) if (strpos($key, '#')===FALSE) {
412
413
414
415
416
    $type = @$value['#type'];
    // Set theme functions for specific types to be inline forms.
    switch ($type) {
      case 'fieldset':
      case NULL:
417
418
      case 'submit':
      case '';
metzlerd's avatar
metzlerd committed
419
      break;
420
421
422
423
424
425
426
      default:
        $elements[$key]['#theme_wrappers'] = array('forena_inline_form_element');
    }

  }
}

427
428
429
430
/**
 * Renders forena reprot as
 * @param unknown_type $variables
 */
431
function theme_forena_inline_form_element($variables) {
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  $element = &$variables['element'];
  // This is also used in the installer, pre-database setup.
  $t = get_t();

  // This function is invoked as theme wrapper, but the rendered form element
  // may not necessarily have been processed by form_builder().
  $element += array(
    '#title_display' => 'before',
  );

  // Add element #id for #type 'item'.
  if (isset($element['#markup']) && !empty($element['#id'])) {
    $attributes['id'] = $element['#id'];
  }
  // Add element's #type and #name as class to aid with JS/CSS selectors.
  $attributes['class'] = array('form-item');
  if (!empty($element['#type'])) {
    $attributes['class'][] = 'form-type-' . strtr($element['#type'], '_', '-');
  }
  if (!empty($element['#name'])) {
    $attributes['class'][] = 'form-item-' . strtr($element['#name'], array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
  }
  // Add a class for disabled elements to facilitate cross-browser styling.
  if (!empty($element['#attributes']['disabled'])) {
    $attributes['class'][] = 'form-disabled';
  }
  $output = '';
  $output = '<span' . drupal_attributes($attributes) . '>' . "\n";
  //drupal_set_message('<pre>'. print_r($element,1) . '</pre>');

  $element['#title_display'] = 'none';

  $prefix = isset($element['#field_prefix']) ? '<span class="field-prefix">' . $element['#field_prefix'] . '</span> ' : '';
  $suffix = isset($element['#field_suffix']) ? ' <span class="field-suffix">' . $element['#field_suffix'] . '</span>' : '';

  switch ($element['#title_display']) {
    case 'before':
    case 'invisible':
      $output .= ' ' . theme('form_element_label', $variables);
      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;

    case 'after':
      $output .= ' ' . $prefix . $element['#children'] . $suffix;
      $output .= ' ' . theme('form_element_label', $variables) . "\n";
      break;

    case 'none':
    case 'attribute':
      // Output no label and no required marker, only the children.
      $output .= ' ' . $prefix . $element['#children'] . $suffix . "\n";
      break;
  }

  // No descriptions or titles.
  /*
  if (!empty($element['#description'])) {
metzlerd's avatar
metzlerd committed
489
  $output .= '<div class="description">' . $element['#description'] . "</div>\n";
490
491
  }
  */
492
  $output .= "</span>\n";
493

494
495
496
497
498
499
500
501
502
503
504
  return $output;
}

function theme_forena_fieldset_template(&$variables) {

  $element = $variables['fieldset'];
  element_set_attributes($element, array('id'));
  _form_set_class($element, array('form-wrapper'));
  $output = '';

  $fields = array();
metzlerd's avatar
metzlerd committed
505
  foreach ($element as $key => $value ) if (strpos($key, '#') ===FALSE) {
506
507
508
509
510
511
512
    $fields[$key] = drupal_render($value);
  }

  $output .= _forena_render_form_template($fields, $element['#forena-template']);
  if (isset($element['#value'])) {
    $output .= $element['#value'];
  }
513
514
515
516
517
518


  return $output;
}


519
520
521
522
523
524
525
526
function theme_forena_data_table($variables) {
  $defaults = array(
      'caption' => '',
      'sticky' => FALSE,
      'colgroups' => array(),
      'empty' => '',
  );
  drupal_add_css(drupal_get_path('module', 'forena') . '/forena.css');
527
  if (forena_library_file('dataTables')) {
528
    drupal_add_js(drupal_get_path('module', 'forena') . '/forena.admin.js');
529
    drupal_add_js(forena_library_file('dataTables'));
530
531
532
533
534
535
536
537
538
539
  }
  // Default in values that theme_table expects.
  $variables['attributes'] = isset($variables['attributes']) ? array_merge($defaults, $variables['attributes']) : $defaults;
  $variables['attributes']['class'][] = 'dataTable-paged';
  $output = theme('table', $variables);
  return $output;
}



540