Parent Directory
|
Revision Log
|
Revision Graph
|
Patch
| revision 1.1, Thu Jun 8 06:34:55 2006 UTC | revision 1.2, Sat Aug 26 00:06:30 2006 UTC | |
|---|---|---|
| # | Line 12 | Line 12 |
| 12 | * | * |
| 13 | * | * |
| 14 | * @author Rob Thorne (rob AT torenware.com) | * @author Rob Thorne (rob AT torenware.com) |
| 15 | * @copyright Bioneers 2006 | * @copyright © 2006 by Collective Heritage Institute |
| 16 | * @license GPL v. 2.0 | * @license GPL v. 2.0 |
| 17 | * @version 4.7.0 - development | * @version 4.7.0 - development |
| 18 | * @see | * @see |
| # | Line 21 | Line 21 |
| 21 | */ | */ |
| 22 | ||
| 23 | ||
| 24 | //includes here. | //includes here. |
| 25 | $mod_path = drupal_get_path('module', 'query_export'); | $mod_path = drupal_get_path('module', 'query_export'); |
| 26 | require_once $mod_path . "/metadata.inc"; | require_once $mod_path . "/metadata.inc"; |
| 27 | require_once $mod_path . "/views.inc"; | |
| 28 | require_once $mod_path . "/civicrm.inc"; | |
| 29 | require_once $mod_path . "/formatters.inc"; | |
| 30 | if (module_exist('civinode')) { | if (module_exist('civinode')) { |
| 31 | $civinode_path = drupal_get_path('module', 'civinode'); | $civinode_path = drupal_get_path('module', 'civinode'); |
| 32 | require_once $civinode_path . "/civinode_utils.inc"; | require_once $civinode_path . "/civinode_utils.inc"; |
| # | Line 34 define('QUERY_EXPORT_ERROR_BASE', 8300); | Line 37 define('QUERY_EXPORT_ERROR_BASE', 8300); |
| 37 | define('QUERY_EXPORT_ERROR_NO_AUTH', QUERY_EXPORT_ERROR_BASE + 1); | define('QUERY_EXPORT_ERROR_NO_AUTH', QUERY_EXPORT_ERROR_BASE + 1); |
| 38 | define('QUERY_EXPORT_ERROR_NO_SUCH_QUERY', QUERY_EXPORT_ERROR_BASE + 2); | define('QUERY_EXPORT_ERROR_NO_SUCH_QUERY', QUERY_EXPORT_ERROR_BASE + 2); |
| 39 | define('QUERY_EXPORT_ERROR_SERVER_IS_HORKED', QUERY_EXPORT_ERROR_BASE + 3); | define('QUERY_EXPORT_ERROR_SERVER_IS_HORKED', QUERY_EXPORT_ERROR_BASE + 3); |
| 40 | define('QUERY_EXPORT_ERROR_UNAUTHORIZED', QUERY_EXPORT_ERROR_BASE + 4); | |
| 41 | ||
| 42 | ||
| 43 | //Some Jserv defaults | |
| 44 | define('QUERY_EXPORT_DEFAULT_JSERV', 'http://localhost:8081/jasperService'); | |
| 45 | ||
| 46 | /** | /** |
| 47 | * Implementation of hook_help | * Implementation of hook_help |
| 48 | * | * |
| # | Line 43 define('QUERY_EXPORT_ERROR_SERVER_IS_HOR | Line 50 define('QUERY_EXPORT_ERROR_SERVER_IS_HOR |
| 50 | ||
| 51 | function query_export_help($section) { | function query_export_help($section) { |
| 52 | switch ($section) { | switch ($section) { |
| 53 | case 'admin/help#query_export': | case 'admin/help#query_export': |
| 54 | $output = '<p>'. t('The query_export module is used to create query definitions you want to expose to XML-RPC, for report generation and other purposes. ') .'</p>'; | $output = '<p>'. t('The query_export module is used to create query definitions you want to expose to XML-RPC, for report generation and other purposes. ') .'</p>'; |
| 55 | $output .= '<p>'. t('The query_export administration interface allows for complex configuration.') .'</p>'; | $output .= '<p>'. t('The query_export administration interface allows for complex configuration.') .'</p>'; |
| 56 | $output .= t('<p>You can</p> | $output .= t('<p>You can</p> |
| 57 | <ul> | <ul> |
| 58 | <li><em>Items Listed Here...</em></li></ul> | <li><em>Items Listed Here...</em></li></ul> |
| 59 | ', array('%node-add-query_export' => url('node/add/query_export'), '%admin-settings-content-types-query_export' => url('admin/settings/content-types/query_export'))); | ', array('%node-add-query_export' => url('node/add/query_export'), '%admin-settings-content-types-query_export' => url('admin/settings/content-types/query_export'))); |
| 60 | ||
| 61 | return $output; | return $output; |
| 62 | case 'admin/modules#description': | case 'admin/modules#description': |
| 63 | return t('Allows users to define node queries for export via XML-RPC.'); | return t('Allows users to define node queries for export via XML-RPC.'); |
| 64 | case 'node/add#query_export': | case 'node/add#query_export': |
| 65 | return t('Query Exports are definitions of queries you want to use in reporting applications like JasperReports.'); | return t('Query Exports are definitions of queries you want to use in reporting applications like JasperReports.'); |
| 66 | } | } |
| 67 | } | } |
| 68 | ||
| # | Line 63 function query_export_help($section) { | Line 70 function query_export_help($section) { |
| 70 | * Implementation of hook_node_info(). | * Implementation of hook_node_info(). |
| 71 | */ | */ |
| 72 | function query_export_node_info() { | function query_export_node_info() { |
| 73 | return array('query_export' => array('name' => t('query_export'), 'base' => 'query_export')); | return array('query_export' => array('name' => t('Jasper Report Definition'), 'base' => 'query_export')); |
| 74 | } | } |
| 75 | ||
| 76 | /** | /** |
| 77 | * Implementation of hook_perm(). | * Implementation of hook_perm(). |
| 78 | * | |
| 79 | * Custom tables are a quick hack that will likely go away in | |
| 80 | * official versions (June 2006 RMT). | |
| 81 | */ | */ |
| 82 | function query_export_perm() { | function query_export_perm() { |
| 83 | return array('create query exports', 'administer query exports', 'access query exports via XML-RPC'); | return array('create query exports', 'administer query exports', 'access query exports via XML-RPC', 'access custom tables'); |
| 84 | } | } |
| 85 | ||
| 86 | /** | /** |
| # | Line 80 function query_export_access($op, $node) | Line 90 function query_export_access($op, $node) |
| 90 | //TO DO: this piece requires thought. | //TO DO: this piece requires thought. |
| 91 | global $user; | global $user; |
| 92 | ||
| 93 | if ($op == 'create') { | if ($op == 'create' or $op == 'update' or $op == 'delete') { |
| 94 | return user_access('create query exports'); | return user_access('create query exports'); |
| 95 | } | } |
| 96 | /** | /** |
| 97 | if ($op == 'update' || $op == 'delete') { | if ($op == 'update' || $op == 'delete') { |
| 98 | if (user_access('edit own stories') && ($user->uid == $node->uid)) { | if (user_access('edit own stories') && ($user->uid == $node->uid)) { |
| 99 | return TRUE; | return TRUE; |
| 100 | } | } |
| 101 | } | } |
| 102 | **/ | **/ |
| 103 | } | } |
| 104 | ||
| # | Line 102 function query_export_menu($may_cache) { | Line 112 function query_export_menu($may_cache) { |
| 112 | if ($may_cache) { | if ($may_cache) { |
| 113 | //Admin screens, query creation screens | //Admin screens, query creation screens |
| 114 | $items[] = array('path' => 'query_export/test_rpc', | $items[] = array('path' => 'query_export/test_rpc', |
| 115 | 'title' => t('Test RPC Routines'), | 'title' => t('Test RPC Routines'), |
| 116 | 'callback' => 'query_export_test_rpc', | 'callback' => 'query_export_test_rpc', |
| 117 | 'type' => MENU_CALLBACK, | 'type' => MENU_CALLBACK, |
| 118 | 'access' => 1 | 'access' => user_access('create query exports') |
| 119 | ); | ); |
| 120 | $items[] = array('path' => 'query_export/field_editor', | |
| 121 | 'title' => t('Set Up Field Handlers'), | |
| 122 | 'callback' => 'query_export_edit_field_info_form', | |
| 123 | 'type' => MENU_CALLBACK, | |
| 124 | 'access' => user_access('create query exports') | |
| 125 | ); | |
| 126 | ||
| 127 | $items[] = array('path' => 'query_export/param_editor', | |
| 128 | 'title' => t('Set Up Default Parameters'), | |
| 129 | 'callback' => 'query_export_edit_default_params_form', | |
| 130 | 'type' => MENU_CALLBACK, | |
| 131 | 'access' => user_access('create query exports') | |
| 132 | ); | |
| 133 | ||
| 134 | $items[] = array('path' => 'query_export/render', | |
| 135 | 'title' => t('Produce A Report In Jasper Reports'), | |
| 136 | 'callback' => 'query_export_render_form', | |
| 137 | 'type' => MENU_CALLBACK, | |
| 138 | 'access' => user_access('access query exports via XML-RPC') | |
| 139 | ); | |
| 140 | } | } |
| 141 | ||
| 142 | return $items; | return $items; |
| # | Line 114 function query_export_menu($may_cache) { | Line 144 function query_export_menu($may_cache) { |
| 144 | ||
| 145 | ||
| 146 | /** | /** |
| 147 | * Menu handler for rendering reports | |
| 148 | * | |
| 149 | */ | |
| 150 | function query_export_render_form(){ | |
| 151 | $node_arg = arg(2) and is_numeric(arg(2)) ? arg(2) : FALSE; | |
| 152 | $output = ''; | |
| 153 | if (!$node_arg) { | |
| 154 | $output .= "This page should have a listing of possible rep defs"; | |
| 155 | } | |
| 156 | else { | |
| 157 | $node = node_load($node_arg); | |
| 158 | if (!$node or $node->type != 'query_export') | |
| 159 | drupal_not_found(); | |
| 160 | //$output .= "This should be a form to render Node $node_arg, if it exists"; | |
| 161 | ||
| 162 | //If we are processing, set up the call to the proxy. | |
| 163 | if (isset($_POST['edit'])) { | |
| 164 | $edit = $_POST['edit']; | |
| 165 | $params = array(); | |
| 166 | if (isset($edit['output_type'])) { | |
| 167 | if ($edit['output_type'] == 'csv') { | |
| 168 | return query_export_render_csv_file($node, | |
| 169 | $edit['query_string'] | |
| 170 | ); | |
| 171 | } | |
| 172 | else | |
| 173 | $params['format'] = urlencode($edit['output_type']); | |
| 174 | } | |
| 175 | if (isset($edit['query_string'])) | |
| 176 | $params['qstring'] = urlencode($edit['query_string']); | |
| 177 | error_log('About to launch our proxy...'); | |
| 178 | return query_export_render_report($node_arg, $params); | |
| 179 | ||
| 180 | } | |
| 181 | $form = array(); | |
| 182 | $form['dummy_label'] = | |
| 183 | array('#type' => 'item', | |
| 184 | '#title' => t('Definition Name'), | |
| 185 | '#value' => $node->title); | |
| 186 | $form['dummy_dsource'] = | |
| 187 | array('#type' => 'item', | |
| 188 | '#title' => t('Data Source'), | |
| 189 | '#value' => query_export_query_ui_name($node->query_name)); | |
| 190 | $form['dummy_dstype'] = | |
| 191 | array('#type' => 'item', | |
| 192 | '#title' => t('Source Type'), | |
| 193 | '#value' => $node->query_type); | |
| 194 | if (module_exist('civinode') and $node->query_type == 'civicrm') { | |
| 195 | global $user; | |
| 196 | $pid = $node->query_profile ? | |
| 197 | $node->query_profile : | |
| 198 | civinode_get_default_profile_id($user->uid); | |
| 199 | $prof_title = civinode_get_profile_title($pid); | |
| 200 | $form['dummy_prof_id'] = | |
| 201 | array('#type' => 'item', | |
| 202 | '#title' => t('Contact Fields From Profile'), | |
| 203 | '#value' => $prof_title); | |
| 204 | ||
| 205 | } | |
| 206 | ||
| 207 | $form['output_type'] = | |
| 208 | array('#type' => 'select', | |
| 209 | '#title' => t('Type Of Output'), | |
| 210 | '#description' => t('Jasper Reports can generate multiple kinds of output. Choose the desired format for this report'), | |
| 211 | '#default_value' => 'csv', | |
| 212 | '#options' => | |
| 213 | array('pdf' => t("PDF File"), | |
| 214 | 'xls' => t('MS Excel Spreadsheet'), | |
| 215 | 'html' => t('HTML Web Page'), | |
| 216 | 'rtf' => t('Rich Text (RTF)'), | |
| 217 | 'txt' => t('Plain Text'), | |
| 218 | 'csv' => t('CSV File') | |
| 219 | ) | |
| 220 | ); | |
| 221 | $form['query_string'] = | |
| 222 | array('#type' => 'textfield', | |
| 223 | '#title' => t('Query String'), | |
| 224 | '#description' => t('For some report types, you can pass an optional query string that will further restrict what rows you will get in your report You should ask your system admin how to use this, since this is an "Advanced Feature". YMMV :-)'), | |
| 225 | '#size' => 30, | |
| 226 | '#maxlength' => 60 | |
| 227 | ); | |
| 228 | ||
| 229 | $form['submit'] = | |
| 230 | array('#type' => 'submit', | |
| 231 | '#value' => t('Run Report') | |
| 232 | ); | |
| 233 | $output .= drupal_get_form('request_jasper_form', $form); | |
| 234 | } | |
| 235 | ||
| 236 | return $output; | |
| 237 | } | |
| 238 | ||
| 239 | ||
| 240 | /** | |
| 241 | * Bridging functions for the Java servlet | |
| 242 | * | |
| 243 | */ | |
| 244 | ||
| 245 | ||
| 246 | /** | |
| 247 | * Ask the servlet to compile and reload a report definition | |
| 248 | * | |
| 249 | * @param int $nid node id of the report to load or reload. | |
| 250 | * @return TRUE on success | |
| 251 | */ | |
| 252 | ||
| 253 | function query_export_request_compile($nid){ | |
| 254 | $base = variable_get('query_export_jserv_url', QUERY_EXPORT_DEFAULT_JSERV); | |
| 255 | if ($base) { | |
| 256 | //$len = strlen($base); | |
| 257 | //build the query string | |
| 258 | $qs = "?compile=$nid"; | |
| 259 | $curl = curl_init(); | |
| 260 | $url = $base . $qs; | |
| 261 | curl_setopt($curl, CURLOPT_URL, $url); | |
| 262 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); | |
| 263 | $rslt = curl_exec($curl); | |
| 264 | //For now, just log this | |
| 265 | error_log("Polled $url and got: " . $rslt); | |
| 266 | //Need to test result, but for now... | |
| 267 | $match = array(); | |
| 268 | if (preg_match('/^Status\:\s+(\S+)/', $rslt, $match)) { | |
| 269 | error_log("Got code: " . $match[1]); | |
| 270 | return TRUE; | |
| 271 | } | |
| 272 | ||
| 273 | } | |
| 274 | return FALSE; | |
| 275 | } | |
| 276 | ||
| 277 | ||
| 278 | /** | |
| 279 | * Process a report rendering via the servlet | |
| 280 | * | |
| 281 | */ | |
| 282 | ||
| 283 | function query_export_render_report($nid, $params = array()){ | |
| 284 | //build request for proxy. | |
| 285 | $base = variable_get('query_export_jserv_url', QUERY_EXPORT_DEFAULT_JSERV); | |
| 286 | //Check for params or set defaults | |
| 287 | //?report=nid&cookie=xxxx&qstring=xxxxxx&format=pdf | |
| 288 | //TO DO: this should really be using URL encoding functions | |
| 289 | $cookie = query_export_generate_cookie($nid); | |
| 290 | if (isset($params['format'])) | |
| 291 | $format = $params['format']; | |
| 292 | else | |
| 293 | $format = 'pdf'; | |
| 294 | $qs = "?report=$nid&format=$format&cookie=$cookie"; | |
| 295 | if (isset($params['qstring'])) | |
| 296 | $qs .= "&qstring=" . $params['qstring']; | |
| 297 | if ($base) { | |
| 298 | //$len = strlen($base); | |
| 299 | //build the query string | |
| 300 | $node = node_load($nid); | |
| 301 | $file_name = query_export_query_ui_name($node->query_name); | |
| 302 | //Get an extension and MIME type | |
| 303 | switch ($format) { | |
| 304 | case 'pdf': | |
| 305 | $extension = "pdf"; | |
| 306 | $mime = 'application/pdf'; | |
| 307 | break; | |
| 308 | case 'xls': | |
| 309 | $extension = "xls"; | |
| 310 | $mime = 'application/vnd.ms-excel'; | |
| 311 | break; | |
| 312 | case 'html': | |
| 313 | $extension = "html"; | |
| 314 | $mime = 'text/html'; | |
| 315 | break; | |
| 316 | case 'txt': | |
| 317 | $extension = "txt"; | |
| 318 | $mime = 'text/plain'; | |
| 319 | break; | |
| 320 | case 'doc': | |
| 321 | $extension = "doc"; | |
| 322 | $mime = 'application/msword'; | |
| 323 | break; | |
| 324 | case 'rtf': | |
| 325 | $extension = "rtf"; | |
| 326 | $mime = 'application/rtf'; | |
| 327 | break; | |
| 328 | default: | |
| 329 | $extension = "bin"; | |
| 330 | $mime = 'application/octet-stream'; | |
| 331 | break; | |
| 332 | } | |
| 333 | $curl = curl_init(); | |
| 334 | $url = $base . $qs; | |
| 335 | curl_setopt($curl, CURLOPT_URL, $url); | |
| 336 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, FALSE); | |
| 337 | curl_setopt($curl, CURLOPT_BINARYTRANSFER, TRUE); | |
| 338 | curl_setopt($curl, CURLOPT_HEADER, FALSE); //write our own | |
| 339 | //curl_setopt($curl, CURLOPT_FAILONERROR, 1); | |
| 340 | //We need to proxy over our headers | |
| 341 | //curl_setopt($curl, CURLOPT_HEADERFUNCTION, | |
| 342 | // 'query_export_proxy_header_callback'); | |
| 343 | curl_setopt($curl, CURLOPT_WRITEFUNCTION, | |
| 344 | 'query_export_proxy_data_callback'); | |
| 345 | $date = date('r'); | |
| 346 | error_log("Start transfer at $date"); | |
| 347 | header( "Content-type: $mime" ); | |
| 348 | $file_name .= "." . $extension; | |
| 349 | $file_name = strtr($file_name, array(' ' => "_")); | |
| 350 | header( "Content-disposition: attachment; filename=$file_name" ); | |
| 351 | ||
| 352 | curl_exec($curl); | |
| 353 | $errno = curl_errno($curl); | |
| 354 | if ($errno) | |
| 355 | error_log(curl_error($curl)); | |
| 356 | error_log("Called curl_exec with $errno for " . $url); | |
| 357 | curl_close($curl); | |
| 358 | exit(0); | |
| 359 | } | |
| 360 | } | |
| 361 | ||
| 362 | ||
| 363 | /** | |
| 364 | * Proxy http headers | |
| 365 | * | |
| 366 | * Curl callback to proxy over the HTTP headers | |
| 367 | */ | |
| 368 | function query_export_proxy_header_callback($curl_obj, $header){ | |
| 369 | error_log($header); | |
| 370 | if (preg_match('/^HTTP\:/', $header) or | |
| 371 | preg_match('/^Content/', $header)) { | |
| 372 | ||
| 373 | header($header); | |
| 374 | return strlen($header); | |
| 375 | } | |
| 376 | return 0; | |
| 377 | } | |
| 378 | ||
| 379 | ||
| 380 | function query_export_proxy_data_callback($curl_obj, $data){ | |
| 381 | $size = strlen($data); | |
| 382 | print $data; | |
| 383 | #error_log("Wrote $size bytes"); | |
| 384 | return $size; | |
| 385 | } | |
| 386 | ||
| 387 | ||
| 388 | ||
| 389 | ||
| 390 | ||
| 391 | /** | |
| 392 | * Implementation of hook_settings | |
| 393 | * | |
| 394 | */ | |
| 395 | function query_export_settings(){ | |
| 396 | $form['query_export_jserv_url'] = | |
| 397 | array('#type' => 'textfield', | |
| 398 | '#default_value' => variable_get('query_export_jserv_url', | |
| 399 | QUERY_EXPORT_DEFAULT_JSERV), | |
| 400 | '#title' => t('URL of your Drupal/Jasper bridge'), | |
| 401 | '#description' => t('If you want to run Jasper Reports queries, you need ' . | |
| 402 | 'to supply a URL of your Drupal bridge servlet.'), | |
| 403 | '#size' => 35, | |
| 404 | '#maxlength' => 60); | |
| 405 | return $form; | |
| 406 | ||
| 407 | } | |
| 408 | ||
| 409 | ||
| 410 | /** | |
| 411 | * | |
| 412 | * Implementation of hook_view | |
| 413 | * | |
| 414 | * | |
| 415 | */ | |
| 416 | function query_export_view(&$node, $teaser = FALSE, $page = FALSE){ | |
| 417 | //Very simple rendering so we can see settings | |
| 418 | $items = array(); | |
| 419 | $items[] = t('<b>Data Source Name:</b> ') . query_export_query_ui_name($node->query_name); | |
| 420 | $items[] = t('<b>Type Of Data:</b> ') . $node->query_type; | |
| 421 | if ($node->query_type == 'civicrm' and $node->query_profile) { | |
| 422 | if (module_exist('civinode')) { | |
| 423 | $profile = civinode_get_profile_title($node->query_profile); | |
| 424 | } | |
| 425 | else | |
| 426 | $profile = t('<em>NA</em>'); | |
| 427 | $items[] = t('<b>Use Fields From CRM Profile:</b> ') . $profile; | |
| 428 | ||
| 429 | } | |
| 430 | $output = theme('item_list', $items, t('Report Parameters')); | |
| 431 | if (module_exist('views') and $node->nid) { | |
| 432 | $output .= t('<br /><p>You can define special handlers to modify the way some of your fields are formatted, | |
| 433 | or in the case of custom queries (queries where you write the SQL yourself) where you want to make report | |
| 434 | generators like iReport aware that you are exporting a particular field. You can add new definitions or | |
| 435 | edit existing fields here:</p>'); | |
| 436 | $num_field_defs = query_export_load_field_definitions($node->nid); | |
| 437 | if ($num_field_defs) { | |
| 438 | $view = views_get_view('query_export_field_defs'); | |
| 439 | $output .= views_build_view('embed', $view, array($node->nid), TRUE, 20); | |
| 440 | $output .= "<br />"; | |
| 441 | } | |
| 442 | $output .= l(t('Add A Special Handler'), 'query_export/field_editor/' . $node->nid); | |
| 443 | $output .= "<br /><br />\n"; | |
| 444 | ||
| 445 | $output .= t('<p>You can also define default parameters that can be passed to the report generator | |
| 446 | to define ranges for queries or to add a parameter to restrict the report to one group or contact. You may | |
| 447 | add these here:</p><br />'); | |
| 448 | //TO DO: render a view here. | |
| 449 | $all_params = query_export_fetch_default_param($node->nid); | |
| 450 | if (count($all_params)) { | |
| 451 | $view = views_get_view('query_export_param_embed'); | |
| 452 | $output .= views_build_view('embed', $view, array($node->nid), TRUE, 20); | |
| 453 | $output .= "<br />"; | |
| 454 | } | |
| 455 | ||
| 456 | $output .= l(t('Define A Default Parameter'), 'query_export/param_editor/' . $node->nid); | |
| 457 | $output .= "<br /><br />\n"; | |
| 458 | ||
| 459 | } | |
| 460 | ||
| 461 | $node->body = $output; | |
| 462 | $node->teaser = $output; | |
| 463 | } | |
| 464 | ||
| 465 | ||
| 466 | /** | |
| 467 | * Implementation of hook_link. | |
| 468 | * | |
| 469 | * Link goes to the "generate report" page for that node. | |
| 470 | * @author | |
| 471 | * @param | |
| 472 | * @return | |
| 473 | * @exeption | |
| 474 | * @see | |
| 475 | */ | |
| 476 | ||
| 477 | function query_export_link($type, $node = NULL, $teaser = false){ | |
| 478 | $links = array(); | |
| 479 | ||
| 480 | if ($type == 'node' and $node->type == 'query_export' and $node->nid) { | |
| 481 | if (user_access('access query exports via XML-RPC')) { | |
| 482 | $links[] = l(t('generate a report'), "query_export/render/" . $node->nid, | |
| 483 | array('title' => t('Generate a JasperReports report.'))); | |
| 484 | } | |
| 485 | } | |
| 486 | ||
| 487 | return $links; | |
| 488 | ||
| 489 | } | |
| 490 | ||
| 491 | ||
| 492 | ||
| 493 | /** | |
| 494 | * Implementation of hook_prepare. | |
| 495 | * | |
| 496 | * We use this to set up the uploads | |
| 497 | */ | |
| 498 | function query_export_prepare(&$node){ | |
| 499 | // Clears our files in session when you enter the edit view the first time. | |
| 500 | // This is so files don't linger on the form if you happen to leave the node | |
| 501 | // and come back into it. | |
| 502 | if(count($_POST) == 0) { | |
| 503 | unset($_SESSION['jasper_jrxml_file']); | |
| 504 | } | |
| 505 | ||
| 506 | //We need to remember if we already have an upload from | |
| 507 | //before | |
| 508 | $have_upload = $node->report_text ? TRUE : FALSE; | |
| 509 | ||
| 510 | // check for an upload | |
| 511 | if ($file = file_check_upload('jasper_jrxml_upload')) { | |
| 512 | // save the upload into drupal's temp directory | |
| 513 | $temppath = file_directory_temp() . '/jasper/'; | |
| 514 | file_check_directory($temppath, TRUE); | |
| 515 | $node->jasper_jrxml_file = file_save_upload($file, $temppath .'/'. $file->filename, | |
| 516 | FILE_EXISTS_REPLACE); | |
| 517 | $node->jasper_jrxml_file->newfile = TRUE; | |
| 518 | $_SESSION['jasper_jrxml_file'] = $node->jasper_jrxml_file; | |
| 519 | $have_upload = TRUE; | |
| 520 | } | |
| 521 | else if (!empty($_SESSION['jasper_jrxml_file'])) { | |
| 522 | $node->jasper_jrxml_file = $_SESSION['jasper_jrxml_file']; | |
| 523 | $have_upload = TRUE; | |
| 524 | } | |
| 525 | else { | |
| 526 | //May not need this, since we will not keep the file | |
| 527 | if ($node->jasper_jrxml_file) | |
| 528 | $_SESSION['jasper_jrxml_file'] = $node->jasper_jrxml_file; | |
| 529 | } | |
| 530 | ||
| 531 | //And if we have an upload, save away this info so that we | |
| 532 | //can see this later in processing | |
| 533 | //form_set_value(array('qe_have_upload' => | |
| 534 | // array('#value' => $have_upload)),) | |
| 535 | ||
| 536 | } | |
| 537 | ||
| 538 | ||
| 539 | /** | |
| 540 | * Function to aid managing uploads | |
| 541 | * | |
| 542 | * @param int $nid | |
| 543 | */ | |
| 544 | ||
| 545 | function query_export_report_rec_has_text($nid){ | |
| 546 | $sql = "SELECT length(report_text) FROM {query_export_reports} WHERE nid = %d"; | |
| 547 | return db_result(db_query($sql,$nid)); | |
| 548 | } | |
| 549 | ||
| 550 | /** | |
| 551 | * Implementation of hook_validate(). | * Implementation of hook_validate(). |
| 552 | */ | */ |
| 553 | function query_export_validate($node) { | function query_export_validate(&$node) { |
| 554 | //node_validate_title($node); | |
| 555 | //First, as far as query names go, There Can Be Only One. | |
| 556 | if ($node->query_type == 'views') { | |
| 557 | $node->crm_query = ''; | |
| 558 | if (!$node->views_query) { | |
| 559 | form_set_error('views_query', t("Please choose a View to extract data from")); | |
| 560 | } | |
| 561 | ||
| 562 | $node->query_name = $node->views_query; | |
| 563 | } | |
| 564 | else if ($node->query_type == 'civicrm') { | |
| 565 | $node->views_query = ''; | |
| 566 | if (!$node->crm_query) { | |
| 567 | form_set_error('crm_query', t("Please choose a CiviCRM group to extract data from")); | |
| 568 | } | |
| 569 | ||
| 570 | $node->query_name = $node->crm_query; | |
| 571 | } | |
| 572 | ||
| 573 | //Check if we need an upload | |
| 574 | $had_report = FALSE; | |
| 575 | if ($node->nid) { | |
| 576 | $had_report = query_export_report_rec_has_text($node->nid); | |
| 577 | } | |
| 578 | if (!$had_report && !isset($_SESSION['jasper_jrxml_file'])) { | |
| 579 | form_set_error('jasper_jrxml_upload', t('Please choose a Jasper Reports definition file (JRXML) to upload.')); | |
| 580 | } | |
| 581 | ||
| 582 | //Prepare stuff gets thrown away? Fix it here as well | |
| 583 | if (isset($_SESSION['jasper_jrxml_file'])) { | |
| 584 | $node->jasper_jrxml_file = $_SESSION['jasper_jrxml_file']; | |
| 585 | } | |
| 586 | } | |
| 587 | ||
| 588 | ||
| 589 | ||
| 590 | function query_export_submit(&$node) { | |
| 591 | //Set derived fields if not already set | |
| 592 | if (!isset($node->query_name)) { | |
| 593 | if ($node->query_type == 'civicrm') | |
| 594 | $node->query_name = $node->crm_query; | |
| 595 | elseif ($node->query_type == 'views') | |
| 596 | $node->query_name = "views::" . $node->views_query; | |
| 597 | else | |
| 598 | $node->query_name = "custom::custom_{$node->nid}"; | |
| 599 | } | |
| 600 | // check if the file was stored in the session during audio_prepare(). | |
| 601 | if (isset($_SESSION['jasper_jrxml_file'])) { | |
| 602 | $node->jasper_jrxml_file = $_SESSION['jasper_jrxml_file']; | |
| 603 | //Scoop up the file and delete it | |
| 604 | $report_source = file_get_contents($node->jasper_jrxml_file->filepath); | |
| 605 | if (!$report_source) { | |
| 606 | drupal_set_message(t('Warning -- upload failed!')); | |
| 607 | error_log("Upload error: " . $node->jasper_jrxml_file->filepath); | |
| 608 | } | |
| 609 | else { | |
| 610 | $node->report_text = $report_source; | |
| 611 | $node->jasper = NULL; | |
| 612 | } | |
| 613 | ||
| 614 | // won't be needing this any longer | |
| 615 | file_delete($node->jasper_jrxml_file); | |
| 616 | unset($_SESSION['jasper_jrxml_file']); | |
| 617 | } | |
| 618 | ||
| 619 | } | |
| 620 | ||
| 621 | ||
| 622 | ||
| 623 | ||
| 624 | /** | |
| 625 | * Implementation of hook_insert | |
| 626 | */ | |
| 627 | function query_export_insert(&$node) { | |
| 628 | $sql = "INSERT INTO {query_export_reports} | |
| 629 | (nid, last_update, query_name, query_type,query_profile, | |
| 630 | query_sql, report_text) | |
| 631 | VALUES(%d,UNIX_TIMESTAMP(),'%s','%s',%d,'%s','%s')"; | |
| 632 | ||
| 633 | db_query($sql, $node->nid, | |
| 634 | $node->query_name, $node->query_type, | |
| 635 | $node->query_profile, | |
| 636 | $node->query_sql, | |
| 637 | $node->report_text); | |
| 638 | //Get the proxy to update the record | |
| 639 | query_export_request_compile($node->nid); | |
| 640 | } | } |
| 641 | ||
| 642 | ||
| 643 | /** | /** |
| 644 | * Implementation of hook_update | |
| 645 | * | |
| 646 | */ | |
| 647 | function query_export_update($node){ | |
| 648 | if ($node->report_text) { | |
| 649 | //If we are not setting this now, we want | |
| 650 | //to 0 out the jasper object, but we don't | |
| 651 | //want to remove an existing report. | |
| 652 | $set_report = "report_text = '%s',"; | |
| 653 | } | |
| 654 | else | |
| 655 | $set_report = ''; | |
| 656 | ||
| 657 | $sql = "UPDATE {query_export_reports} | |
| 658 | SET | |
| 659 | query_name = '%s', query_type = '%s', | |
| 660 | query_profile = %d, | |
| 661 | query_sql = '%s', | |
| 662 | $set_report | |
| 663 | last_update = UNIX_TIMESTAMP(), | |
| 664 | jasper = NULL | |
| 665 | WHERE nid = %d"; | |
| 666 | if ($set_report) | |
| 667 | db_query($sql, | |
| 668 | $node->query_name, $node->query_type, | |
| 669 | $node->query_profile, $node->query_sql, | |
| 670 | $node->report_text, $node->nid); | |
| 671 | else | |
| 672 | db_query($sql, | |
| 673 | $node->query_name, $node->query_type, | |
| 674 | $node->query_profile, $node->query_sql, $node->nid); | |
| 675 | ||
| 676 | //Get the proxy to update the record | |
| 677 | query_export_request_compile($node->nid); | |
| 678 | ||
| 679 | } | |
| 680 | ||
| 681 | ||
| 682 | /** | |
| 683 | * Implementation of hook_load | |
| 684 | * | |
| 685 | */ | |
| 686 | function query_export_load($node){ | |
| 687 | $sql = 'SELECT * FROM {query_export_reports} WHERE nid = %d'; | |
| 688 | $obj = db_fetch_object(db_query($sql, $node->nid)); | |
| 689 | unset($obj->jasper); //we do not use this internally | |
| 690 | if ($obj->query_type == 'civicrm') | |
| 691 | $obj->crm_query = $obj->query_name; | |
| 692 | else if ($obj->query_type == 'views') | |
| 693 | $obj->views_query = $obj->query_name; | |
| 694 | ||
| 695 | return $obj; | |
| 696 | } | |
| 697 | ||
| 698 | ||
| 699 | /** | |
| 700 | * Implementation of hook_delete | |
| 701 | * | |
| 702 | */ | |
| 703 | function query_export_delete($node){ | |
| 704 | $sql = "DELETE FROM {query_export_reports} WHERE nid = %d "; | |
| 705 | db_query($sql, $node->nid); | |
| 706 | } | |
| 707 | ||
| 708 | ||
| 709 | ||
| 710 | /** | |
| 711 | * Implementation of hook_form(). | * Implementation of hook_form(). |
| 712 | */ | */ |
| 713 | function query_export_form(&$node) { | function query_export_form(&$node) { |
| 714 | $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); | $form['#attributes'] = array("enctype" => "multipart/form-data"); |
| 715 | //TO DO: Lots of stuff here. | $form['title'] = array('#type' => 'textfield', '#title' => t('Short Query Description'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); |
| 716 | ||
| 717 | //Handle preview problems... | |
| 718 | if (!isset($node->query_type)) { | |
| 719 | if (isset($_POST['edit'])) { | |
| 720 | $edit = $_POST['edit']; | |
| 721 | if (isset($edit['query_type'])) { | |
| 722 | // make some resettings | |
| 723 | if ($edit['query_type'] != 'views') | |
| 724 | $_POST['edit']['views_query'] = ''; | |
| 725 | else if ($edit['query_type'] != 'civicrm') | |
| 726 | $_POST['edit']['crm_query'] = ''; | |
| 727 | } | |
| 728 | } | |
| 729 | } | |
| 730 | ||
| 731 | $form['query_type'] = | |
| 732 | array('#type' => 'radios', | |
| 733 | '#title' => t('Type Of Data Object'), | |
| 734 | '#description' => t('What kind of data do you want to use for your report?'), | |
| 735 | '#options' => | |
| 736 | array( | |
| 737 | 'civicrm' => t('CiviCRM Group Of Contacts'), | |
| 738 | 'views' => t('Drupals Views Data'), | |
| 739 | 'custom' => t('Custom Query (SQL)'), | |
| 740 | ), | |
| 741 | '#required' => TRUE, | |
| 742 | '#default_value' => $node->query_type | |
| 743 | ); | |
| 744 | //Nice option for use of Stupid Java Script tricks: hide one or the other: | |
| 745 | $v_options = | |
| 746 | array('' => "-- select a view --", | |
| 747 | query_export_list_views_queries()); | |
| 748 | $v_default = $node->views_query ? $node->views_query : ''; | |
| 749 | ||
| 750 | $c_options = array('' => "-- select a CiviCRM group --", | |
| 751 | query_export_list_civicrm_queries()); | |
| 752 | $c_default = $node->crm_query ? $node->crm_query : ''; | |
| 753 | ||
| 754 | $form['views_query'] = | |
| 755 | array('#type' => 'select', | |
| 756 | '#title' => t('Exportable Drupal Views'), | |
| 757 | '#options' => $v_options, | |
| 758 | '#default_value' => $v_default, | |
| 759 | '#description' => t('Choose a views table to pass to the reporting engine')); | |
| 760 | ||
| 761 | $form['crm_query'] = | |
| 762 | array('#type' => 'select', | |
| 763 | '#title' => t('Exportable CiviCRM Groups'), | |
| 764 | '#options' => $c_options, | |
| 765 | '#default_value' => $c_default, | |
| 766 | '#description' => t('Choose a group of CiviCRM contacts to pass to the reporting engine')); | |
| 767 | ||
| 768 | ||
| 769 | if (module_exist('civinode')) { | |
| 770 | $profile_opts = civinode_profile_selector(t('CRM Profile'), | |
| 771 | t('For formatting CiviCRM contacts, choose a profile to define what fields to pass to the reporting engine'), | |
| 772 | $node->query_profile); | |
| 773 | $form['query_profile'] = $profile_opts; | |
| 774 | } | |
| 775 | ||
| 776 | ||
| 777 | //Custom stuff | |
| 778 | $form['query_sql'] = | |
| 779 | array('#type' => 'textarea', | |
| 780 | '#title' => t('Custom SQL'), | |
| 781 | '#description' => t("For custom queries only, you can design your own query on any Drupal table. This is an advanced feature, and if your database disappears in a puff of smoke, don't say we didn't warn you :-)"), | |
| 782 | '#cols' => 60, | |
| 783 | '#rows' => 10, | |
| 784 | '#default_value' => $node->query_sql | |
| 785 | ); | |
| 786 | //File object for upload | |
| 787 | $form['jasper_jrxml_upload'] = | |
| 788 | array( | |
| 789 | '#type' => 'file', | |
| 790 | '#title' => t('Attach JRXML File'), | |
| 791 | '#description' => | |
| 792 | t('Select a JasperReports report definition file (.jrxml) to upload to the server')); | |
| 793 | ||
| 794 | return $form; | return $form; |
| 795 | } | } |
| 796 | ||
| 797 | ||
| 798 | ||
| 799 | ||
| 800 | /** | |
| 801 | * Implementation of hook_form_alter: | |
| 802 | * Add a copy button to the main node form | |
| 803 | * | |
| 804 | */ | |
| 805 | function query_export_form_alter($form_id, &$form){ | |
| 806 | if ($form_id == 'query_export_node_form') { | |
| 807 | $copy_value = t('Copy'); | |
| 808 | //check for '#submit' key, 'submit' item(s) #submit['node_form_submit'] | |
| 809 | //#node key on submit, key nid['#value'] | |
| 810 | ||
| 811 | //check for a delete item, and if it exists, add a copy as well | |
| 812 | if (isset($form['delete'])) { | |
| 813 | //construct our copy button | |
| 814 | if (!isset($form['copy'])) { | |
| 815 | $form['copy'] = | |
| 816 | array('#type' => 'submit', | |
| 817 | '#value' => $copy_value); | |
| 818 | ||
| 819 | $form['copy']['#weight'] = $form['delete']['#weight'] + 2; | |
| 820 | ||
| 821 | } | |
| 822 | } | |
| 823 | ||
| 824 | } | |
| 825 | } | |
| 826 | ||
| 827 | ||
| 828 | /** | |
| 829 | * Take advantage of EditAsNew module if available. For now, | |
| 830 | * I'm using the renamed template.module version of this. | |
| 831 | * | |
| 832 | */ | |
| 833 | function query_export_template_insert(&$node, $template_node){ | |
| 834 | error_log("Template insert was called"); | |
| 835 | ||
| 836 | } | |
| 837 | ||
| 838 | ||
| 839 | ||
| 840 | ||
| 841 | function query_export_xmlrpc() { | function query_export_xmlrpc() { |
| 842 | $methods = array(); | $methods = array(); |
| 843 | ||
| # | Line 141 function query_export_xmlrpc() { | Line 847 function query_export_xmlrpc() { |
| 847 | //with everything else. | //with everything else. |
| 848 | $methods[] = | $methods[] = |
| 849 | array( | array( |
| 850 | 'drupal.qexport.login', | 'drupal.qexport.login', |
| 851 | 'query_export_qexport_login_handler', | 'query_export_qexport_login_handler', |
| 852 | array('struct', 'string', 'string'), | array('struct', 'string', 'string'), |
| 853 | t('Login/Authentication For Query Export. Takes user/pw, returns qe result object.')); | t('Login/Authentication For Query Export. Takes user/pw, returns qe result object.')); |
| 854 | ||
| 855 | //Get queries of a certain type. pass cookie as first arg, | //Get queries of a certain type. pass cookie as first arg, |
| 856 | //params as second. params is a struct, with keys: | //params as second. params is a struct, with keys: |
| # | Line 152 function query_export_xmlrpc() { | Line 858 function query_export_xmlrpc() { |
| 858 | //allow taxonomy somehow. | //allow taxonomy somehow. |
| 859 | $methods[] = | $methods[] = |
| 860 | array( | array( |
| 861 | 'drupal.qexport.query.list', | 'drupal.qexport.query.list', |
| 862 | 'query_export_qexport_query_list_handler', | 'query_export_qexport_query_list_handler', |
| 863 | array('array', 'string', 'struct'), | array('array', 'string', 'struct'), |
| 864 | t('Fetches a list of queries that meet some set of criteria')); | t('Fetches a list of queries that meet some set of criteria')); |
| 865 | ||
| 866 | //This method returns meta data about a query. For now, enough | //This method returns meta data about a query. For now, enough |
| 867 | //info to describe the query to the reporting engine, whatever | //info to describe the query to the reporting engine, whatever |
| 868 | //that is. | //that is. |
| 869 | $methods[] = | $methods[] = |
| 870 | array( | array( |
| 871 | 'drupal.qexport.query.metadata', | 'drupal.qexport.query.metadata', |
| 872 | 'query_export_qexport_query_metadata_handler', | 'query_export_qexport_query_metadata_handler', |
| 873 | array('array', 'string', 'string'), | array('array', 'string', 'string'), |
| 874 | t('Fetches metadata about a query')); | t('Fetches metadata about a query')); |
| 875 | ||
| 876 | ||
| 877 | ||
| # | Line 175 function query_export_xmlrpc() { | Line 881 function query_export_xmlrpc() { |
| 881 | //queries. | //queries. |
| 882 | $methods[] = | $methods[] = |
| 883 | array( | array( |
| 884 | 'drupal.qexport.query.run', | 'drupal.qexport.query.run', |
| 885 | 'query_export_qexport_query_run_handler', | 'query_export_qexport_query_run_handler', |
| 886 | array('array', 'string', 'string', 'struct'), | array('array', 'string', 'string', 'struct'), |
| 887 | t('Fetches an array of records for a query')); | t('Fetches an array of records for a query')); |
| 888 | ||
| 889 | ||
| 890 | ||
| # | Line 204 function query_export_qexport_login_hand | Line 910 function query_export_qexport_login_hand |
| 910 | else { | else { |
| 911 | return array('user' => $user_name, 'cookie' => $auth_obj->cookie, | return array('user' => $user_name, 'cookie' => $auth_obj->cookie, |
| 912 | 'expires' => $auth_obj->expires | 'expires' => $auth_obj->expires |
| 913 | ); | ); |
| 914 | } | } |
| 915 | //test stub | //test stub |
| 916 | //return array('user' => $user_name, 'cookie' => 'monster'); | //return array('user' => $user_name, 'cookie' => 'monster'); |
| 917 | } | } |
| 918 | ||
| 919 | ||
| # | Line 217 function query_export_qexport_query_list | Line 923 function query_export_qexport_query_list |
| 923 | error_log("user recognized as ", $access->user); | error_log("user recognized as ", $access->user); |
| 924 | if(!user_access('access query exports via XML-RPC')){ | if(!user_access('access query exports via XML-RPC')){ |
| 925 | error_log("User not authorized"); | error_log("User not authorized"); |
| 926 | return array(); //isn't there a nicer way to bail?? | return xmlrpc_server_error(QUERY_EXPORT_ERROR_UNAUTHORIZED, t('Not authorized for this user')); |
| 927 | } | } |
| 928 | //Test stub | //Test stub |
| 929 | //return array('fortunes', 'quick_hits', 'best_episodes'); | //return array('fortunes', 'quick_hits', 'best_episodes'); |
| # | Line 231 function query_export_qexport_query_meta | Line 937 function query_export_qexport_query_meta |
| 937 | global $user; | global $user; |
| 938 | $access = query_export_auth_from_cookie($cookie, $user); | $access = query_export_auth_from_cookie($cookie, $user); |
| 939 | if(!user_access('access query exports via XML-RPC')) | if(!user_access('access query exports via XML-RPC')) |
| 940 | return array(); //isn't there a nicer way to bail?? | return xmlrpc_server_error(QUERY_EXPORT_ERROR_UNAUTHORIZED, t('Not authorized for this user')); |
| 941 | return _qex_test_get_metadata($qname); | return _qex_test_get_metadata($qname); |
| 942 | //Test stub | //Test stub |
| 943 | /** | /** |
| 944 | return array( | return array( |
| 945 | array('name' => 'name', | array('name' => 'name', |
| 946 | 'type' => 'string', | 'type' => 'string', |
| 947 | 'size' => 20, | 'size' => 20, |
| 948 | 'required' => 1), | 'required' => 1), |
| 949 | array('name' => 'rating', | array('name' => 'rating', |
| 950 | 'type' => 'int', | 'type' => 'int', |
| 951 | 'required' => 1) | 'required' => 1) |
| 952 | ); | ); |
| 953 | **/ | **/ |
| 954 | ||
| 955 | } | } |
| # | Line 252 function query_export_qexport_query_meta | Line 958 function query_export_qexport_query_meta |
| 958 | function query_export_qexport_query_run_handler($cookie, $qname, $qparams){ | function query_export_qexport_query_run_handler($cookie, $qname, $qparams){ |
| 959 | $results = array(); | $results = array(); |
| 960 | /** | /** |
| 961 | //Test stub | //Test stub |
| 962 | $results[] = array('name' => "Uncle Junior", 'rating' => 9); | $results[] = array('name' => "Uncle Junior", 'rating' => 9); |
| 963 | $results[] = array('name' => "Big Tony", 'rating' => 15); | $results[] = array('name' => "Big Tony", 'rating' => 15); |
| 964 | $results[] = array('name' => "Little Tony", 'rating' => 0); | $results[] = array('name' => "Little Tony", 'rating' => 0); |
| 965 | **/ | **/ |
| 966 | ||
| 967 | global $user; | global $user; |
| 968 | error_log("Entering Run XMLRPC handler"); | |
| 969 | $access = query_export_auth_from_cookie($cookie, $user); | $access = query_export_auth_from_cookie($cookie, $user); |
| 970 | if(!user_access('access query exports via XML-RPC')) | if(!user_access('access query exports via XML-RPC')) { |
| 971 | return array(); //isn't there a nicer way to bail?? | error_log("Not authorized for run!!"); |
| 972 | return xmlrpc_server_error(QUERY_EXPORT_ERROR_UNAUTHORIZED, t('Not authorized for this user')); | |
| 973 | } | |
| 974 | error_log("OK for run; about to request records"); | |
| 975 | return _qex_test_get_records_for_query($qname, $qparams); | return _qex_test_get_records_for_query($qname, $qparams); |
| 976 | } | } |
| 977 | ||
| 978 | /** | /** |
| 979 | * Authentication functions | * Authentication functions |
| 980 | * | * |
| 981 | */ | */ |
| 982 | ||
| 983 | ||
| 984 | /** | /** |
| 985 | * Authenticate against drupal and return a query_export | * Authenticate against drupal and return a query_export |
| 986 | * auth record. Return NULL if we did not authenticate. | * auth record. Return NULL if we did not authenticate. |
| 987 | * | * |
| 988 | */ | */ |
| 989 | ||
| 990 | function query_export_authenticate($user_name, $password){ | function query_export_authenticate($user_name, $password){ |
| 991 | global $user; | global $user; |
| # | Line 294 function query_export_authenticate($user | Line 1003 function query_export_authenticate($user |
| 1003 | //And create the record (slight possibility of collision. Do I need to | //And create the record (slight possibility of collision. Do I need to |
| 1004 | //add some kind of random seed as well? | //add some kind of random seed as well? |
| 1005 | $sql = "INSERT INTO {query_export_auth} (uid, expires, user_name, cookie) " . | $sql = "INSERT INTO {query_export_auth} (uid, expires, user_name, cookie) " . |
| 1006 | "VALUES(%d, %d, '%s', '%s')"; | "VALUES(%d, %d, '%s', '%s')"; |
| 1007 | db_query($sql, $user_obj->uid, $expires, $user_name, $cookie); | db_query($sql, $user_obj->uid, $expires, $user_name, $cookie); |
| 1008 | //And get back our object | //And get back our object |
| 1009 | $obj = db_fetch_object(db_query("SELECT * FROM {{query_export_auth}} WHERE cookie = '%s'", $cookie)); | $obj = db_fetch_object(db_query("SELECT * FROM {query_export_auth} WHERE cookie = '%s'", $cookie)); |
| 1010 | return $obj; | return $obj; |
| 1011 | } | } |
| 1012 | ||
| 1013 | ||
| 1014 | /** | |
| 1015 | * Create a cookie record so our user can pass a temporary | |
| 1016 | * authorization to our Java proxy | |
| 1017 | * | |
| 1018 | */ | |
| 1019 | ||
| 1020 | function query_export_generate_cookie($seed = 'salt'){ | |
| 1021 | global $user; | |
| 1022 | ||
| 1023 | //Calculate our expiry time | |
| 1024 | //Default at 1 minute | |
| 1025 | $expires = 60 + time(); | |
| 1026 | //Make a cookie | |
| 1027 | $dough = sprintf("%s%d%s", $user->name, $expires, $salt); | |
| 1028 | $cookie = md5($dough); | |
| 1029 | //And create the record (slight possibility of collision. Do I need to | |
| 1030 | //add some kind of random seed as well? | |
| 1031 | $sql = "INSERT INTO {query_export_auth} (uid, expires, user_name, cookie) " . | |
| 1032 | "VALUES(%d, %d, '%s', '%s')"; | |
| 1033 | db_query($sql, $user->uid, $expires, $user->name, $cookie); | |
| 1034 | $time = date('r'); | |
| 1035 | error_log("$date: cookie $cookie for user " . $user->name); | |
| 1036 | return $cookie; | |
| 1037 | } | |
| 1038 | ||
| 1039 | ||
| 1040 | ||
| 1041 | /** | /** |
| 1042 | * Authenticate by looking up the cookie. Must find the cookie, and | * Authenticate by looking up the cookie. Must find the cookie, and |
| 1043 | * the cookie should still be valid. | * the cookie should still be valid. |
| 1044 | * | * |
| 1045 | */ | */ |
| 1046 | ||
| 1047 | function query_export_auth_from_cookie($cookie, &$user_obj){ | function query_export_auth_from_cookie($cookie, &$user_obj){ |
| 1048 | global $user; | global $user; |
| # | Line 324 function query_export_auth_from_cookie($ | Line 1061 function query_export_auth_from_cookie($ |
| 1061 | _query_export_construct_user($obj->uid); | _query_export_construct_user($obj->uid); |
| 1062 | ||
| 1063 | if ($user->uid) { | if ($user->uid) { |
| 1064 | $time = date('r'); | |
| 1065 | error_log("$time: authed as user " . $user->uid . "with cookie $cookie"); | |
| 1066 | return $obj; | return $obj; |
| 1067 | } | } |
| 1068 | } | } |
| 1069 | $time = date('r'); | |
| 1070 | error_log("$time: auth failed with cookie $cookie"); | |
| 1071 | //Or we are nobody at all | //Or we are nobody at all |
| 1072 | return NULL; | return NULL; |
| 1073 | } | } |
| # | Line 394 function query_export_test_rpc(){ | Line 1135 function query_export_test_rpc(){ |
| 1135 | } | } |
| 1136 | ||
| 1137 | ||
| 1138 | function _qex_test_avail_queries(){ | function query_export_list_civicrm_queries(){ |
| $queries = array(); | ||
| //For now, let's export one query for each flexinode type we know about | ||
| if (module_exist('flexinode')) { | ||
| $types = flexinode_content_types(); | ||
| foreach($types as $ctype_id => $type_data){ | ||
| $key = 'flexinode::flexinode-' . $ctype_id; | ||
| $queries[$key] = $type_data->name; // | ||
| } | ||
| } | ||
| 1139 | //And for civicrm, list our groups | //And for civicrm, list our groups |
| 1140 | $queries = array(); | |
| 1141 | if (module_exist('civicrm') and module_exist('civinode')) { | if (module_exist('civicrm') and module_exist('civinode')) { |
| 1142 | $groups = civinode_get_groups(); //get 'em all for now | $groups = civinode_get_groups(); //get 'em all for now |
| 1143 | if($groups){ | if($groups){ |
| # | Line 414 function _qex_test_avail_queries(){ | Line 1147 function _qex_test_avail_queries(){ |
| 1147 | } | } |
| 1148 | } | } |
| 1149 | } | } |
| 1150 | return $queries; | |
| 1151 | } | |
| 1152 | ||
| 1153 | function _qex_test_avail_queries(){ | |
| 1154 | $queries = array(); | |
| 1155 | //For now, let's export one query for each flexinode type we know about | |
| 1156 | /** | |
| 1157 | ||
| 1158 | //No flexinode unless someone begs :-) | |
| 1159 | //and pays too, for that matter | |
| 1160 | ||
| 1161 | if (module_exist('flexinode')) { | |
| 1162 | $types = flexinode_content_types(); | |
| 1163 | foreach($types as $ctype_id => $type_data){ | |
| 1164 | $key = 'flexinode::flexinode-' . $ctype_id; | |
| 1165 | $queries[$key] = $type_data->name; // | |
| 1166 | } | |
| 1167 | } | |
| 1168 | **/ | |
| 1169 | ||
| 1170 | //And for civicrm, list our groups | |
| 1171 | if (module_exist('civicrm') and module_exist('civinode')) { | |
| 1172 | $queries = query_export_list_civicrm_queries(); | |
| 1173 | } | |
| 1174 | ||
| 1175 | //If we have created an custom queries, fetch them now | |
| 1176 | $custom_queries = _qex_fetch_custom_queries(); | |
| 1177 | foreach ($custom_queries as $qname => $qtitle) { | |
| 1178 | $queries[$qname] = $qtitle; | |
| 1179 | } | |
| 1180 | if (module_exist('views') and module_exist('civinode')) { | if (module_exist('views') and module_exist('civinode')) { |
| $groups = civinode_get_groups(); //get 'em all for now | ||
| 1181 | //TO DO: I probably need to wrap this stuff into | //TO DO: I probably need to wrap this stuff into |
| 1182 | //some kind of loader function. | //some kind of loader function. |
| 1183 | if (!function_exists('query_export_list_views_queries')) { | if (!function_exists('query_export_list_views_queries')) { |
| # | Line 437 function _qex_test_avail_queries(){ | Line 1198 function _qex_test_avail_queries(){ |
| 1198 | } | } |
| 1199 | ||
| 1200 | ||
| 1201 | /** | |
| 1202 | * Return our custom queries | |
| 1203 | * | |
| 1204 | */ | |
| 1205 | function _qex_fetch_custom_queries(){ | |
| 1206 | $sql = "select q.query_name, n.title as query_title from {node} n LEFT JOIN {query_export_reports} q ON (n.nid = q.nid) WHERE q.query_type = 'custom'"; | |
| 1207 | //exclude queries we do not have the right to see | |
| 1208 | $mod_sql = db_rewrite_sql($sql, 'n', 'nid'); | |
| 1209 | $rslt = db_query($mod_sql); | |
| 1210 | $queries = array(); | |
| 1211 | while ($row = db_fetch_array($rslt)) { | |
| 1212 | $queries[$row['query_name']] = $row['query_title']; | |
| 1213 | } | |
| 1214 | return $queries; | |
| 1215 | } | |
| 1216 | ||
| 1217 | ||
| 1218 | function _qex_test_get_metadata($qname){ | function _qex_test_get_metadata($qname){ |
| 1219 | list($namespace, $name) = explode('::', $qname); | list($namespace, $name) = explode('::', $qname); |
| 1220 | //Test data: all legal names have this | //Test data: all legal names have this |
| # | Line 445 function _qex_test_get_metadata($qname){ | Line 1223 function _qex_test_get_metadata($qname){ |
| 1223 | $type_info = query_export_get_type($namespace, $name); | $type_info = query_export_get_type($namespace, $name); |
| 1224 | if(!$type_info){ | if(!$type_info){ |
| 1225 | //consider logging error or telling xmlrpc | //consider logging error or telling xmlrpc |
| 1226 | error_log("No type info... bailing"); | |
| 1227 | return xmlrpc_server_error(QUERY_EXPORT_ERROR_NO_SUCH_QUERY, t('Query is unknown to server')); | return xmlrpc_server_error(QUERY_EXPORT_ERROR_NO_SUCH_QUERY, t('Query is unknown to server')); |
| 1228 | } | } |
| 1229 | //Do not pass internal data to XMLRPC | //Do not pass internal data to XMLRPC |
| # | Line 457 function _qex_test_get_metadata($qname){ | Line 1236 function _qex_test_get_metadata($qname){ |
| 1236 | function _qex_test_get_records_for_query($qname, $params){ | function _qex_test_get_records_for_query($qname, $params){ |
| 1237 | list($namespace, $name) = explode('::', $qname); | list($namespace, $name) = explode('::', $qname); |
| 1238 | ||
| 1239 | //Temporary hack: custom types | |
| 1240 | //if ($namespace == 'custom' and !user_access('access custom tables')) | |
| 1241 | // xmlrpc_server_error(QUERY_EXPORT_ERROR_UNAUTHORIZED, t('Not authorized for this user')); | |
| 1242 | //process parameters | //process parameters |
| 1243 | $args = isset($params['args']) ? $params['args'] : FALSE; | $args = isset($params['args']) ? $params['args'] : FALSE; |
| 1244 | $start = isset($params['start']) ? $params['start'] : 0; | $start = isset($params['start']) ? $params['start'] : 0; |
| 1245 | $num_recs = isset($params['num_recs']) ? $params['num_recs'] : 200; | //num_recs should be 0 if no limit, the number otherwise. Not all sources |
| 1246 | //implement this (currently, only CiviCRM does) | |
| 1247 | $num_recs = isset($params['num_recs']) ? $params['num_recs'] : 0; | |
| 1248 | $profile = isset($params['profile_id']) ? $params['profile_id'] : 0; | |
| 1249 | $query_nid = isset($params['query_nid']) ? $params['query_nid'] : 0; | |
| 1250 | ||
| 1251 | return query_export_get_record_array($namespace, $name, $query_nid, | |
| 1252 | $args, $profile, $start, $num_recs); | |
| 1253 | } | |
| 1254 | ||
| 1255 | ||
| 1256 | /** | |
| 1257 | * Some view support for choosing queries | |
| 1258 | * | |
| 1259 | */ | |
| 1260 | ||
| 1261 | ||
| 1262 | ||
| 1263 | /** | |
| 1264 | * Implementation of hook_views_tables (View API) | |
| 1265 | * | |
| 1266 | */ | |
| 1267 | ||
| 1268 | function query_export_views_tables(){ | |
| 1269 | $tables['query_export_defs'] = | |
| 1270 | array('name' => 'query_export_reports', | |
| 1271 | 'provider' => 'query_export', | |
| 1272 | 'join' => | |
| 1273 | array('left' => | |
| 1274 | array('table' => 'node', | |
| 1275 | 'field' => 'nid'), | |
| 1276 | 'right' => | |
| 1277 | array('field' => 'nid') | |
| 1278 | ), | |
| 1279 | 'fields' => | |
| 1280 | array('query_name' => | |
| 1281 | array('name' => t('Query Exporter: Query Name'), | |
| 1282 | 'handler' => 'query_export_qname_view_handler', | |
| 1283 | 'sortable' => TRUE), | |
| 1284 | 'query_type' => | |
| 1285 | array('name' => t('Query Exporter: Query Type')), | |
| 1286 | 'query_profile' => | |
| 1287 | array('name' => t('Query Exporter: CRM Profile Definition'), | |
| 1288 | 'handler' => 'query_export_profile_view_handler' | |
| 1289 | ), | |
| 1290 | 'last_update' => | |
| 1291 | array('name' => t('Query Exporter: Last Updated'), | |
| 1292 | 'handler' => 'query_export_unix_time_view_handler'), | |
| 1293 | ) | |
| 1294 | ); | |
| 1295 | ||
| 1296 | //Expose data handlers | |
| 1297 | $tables['query_export_formatters'] = | |
| 1298 | array('name' => 'query_export_data_handlers', | |
| 1299 | 'provider' => 'query_export', | |
| 1300 | 'join' => | |
| 1301 | array('left' => | |
| 1302 | array('table' => 'node', | |
| 1303 | 'field' => 'nid'), | |
| 1304 | 'right' => | |
| 1305 | array('field' => 'nid') | |
| 1306 | ), | |
| 1307 | 'fields' => | |
| 1308 | array( | |
| 1309 | 'field_name' => | |
| 1310 | array('name' => t('Query Exporter: Field Name'), | |
| 1311 | 'addlfields' => array('nid'), | |
| 1312 | 'handler' => | |
| 1313 | array('query_export_formatter_field_link_handler' => t('Edit Or Delete Link'), | |
| 1314 | 'query_export_passthru_handler' => t('Pass through')), | |
| 1315 | 'sortable' => TRUE), | |
| 1316 | 'handler' => | |
| 1317 | array('name' => t('Query Exporter: Data Formatter'), | |
| 1318 | 'handler' => 'query_export_formatter_name_handler'), | |
| 1319 | 'extra' => | |
| 1320 | array('name' => t('Query Exporter: Extra Formatter Info')), | |
| 1321 | ) | |
| 1322 | ); | |
| 1323 | ||
| 1324 | //Expose default parameters | |
| 1325 | $tables['query_export_parameters'] = | |
| 1326 | array('name' => 'query_export_param_defaults', | |
| 1327 | 'provider' => 'query_export', | |
| 1328 | 'join' => | |
| 1329 | array('left' => | |
| 1330 | array('table' => 'node', | |
| 1331 | 'field' => 'nid'), | |
| 1332 | 'right' => | |
| 1333 | array('field' => 'nid') | |
| 1334 | ), | |
| 1335 | 'fields' => | |
| 1336 | array( | |
| 1337 | 'param_name' => | |
| 1338 | array('name' => t('Query Exporter: Default Parameter'), | |
| 1339 | 'addlfields' => array('nid'), | |
| 1340 | 'handler' => | |
| 1341 | array('query_export_param_link_handler' => t('Edit Or Delete Link'), | |
| 1342 | 'query_export_passthru_handler' => t('Pass through')), | |
| 1343 | 'sortable' => TRUE), | |
| 1344 | 'default_value' => | |
| 1345 | array('name' => t('Query Exporter: Default Param Value')), | |
| 1346 | 'description' => | |
| 1347 | array('name' => t('Query Exporter: Parameter Description')), | |
| 1348 | ) | |
| 1349 | ); | |
| 1350 | ||
| 1351 | ||
| 1352 | return $tables; | |
| 1353 | } | |
| 1354 | ||
| 1355 | ||
| 1356 | function query_export_profile_view_handler($fieldinfo, $fielddata, $value, $data){ | |
| 1357 | if (module_exist('civinode') and module_exist('civicrm')) { | |
| 1358 | if ($value) | |
| 1359 | $title = civinode_get_profile_title($value); | |
| 1360 | else | |
| 1361 | $title = t('NA'); | |
| 1362 | if ($title) | |
| 1363 | return $title; | |
| 1364 | else | |
| 1365 | return $value; | |
| 1366 | } | |
| 1367 | else | |
| 1368 | return t('NA'); | |
| 1369 | } | |
| 1370 | ||
| 1371 | ||
| 1372 | function query_export_qname_view_handler($fieldinfo, $fielddata, $value, $data){ | |
| 1373 | return query_export_query_ui_name($value); | |
| 1374 | } | |
| 1375 | ||
| 1376 | function query_export_unix_time_view_handler($fieldinfo, $fielddata, $value, $data){ | |
| 1377 | if (!$value or !is_numeric($value)) | |
| 1378 | return t('NA'); | |
| 1379 | return date('r', $value); | |
| 1380 | } | |
| 1381 | ||
| 1382 | ||
| 1383 | function query_export_formatter_name_handler($fieldinfo, $fielddata, $value, $data){ | |
| 1384 | query_export_prime_handler_cache(); //safe to call multiple times | |
| 1385 | $title = query_export_get_data_handlers($value); | |
| 1386 | if ($title) | |
| 1387 | return $title; | |
| 1388 | else | |
| 1389 | return $value; | |
| 1390 | } | |
| 1391 | ||
| 1392 | ||