/[drupal]/contributions/modules/netforum/netforum.module
ViewVC logotype

Contents of /contributions/modules/netforum/netforum.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.13 - (show annotations) (download) (as text)
Sun Oct 18 17:14:55 2009 UTC (5 weeks, 1 day ago) by jamesmichaelhill
Branch: MAIN
CVS Tags: DRUPAL-5--1-1, HEAD
Changes since 1.12: +216 -81 lines
File MIME type: text/x-php
Updating xWeb testing form to smartly display parameters for complex methods.
1 <?php
2 // $Id: netforum.module,v 1.12 2009/07/11 13:43:51 jamesmichaelhill Exp $
3
4 /**
5 * @file
6 * The netFORUM xWeb Secure module
7 *
8 * Provides a foundation for issuing requests and reading responses from xWeb
9 *
10 */
11
12
13 /**
14 * Implementation of hook_menu()
15 */
16 function netforum_menu($may_cache) {
17 $items = array();
18
19 if ($may_cache) {
20 $items[] = array(
21 'path' => 'admin/settings/netforum',
22 'title' => t('Netforum'),
23 'description' => t('Set the connection characteristics for the netFORUM database'),
24 'callback' => 'system_admin_menu_block_page',
25 'access' => user_access('administer site configuration'),
26 );
27 $items[] = array(
28 'path' => 'admin/settings/netforum/connection',
29 'title' => t('xWeb settings'),
30 'description' => t('Set the connection characteristics for the netFORUM database'),
31 'callback' => 'drupal_get_form',
32 'callback arguments' => array('netforum_admin_settings'),
33 'access' => user_access('administer site configuration'),
34 );
35 $items[] = array(
36 'path' => 'admin/settings/netforum/xwebtest',
37 'title' => t('xWeb testing'),
38 'description' => t('Test xWeb queries to the database'),
39 'callback' => 'netforum_test_page',
40 'access' => user_access('administer site configuration'),
41 'weight' => 5,
42 );
43
44 $items[] = array(
45 'path' => 'admin/settings/netforum/object-info',
46 'title' => t('Object information'),
47 'description' => t('View, fetch and delete cached object information'),
48 'callback' => 'netforum_objects_page',
49 'access' => user_access('administer site configuration'),
50 'weight' => 6,
51 );
52
53 $items[] = array('path' => 'admin/settings/netforum/object-names-refresh',
54 'title' => t('Refresh object names'),
55 'callback' => 'netforum_objects_refresh_names',
56 'description' => t('Contact netFORUM and re-populate the list of available objects'),
57 'access' => user_access('administer site configuration'),
58 'weight' => 6,
59 );
60
61 $items[] = array('path' => 'admin/settings/netforum/object-autocomplete',
62 'title' => t('Netforum object name autocomplete'),
63 'callback' => 'netforum_objects_autocomplete',
64 'type' => MENU_CALLBACK,
65 'access' => user_access('administer site configuration'),
66 );
67
68 $items[] = array('path' => 'admin/settings/netforum/xwebtest/params_for',
69 'title' => 'xWeb testing - parameters as JSON',
70 'callback' => 'netforum_test_page_params',
71 'access' => user_access('administer site configuration'),
72 'type' => MENU_CALLBACK,
73 );
74 }
75 else{
76
77 }
78 return $items;
79 }
80
81 /**
82 * Looks for a function name and returns the parameters as JSON. Used via AJAX
83 *
84 * ...
85 * @ingroup pages
86 */
87 function netforum_test_page_params($fname) {
88 drupal_set_header('Content-Type: text/javascript; charset=utf-8');
89 print drupal_to_js(netforum_xweb_function_struct_parameters($fname));
90 exit;
91 }
92
93 /**
94 * Provides a testing page for xWeb requests, useful for examining output
95 *
96 * ...
97 * @ingroup pages
98 * @see netforum_test_form()
99 */
100 function netforum_test_page() {
101 $output = t('Test a request here by selecting the function from the drop down list and filling in the parameters below. The result of the request and debugging information will be shown below.');
102
103
104 //first, get the functions as an array with function names as keys
105 //and an array of function parameters as the value
106 $xweb_functions = array();
107 $function_list = _netforum_xweb_soap_functions();
108 foreach (array_keys($function_list) as $function_name) {
109 $xweb_functions[$function_name] = netforum_xweb_function_parameters($function_name);
110 }
111
112 if (isset($_POST['netforum_request'])) {
113 if (is_array($_POST['netforum_params'])) {
114 $request_parameters = array_filter_recursive($_POST['netforum_params']);
115 }
116 else {
117 $request_parameters = array();
118 }
119
120
121 $request_code = '';
122 $request_code .= "\$arguments = ". var_export($request_parameters, TRUE) .";\n";
123 $request_code .= "\$response = netforum_xweb_request('". $_POST['netforum_request'] ."', \$arguments); ";
124
125 $response = netforum_xweb_request($_POST['netforum_request'], $request_parameters);
126
127 $form_defaults = array();
128 $form_defaults[$_POST['netforum_request']] = $request_parameters;
129 drupal_set_html_head("<script type=\"text/javascript\">
130 var form_defaults = ". drupal_to_js($form_defaults) .";
131 </script>");
132
133 //Overwrite the existing set of parameters (that did shallow inspection) with a deep inspection of the current function
134 $xweb_functions[$_POST['netforum_request']] = netforum_xweb_function_struct_parameters($_POST['netforum_request']);
135
136 }
137
138 //Turn our array of functions and parameters to json for later use
139 drupal_set_html_head("<script type=\"text/javascript\">
140 var xweb_functions = ". drupal_to_js($xweb_functions) .";
141 </script>");
142
143 //this is the later use referenced above. The js here will adjust the form fields
144 //and labels based on the selected function
145 drupal_add_js(drupal_get_path('module', 'netforum') .'/jquery.netforum.js');
146 drupal_add_js('misc/collapse.js');
147
148
149 $output .= drupal_get_form('netforum_test_form', $response, $request_code);
150 return $output;
151 }
152
153 /**
154 * The form for the xWeb testing page
155 *
156 * ...
157 * @ingroup forms
158 * @see netforum_test_page()
159 */
160 function netforum_test_form($response, $request_code = '') {
161
162 $request_options = netforum_xweb_functions();
163 if (count($request_options) == 0) {
164 drupal_set_message("No netFORUM xWeb functions found, perhaps xWeb is currently unavailable?", 'error');
165 }
166 $request_log = '';
167 $request_soap_log = '';
168
169 $request_log = netforum_request_log();
170 $request_soap_log = netforum_soap_log();
171
172 $form['netforum_xweb_request'] = array(
173 '#title' => 'xWeb Request',
174 '#type' => 'fieldset',
175 '#description' => 'Construct the xWeb request'
176 );
177
178 $form['netforum_xweb_request']['netforum_request'] = array(
179 '#type' => 'select',
180 '#title' => t('netFORUM xWeb Function'),
181 '#description' => t('The name of the function to call'),
182 '#default_value' => $form_values['netforum_request'],
183 '#options' => $request_options,
184 );
185
186 $form['netforum_xweb_request']['parameters'] = array(
187 '#type' => 'fieldset',
188 '#attributes' => array('id' => 'parameters'),
189 );
190
191 /*
192 * Normally, there would be inputs for the function here, but
193 * we're relying on some JS and AJAX to build the right form
194 * for the right function
195 */
196
197 $form['netforum_xweb_request']['submit'] = array(
198 '#type' => 'button',
199 '#value' => 'submit',
200 '#name' => 'clickbutton',
201 );
202
203
204 $form['netforum_xweb_results'] = array(
205 '#title' => 'xWeb Results',
206 '#type' => 'fieldset',
207 '#description' => 'Response from xWeb'
208 );
209
210 $form['netforum_xweb_results']['code'] = array(
211 '#title' => 'PHP Code',
212 '#type' => 'item',
213 '#description' => "<pre>". htmlspecialchars($request_code) ."</pre>" ,
214 );
215
216 $form['netforum_xweb_results']['response'] = array(
217 '#title' => 'Response',
218 '#type' => 'item',
219 '#description' => "<pre>". htmlspecialchars(print_r($response, TRUE)) ."</pre>" ,
220 );
221
222 $form['netforum_xweb_results']['soap_log'] = array(
223 '#title' => 'SOAP Log',
224 '#type' => 'item',
225 '#description' => "<pre>". htmlspecialchars($request_soap_log) ."</pre>" ,
226 );
227
228 $form['netforum_xweb_results']['request_log'] = array(
229 '#title' => 'xWebSecureClient Class Log',
230 '#type' => 'item',
231 '#description' => "<pre>". htmlspecialchars($request_log) ."</pre>" ,
232 );
233
234 $form['#submit'] = array(
235 'netforum_test_page' => array(),
236 );
237
238 $form['#redirect'] = FALSE;
239
240 return $form;
241 }
242
243 /**
244 * The Objects page provides a way to search for objects available for queries and view the columns
245 *
246 * ...
247 * @ingroup pages
248 * @see netforum_objects_form()
249 */
250 function netforum_objects_page() {
251 $output = t('Use this page to view columns and column details for netFORUM objects. Each request clears the cache for that object and fetches the details from xWeb. If the object is not found, try ') . l(t('refreshing the object names'), 'admin/settings/netforum/object-names-refresh') .'.';
252 $object_details = array();
253 $obj = new stdClass();
254 $obj->obj_key = $obj->obj_defaultcolumns = $obj->obj_defaultorderby = $obj->obj_name = '';
255
256 if (isset($_POST['netforum_object_name'])) {
257 $name = trim($_POST['netforum_object_name']);
258 $res = db_query("SELECT obj_key, obj_defaultcolumns, obj_defaultorderby, obj_name FROM {netforum_object_cache} WHERE obj_name = '%s' ", $name);
259 if (db_num_rows($res) > 0) {
260 $obj = db_fetch_object($res);
261 db_query("UPDATE {netforum_object_cache} SET data = '' WHERE obj_name='%s' ", $name);
262 $object_details = netforum_object_fields($name);
263 }
264 else {
265 drupal_set_message(t('Object name not found in the cache, perhaps you need to refresh the object names?'), 'error');
266 }
267 }
268
269 $output .= drupal_get_form('netforum_objects_form', $object_details, $obj);
270
271 return $output;
272 }
273
274 /**
275 * The form for the objects page
276 *
277 * ...
278 * @ingroup forms
279 * @see netforum_objects_page()
280 */
281 function netforum_objects_form($object_details = array(), $obj) {
282
283 $search_on = module_invoke_all('netforum_node_obj_search_on', $obj->obj_name);
284 $order_by = module_invoke_all('netforum_node_obj_order_by', $obj->obj_name);
285
286 if (count($search_on) == 0) {
287 //If the default columns are * or empty, try setting it to the default order by
288 if ( ! isset($obj->obj_defaultcolumns) || $obj->obj_defaultcolumns == "*" || trim($obj->obj_defaultcolumns) == '' ) {
289 $search_on = $obj->obj_defaultorderby;
290 }
291 else {
292 $search_on = $obj->obj_defaultcolumns;
293 }
294 }
295 else{
296 $search_on = implode(",", array_keys($search_on));
297 }
298
299 if (count($order_by) == 0) {
300 $order_by = $obj->obj_defaultorderby;
301 }
302 else {
303 $order_by = implode(",", $order_by);
304 }
305
306 $form['netforum_object_name'] = array(
307 '#type' => 'textfield',
308 '#title' => t('Object Name'),
309 '#size' => 50,
310 '#autocomplete_path' => 'admin/settings/netforum/object-autocomplete',
311 );
312
313 $form['submit'] = array(
314 '#type' => 'button',
315 '#value' => 'Fetch / refresh object details',
316 '#name' => 'clickbutton',
317 );
318
319
320 $form['netforum_object_details'] = array(
321 '#title' => 'Object details',
322 '#type' => 'fieldset',
323 '#description' => 'Refreshed object details from xWeb'
324 );
325
326 $form['netforum_object_details']['key'] = array(
327 '#title' => 'Object Key',
328 '#type' => 'item',
329 '#description' => $obj->obj_key ,
330 );
331
332 $form['netforum_object_details']['searchcolumns'] = array(
333 '#title' => 'Object Search Columns',
334 '#type' => 'item',
335 '#description' => $search_on,
336 );
337
338 $form['netforum_object_details']['orderby'] = array(
339 '#title' => 'Object Order By',
340 '#type' => 'item',
341 '#description' => $order_by ,
342 );
343
344 $form['netforum_object_details']['keyfield'] = array(
345 '#title' => 'Key Field',
346 '#type' => 'item',
347 '#description' => netforum_object_key_field($obj->obj_name),
348 );
349
350 $form['netforum_object_details']['response'] = array(
351 '#title' => 'Object Fields',
352 '#type' => 'item',
353 '#description' => "<pre>". htmlspecialchars(print_r($object_details, TRUE)) ."</pre>" ,
354 );
355
356 return $form;
357 }
358
359
360 function netforum_objects_autocomplete($search="") {
361 drupal_set_header('Content-Type: text/javascript; charset=utf-8');
362 $obj_names = array();
363 if ($search != "") {
364 $results = db_query("SELECT obj_name FROM {netforum_object_cache} WHERE obj_name like '%s%%' OR obj_prefix like '%s%%' ORDER BY obj_name", $search, $search);
365 while ($obj = db_fetch_object($results)) {
366 $obj_names[$obj->obj_name] = check_plain($obj->obj_name);
367 }
368 }
369 print drupal_to_js($obj_names);
370 exit;
371 }
372
373 /**
374 * Technically a page, this will refresh the cached list of objects and redirect the user to
375 * the object info page
376 *
377 * ...
378 * @ingroup pages
379 */
380 function netforum_objects_refresh_names() {
381 netforum_refresh_object_names();
382 drupal_set_message(t('netFORUM object names refreshed'));
383 drupal_goto('admin/settings/netforum/object-info');
384 }
385
386
387 /**
388 * The settings form for netFORUM Secure
389 *
390 * @ingroup forms
391 * @see netforum_admin_settings_validate
392 */
393 function netforum_admin_settings() {
394
395 $form['xWeb'] = array(
396 '#title' => "xWeb connection",
397 '#type' => 'fieldset',
398 );
399
400 $form['xWeb']['netforum_wsdl_url'] = array(
401 '#type' => 'textfield',
402 '#title' => t('Netforum WSDL URL'),
403 '#description' => t('xWeb endpoint, commonly https://example.com/xweb/Secure/netFORUMXML.asmx?WSDL'),
404 '#default_value' => variable_get('netforum_wsdl_url', 'https://'),
405 '#required' => TRUE,
406 '#size' => 50,
407 );
408
409 $form['xWeb']['netforum_xweb_username'] = array(
410 '#type' => 'textfield',
411 '#title' => t('xWeb User Name'),
412 '#description' => t('This is the username that is used to authenticate with netFORUM and gain access'),
413 '#default_value' => variable_get('netforum_xweb_username', ''),
414 '#size' => 16
415 );
416
417 $form['xWeb']['netforum_xweb_password'] = array(
418 '#type' => 'password',
419 '#title' => t('xWeb User Password'),
420 '#description' => t('The password to go with the xweb user, commonly requires manually setting usr_pwd in fw_user'),
421 '#default_value' => variable_get('netforum_xweb_password', ''),
422 '#size' => 16
423 );
424
425 $form['xWeb']['netforum_hide_error_messages'] = array(
426 '#type' => 'checkbox',
427 '#title' => t('Hide connection error messages'),
428 '#description' => t('Should regular visitors be able to see when the connection to netFORUM is not available? Site administrators will always see connection errors.'),
429 '#default_value' => variable_get('netforum_hide_error_messages', 0),
430 );
431
432 $form['cache'] = array(
433 '#title' => "xWeb caching",
434 '#type' => 'fieldset',
435 );
436
437 $form['cache']['netforum_cache_default'] = array(
438 '#type' => 'textfield',
439 '#title' => t('Cache Length'),
440 '#description' => t('How long should a response be used before fetching new values from xWeb'),
441 '#default_value' => variable_get('netforum_cache_default', '1 hour'),
442 '#size' => 16
443 );
444
445 $form['cache']['netforum_cache_max'] = array(
446 '#type' => 'textfield',
447 '#title' => t('Maximum Cache Length'),
448 '#description' => t('If the first request fails how far back in the cache should we search before returning empty data?'),
449 '#default_value' => variable_get('netforum_cache_max', '4 days'),
450 '#size' => 16
451 );
452
453 $form['availability'] = array(
454 '#title' => "xWeb availability",
455 '#type' => 'fieldset',
456 );
457
458 $form['availability']['netforum_slow_query_limit'] = array(
459 '#type' => 'textfield',
460 '#title' => t('Slow query limit'),
461 '#description' => t('Any xWeb requests over this time limit are marked with warnings in the system logs.'),
462 '#after_field' => t('seconds'),
463 '#default_value' => variable_get('netforum_slow_query_limit', '1.5'),
464 '#field_suffix' => t('seconds'),
465 '#size' => 4
466 );
467
468
469 $form['availability']['netforum_verify_timeout'] = array(
470 '#type' => 'textfield',
471 '#title' => t('xWeb timeout'),
472 '#description' => t('If netFORUM cannot be contacted in this time it is logged as an error'),
473 '#field_suffix' => t('seconds'),
474 '#default_value' => variable_get('netforum_verify_timeout', 5),
475 '#size' => 4
476 );
477
478 $form['availability']['netforum_verify_availability'] = array(
479 '#type' => 'checkbox',
480 '#title' => t('Verify xWeb availability before requests'),
481 '#description' => t('Verify that netFORUM is available before issuing requests by trying to open a connection to the server.'),
482 '#default_value' => variable_get('netforum_verify_availability', 1),
483 );
484
485 $form['availability']['netforum_verify_length'] = array(
486 '#type' => 'textfield',
487 '#title' => t('xWeb verification length'),
488 '#description' => t('This sets how frequently netFORUM should be checked for availability. Think of it as the time between verification checks. There is overhead associated with this, so anything less than 2 minutes might not be efficient. Enter zero to disable and check before every request.'),
489 '#field_suffix' => t('minutes'),
490 '#default_value' => variable_get('netforum_verify_length', 3),
491 '#size' => 4
492 );
493
494 $form['#validate'] = array(
495 'netforum_admin_settings_validate' => array()
496 );
497
498 return system_settings_form($form);
499 }
500
501 /**
502 * Validating the system settings form
503 *
504 * @see netforum_admin_settings
505 */
506 function netforum_admin_settings_validate($form_id, $form_values, $form) {
507 $clear_to_connect = TRUE;
508
509 if ( ! valid_url($form_values['netforum_wsdl_url'], TRUE)) {
510 form_set_error('netforum_wsdl_url', t('Please enter a valid wsdl url for access'));
511 $clear_to_connect = FALSE;
512 }
513
514 if ( !isset($form_values['netforum_xweb_username']) || trim($form_values['netforum_xweb_username']) == '') {
515 form_set_error('netforum_xweb_username', t('Please enter a username'));
516 $clear_to_connect = FALSE;
517 }
518
519 if ( !isset($form_values['netforum_xweb_password']) || trim($form_values['netforum_xweb_password']) == '') {
520 $old_password = variable_get('netforum_xweb_password', FALSE);
521 if ($old_password) {
522 form_set_value($form['xWeb']['netforum_xweb_password'], $old_password);
523 $form_values['netforum_xweb_password'] = $old_password;
524 }
525 else {
526 form_set_error('netforum_xweb_password', t('Please enter a password'));
527 $clear_to_connect = FALSE;
528 }
529 }
530
531 if (isset($form_values['netforum_cache_default'])) {
532 if (substr(trim($form_values['netforum_cache_default']), 0, 1) == '-') {
533 form_set_error('netforum_cache_default', t('The default cache cannot be a negative amount of time.'));
534 }
535 elseif (strtotime($form_values['netforum_cache_default']) === FALSE || strtotime($form_values['netforum_cache_default']) == -1 ) {
536 form_set_error('netforum_cache_default', t('The time format was not recognized, please enter something in the format of 4 days or 1 hour'));
537 }
538 }
539
540 if (isset($form_values['netforum_cache_max'])) {
541 if (substr(trim($form_values['netforum_cache_max']), 0, 1) == '-') {
542 form_set_error('netforum_cache_max', t('The default cache cannot be a negative amount of time.'));
543 }
544 elseif (strtotime($form_values['netforum_cache_max']) === FALSE || strtotime($form_values['netforum_cache_max']) == -1 ) {
545 form_set_error('netforum_cache_max', t('The time format was not recognized, please enter something in the format of 4 days or 1 hour'));
546 }
547 }
548
549 if (isset($form_values['netforum_wsdl_url']) && stristr($form_values['netforum_wsdl_url'], 'netForumXMLOnDemand.asmx')) {
550 variable_set('netforum_xweb_od', 1);
551 }
552 else {
553 variable_set('netforum_xweb_od', 0);
554 }
555
556 if ($clear_to_connect === TRUE) {
557 if ($form_values['netforum_verify_availability'] == 1 && netforum_is_available() === FALSE) {
558 drupal_set_message(t('Could not contact netFORUM server'), 'error');
559 return;
560 }
561 _netforum_include_xweb();
562 try {
563 $error_level = error_reporting(0); //ignore the errors, only catch exceptions
564 $verify_timeout = $form_values['netforum_verify_timeout'];
565 if ($verify_length != 0) {
566 ini_set('default_socket_timeout', $verify_timeout);
567 }
568 $nfh = new xwebSecureClient($form_values['netforum_wsdl_url'],
569 Array('trace' => TRUE,
570 'exceptions' => TRUE,
571 'cache_wsdl' => TRUE,
572 'xwebUserName' => $form_values['netforum_xweb_username'],
573 'xwebUserPass' => $form_values['netforum_xweb_password']));
574 $response = $nfh->GetFacadeXMLSchema(array('szObjectName' => 'Customer'));
575 if ($response) {
576 drupal_set_message(t('Successfully connected to netFORUM server and retreived data'), "status");
577 }
578 error_reporting($error_level);
579 }
580 catch(Exception $exception) {
581 form_set_error('form_token', t('Could not connect to netFORUM server with specified parameters.<br> @exception_message .', array('@exception_message' => $exception->getMessage())) );
582 $wsdl_url_guess = _netforum_guess_xweb_url($form_values['netforum_wsdl_url']);
583 if (strtolower($form_values['netforum_wsdl_url']) != strtolower($wsdl_url_guess)) {
584 drupal_set_message(t('Perhaps the following WSDL endpoint would work better:<br> @wsdl_url_guess', array('@wsdl_url_guess' => $wsdl_url_guess)), "status");
585 }
586 }
587 }
588 }
589
590 /**
591 * Implementation of hook_cron()
592 */
593 function netforum_cron() {
594 $cutoffdate = strftime("%Y-%m-%d %H:%M", strtotime("-". variable_get('netforum_cache_max', '10 days')));
595 db_query("DELETE FROM {netforum_request_cache} WHERE add_date <= '$cutoffdate';");
596 }
597
598 /**
599 * Verify that netforum is aailable for queries.
600 *
601 * This stores the result in a global varaible to prevent repetitive checks in the same request.
602 * If the admin setting verify availability is unchecked then this always returns TRUE
603 *
604 * @return
605 * Boolean TRUE or FALSE
606 */
607 function netforum_is_available() {
608 global $_netforum_is_available;
609
610 if (variable_get('netforum_verify_availability', 1) == 0) {
611 return TRUE;
612 }
613
614 if (isset($_netforum_is_available)) {
615 return $_netforum_is_available;
616 }
617
618 $verify_length = variable_get('netforum_verify_length', 3);
619 if ($verify_length != 0) {
620 $cutoff = strtotime("-". $verify_length ." minutes");
621 $last_result = variable_get('netforum_netforum_is_available', array(NULL, NULL));
622 if ( is_null($last_result[0]) === FALSE && $last_result[1] >= $cutoff ) {
623 return $last_result[0];
624 }
625 }
626 //close down all warnings, because if it does fail we don't want it to show
627 $error_level = error_reporting(0);
628
629 $address = variable_get('netforum_wsdl_url', NULL);
630 if ($address == NULL) {
631 $_netforum_is_available = FALSE;
632 }
633 else {
634 $opts = array('http' => array('timeout' => variable_get('netforum_verify_timeout', 5)), 'https' => array('timeout' => variable_get('netforum_verify_timeout', 5)));
635 $address_parts = explode('/', $address);
636 $server = $address_parts[2];
637 $fp = fsockopen($server, 80, $errno, $errstr, variable_get('netforum_verify_timeout', 5));
638 if ($fp) {
639 $_netforum_is_available = TRUE;
640 }
641 else {
642 $_netforum_is_available = FALSE;
643 watchdog('netforum', t("Could not connect to netFORUM server %server - %error", array('%server' => $server, '%error' => $errstr) ), WATCHDOG_ERROR, l(t('settings'), 'admin/settings/netforum/connection'));
644 }
645 }
646
647 //restore the error reporting level
648 error_reporting($error_level);
649
650 if ($verify_length != 0) {
651 variable_set('netforum_netforum_is_available', array($_netforum_is_available, time()));
652 }
653
654 return $_netforum_is_available;
655 }
656
657 /**
658 * Checks to see if it is netFORUM Enterprise
659 *
660 * @return
661 * Boolean TRUE if enterprise, FALSE otherwise
662 */
663 function netforum_is_enterprise() {
664 return (variable_get('netforum_xweb_od', 0) == 0);
665 }
666
667 /**
668 * Checks to see if it is netFORUM Team or Pro
669 *
670 * @return
671 * Boolean TRUE if Team or Pro, FALSE otherwise
672 */
673 function netforum_is_team() {
674 return (variable_get('netforum_xweb_od', 0) == 1);
675 }
676
677 function _netforum_include_xweb() {
678 $path = drupal_get_path('module', 'netforum');
679 if (netforum_is_enterprise()) {
680 require_once($path .'/xwebSecureClient.class.inc');
681 }
682 else {
683 require_once($path .'/xwebSecureOD.class.inc');
684 }
685 }
686
687 /**
688 * Connect to xWeb and return a copy of the global SoapClient handler.
689 * If the server is not available set it to only check the local cache.
690 */
691 function netforum_connect_xweb() {
692
693 _netforum_include_xweb();
694 global $_netforum_xweb_handler;
695 if (!isset($_netforum_xweb_handler)) {
696 try{
697 $error_level = error_reporting(0); //ignore the errors, only catch exceptions
698 $verify_timeout = variable_get('netforum_verify_timeout', 5);
699 if ($verify_timeout != 0) {
700 ini_set('default_socket_timeout', $verify_timeout);
701 }
702 $_netforum_xweb_handler = new xwebSecureClient(variable_get('netforum_wsdl_url', NULL),
703 Array('trace' => TRUE,
704 'exceptions' => TRUE,
705 'cache_wsdl' => TRUE,
706 'xwebUserName' => variable_get('netforum_xweb_username', NULL),
707 'xwebUserPass' => variable_get('netforum_xweb_password', NULL), )
708 );
709 $_netforum_xweb_handler->setCaching(variable_get('netforum_cache_default', NULL));
710 error_reporting($error_level);
711 }
712 catch(Exception $exception) {
713 if (variable_get('netforum_hide_error_messages', 0) == 0 || user_access('administer site configuration')) {
714 drupal_set_message(t('Could not connect to netFORUM server, please verify connection settings'), 'error');
715 }
716 }
717 }
718
719 if ( isset($_netforum_xweb_handler) ) {
720 if (netforum_is_available() === FALSE) {
721 $_netforum_xweb_handler->enableOfflineMode();
722 }
723 else {
724 $_netforum_xweb_handler->disableOfflineMode();
725 }
726 }
727
728 return $_netforum_xweb_handler;
729 }
730
731 /**
732 * Issue a request to xWeb and return the result, if any
733 *
734 * If $cache_max is not set, it recurses with $cache_max set to the admin set option. If $cache_max is set it only makes one attempt.
735 *
736 * @param $fname
737 * A string containing the name of the xWeb function such as GetQuery or InsertFacadeObject; case sensitive
738 * @param $arguments
739 * An array with keys set to parameter names and values set to the data. For example, Array('szObjectName'=>'Individual').
740 * @param $cache_max
741 * A string parseable to a time that indicates how far back in the cache to search. For example, '1 day'
742 * @return
743 * Bare results, a SimpleXML Object, or NULL if no results are found
744 *
745 * @see netforum_xweb_functions
746 * @see netforum_xweb_function_parameters
747 * @see netforum_xweb_function_struct_parameters
748 * @see netforum_request_log
749 * @see netforum_soap_log
750 * @see netforum_ind_info_parameters
751 * @see netforum_org_info_parameters
752 * @see netforum_facade_object_parameters
753 */
754 function netforum_xweb_request($fname, $arguments = array(), $cache_max = NULL) {
755 $start_time = microtime();
756 $nfh = netforum_connect_xweb();
757
758 if ( ! $nfh) {
759 watchdog('netforum', t("Received xWeb request for !fname but could not connect to xWeb", array('!fname' => $fname)), WATCHDOG_ERROR);
760 return NULL;
761 }
762
763 $xml_object = NULL;
764 $results = NULL;
765
766 if (isset($cache_max)) {
767 $nfh->setCaching($cache_max);
768 }
769
770 //we're going to turn off errors for a moment here. If there is an SSL error the first time, I don't
771 //want to display it. We can throw our own thank you.
772 $error_level = error_reporting(0);
773
774 try{
775 $results = $nfh->{$fname}($arguments);
776 if ( !isset($results)) {
777 throw new Exception('No results returned from xWeb Query');
778 }
779 }
780 catch(Exception $e) {
781 if (isset($cache_max)) { //this implies that we went even further back in the cache and could not connect, so call it quits
782 if (variable_get('netforum_hide_error_messages', 0) == 0 || user_access('administer site configuration')) {
783 drupal_set_message(t('Could not retreive data from server or cache.'), 'error');
784 }
785 $nfh->setCaching(variable_get('netforum_cache_default', '4 hours'));
786 if ($fname == 'GetQuery') {
787 watchdog('netforum', t("Could not get response to !fname request for !columns from !object: @fault", array('!fname' => $fname, '@fault' => $e->faultstring, '!columns' => $arguments['szColumnList'], '!object' => $arguments['szObjectName'])), WATCHDOG_ERROR);
788 }
789 else {
790 watchdog('netforum', t("Could not get response to !fname request: @fault", array('!fname' => $fname, '@fault' => $e->faultstring)), WATCHDOG_ERROR);
791 }
792 }
793 else {
794 //recurse if we hit one snag and try it one more time
795 watchdog('netforum', t("First xWeb request for !fname did not return any arguments", array('!fname' => $fname)), WATCHDOG_WARNING);
796 $results = netforum_xweb_request($fname, $arguments, variable_get('netforum_cache_max', '4 days'));
797 }
798 }
799
800 if (isset($results)) {
801 $fname_result = $fname ."Result";
802 //the following is a test for a response containing yet more xml. If it is a simple request, like WebLogin then the result
803 //is workable as a simpleobject and does not need to be parsed further.
804 if (isset($results->{$fname_result}->any) && $fname != "GetFacadeXMLSchema") {
805 $results = simplexml_load_string($results->{$fname_result}->any, "SimpleXMLElement", LIBXML_NOERROR+LIBXML_NOWARNING);
806 }
807
808 if ($nfh->cachedLastResponse() == 1) {
809 $source = t("from database cache");
810 }
811 else {
812 $source = t("directly from xWeb");
813 }
814 $elapsed_time = microtime()-$start_time;
815 $max_time = (float)variable_get('netforum_slow_query_limit', '1.5');
816 if ($max_time != 0 && $elapsed_time > $max_time) {
817 watchdog('netforum', t("Slow returning response to xWeb query !fname !source in !elapsed_time seconds, parameters: <br> @arguments", array('!fname' => $fname, '!source' => $source, '!elapsed_time' => $elapsed_time, '@arguments' => print_r($arguments, TRUE))), WATCHDOG_WARNING);
818 }
819 }
820
821 //restore the error reporting level
822 error_reporting($error_level);
823
824 return $results;
825
826 }
827
828 /**
829 * Find out if the last request came from the database
830 *
831 * @return
832 * TRUE if the last xWeb request came from the local database cache, FALSE otherwise
833 */
834 function netforum_response_from_cache() {
835 $nfh = netforum_connect_xweb();
836 if (!$nfh) {
837 return;
838 }
839 return $nfh->lastResponseFromCache();
840 }
841
842 /**
843 * Return a list of fields for a given object
844 *
845 * By default this will return the cached object info. If the object info does not exist or is empty, then it will issue a request to
846 * xWeb, parse, store, and return the results. The object name must exist in the database in order to return the fields.
847 *
848 * @param $obj_name
849 * A string representing the object name. For example, Individual or EventsRegistrant
850 * @return
851 * A hashed array with the keys set to the column names and the values set to the column description
852 */
853 function netforum_object_fields($obj_name) {
854 static $seen_objects = array();
855 $obj_fields = array();
856
857 if ( !isset($obj_name) || $obj_name == '') {
858 return $obj_fields;
859 }
860
861 if (isset($seen_objects[$obj_name])) {
862 return $seen_objects[$obj_name];
863 }
864
865 $cached_result = db_query("SELECT data, obj_prefix, obj_defaultcolumns, obj_defaultorderby FROM {netforum_object_cache} WHERE obj_name = '%s' ", $obj_name);
866 if (db_num_rows($cached_result) > 0) {
867 $cached_obj = db_fetch_object($cached_result);
868 if ($cached_obj->data != '') {
869 $obj_fields = unserialize($cached_obj->data);
870 $seen_objects[$obj_name] = $obj_fields;
871 return $obj_fields;
872 }
873 }
874 else {
875 watchdog('netforum', t('Could not find object %objectname in list of netFORUM objects, perhaps the the list of objects needs to be refreshed?', array('%objectname' => $obj_name)), WATCHDOG_WARNING, l(t('refresh object list'), 'admin/settings/netforum/object-names-refresh'));
876 return $obj_fields;
877 }
878
879 set_time_limit(90);
880 $updated_object_data = array();
881 if (netforum_is_enterprise() ) {
882 $arguments = array(
883 'szObjectName' => "Object",
884 'szColumnList' => "obj_prefix, obj_defaultcolumns, obj_defaultorderby",
885 'szWhereClause' => "obj_name = '$obj_name'",
886 'szOrderBy' => "",
887 );
888 $response = netforum_xweb_request('GetQuery', $arguments);
889 if ($response) {
890 $obj = $response->ObjectObject;
891 $updated_object_data['obj_prefix'] = strval($obj->obj_prefix);
892 $updated_object_data['obj_defaultcolumns'] = strval($obj->obj_defaultcolumns);
893 $updated_object_data['obj_defaultorderby'] = strval($obj->obj_defaultorderby);
894 }
895 }
896
897 $response = netforum_xweb_request('GetFacadeXMLSchema', array('szObjectName' => $obj_name));
898 if (is_null($response)) {
899 return $obj_fields;
900 }
901
902 // so this is not the greatest way to parse out the object descriptions, but it's a way, and it works. At least it only goes through each node once,
903 // even if it does wade through several thousand of them for the large objects.
904 $p = xml_parser_create();
905 xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, 0);
906 xml_parser_set_option($p, XML_OPTION_SKIP_WHITE, 1);
907 xml_parse_into_struct($p, $response->GetFacadeXMLSchemaResult->any, $vals);
908 xml_parser_free($p);
909
910 foreach ($vals as $node) {
911 if (isset($node['tag']) && $node['tag'] == 'xsd:group' && $node['type'] == 'open') {
912 $category = $node['attributes']['name'];
913 $level = $node['level'];
914 $found_close = FALSE;
915 while ($found_close === FALSE) {
916 $node = next($vals);
917 if ($node['tag'] && $node['tag'] == 'xsd:element' && $node['type'] != 'close' && isset($node['attributes']['ref'])) {
918 $col = $node['attributes']['ref'];
919 if (stristr($col, '_')) {
920 $obj_fields[$col] = $category ."::";
921 }
922 else {
923 continue;
924 }
925 }
926 elseif ($node['tag'] && $node['tag'] == 'xsd:group' && $node['type'] == 'close' && $node['level'] == $level) {
927 $found_close = TRUE;
928 }
929 }
930 }
931 if (isset($node['tag']) && $node['tag'] == 'xsd:element' && $node['type'] == 'open' && isset($node['attributes']) && isset($node['attributes']['name'])) {
932 $col = $node['attributes']['name'];
933 if (array_key_exists($col, $obj_fields) === FALSE) {
934 continue;
935 }
936 $level = $node['level'];
937 $found_close = FALSE;
938 while ($found_close === FALSE) {
939 $node = next($vals);
940 if ($node['tag'] && $node['tag'] == 'xsd:documentation') {
941 $obj_fields[$col] .= trim($node['value'], '.');
942 }
943 elseif ($node['tag'] && $node['tag'] == 'xsd:element' && $node['type'] == 'close' && $node['level'] == $level) {
944 $found_close = TRUE;
945 }
946 }
947 }
948 }
949
950
951 $update_query = "UPDATE {netforum_object_cache} SET data='%s', ";
952 $update_values = array(serialize($obj_fields));
953 foreach($updated_object_data as $k => $v) {
954 $update_query .= $k ." = '%s', ";
955 $update_values[] = $v;
956 }
957
958 $update_query = rtrim($update_query, ", ");
959 $update_query .= " WHERE obj_name = '%s'";
960 $update_values[] = $obj_name;
961 db_query($update_query, $update_values);
962
963 $seen_objects[$obj_name] = $obj_fields;
964
965 return $obj_fields;
966 }
967
968 /**
969 * Fetch a list of Objects from netForum and stores any new object names and keys in the cache
970 *
971 * netFORUM OnDemand doesn't have many of the features needed, so the names for on demand are loaded from a static file
972 */
973 function netforum_refresh_object_names() {
974
975 if (netforum_is_team()) {
976 //Individual, Organization, Address, and Customer are the only allowed objects for us, calling GetFacadeXMLSchema on anything else is a nogo.
977 db_query("INSERT INTO {netforum_object_cache} (obj_name, obj_key, obj_prefix, obj_defaultcolumns, obj_defaultorderby, data, obj_key_field) VALUES ('Individual','f41b6e06-299b-4022-be6f-0641ba87de59','ind','ind_last_name,ind_first_name','ind_last_name,ind_first_name',NULL,NULL); " );
978 db_query("INSERT INTO {netforum_object_cache} (obj_name, obj_key, obj_prefix, obj_defaultcolumns, obj_defaultorderby, data, obj_key_field) VALUES ('Organization','9c0100ce-f6c2-4b42-aff2-1c065f3734d9','org','org_name, org_acronym','org_name',NULL,NULL); " );
979 db_query("INSERT INTO {netforum_object_cache} (obj_name, obj_key, obj_prefix, obj_defaultcolumns, obj_defaultorderby, data, obj_key_field) VALUES ('Address','0574b4d5-853b-48a9-8b64-8b424b6658bd','adr','*','adr_city',NULL,NULL); " );
980 db_query("INSERT INTO {netforum_object_cache} (obj_name, obj_key, obj_prefix, obj_defaultcolumns, obj_defaultorderby, data, obj_key_field) VALUES ('Customer','65f0eb73-0df7-4196-b2ff-11b61f10e532','cst','cst_type,cst_name_cp','cst_sort_name_dn',NULL,NULL); " );
981 return;
982 }
983
984 $empty_obj_struct = array('obj_key' => '', 'obj_name' => '', 'obj_prefix' => '', 'obj_defaultcolumns' => '', 'obj_defaultorderby' => '');
985 $columns = array_keys($empty_obj_struct);
986
987 $arguments = array(
988 'szObjectName' => "Object @TOP -1",
989 'szColumnList' => implode(", ", $columns),
990 'szWhereClause' => "obj_prefix is not NULL and obj_delete_flag = 0",
991 'szOrderBy' => "",
992 );
993 $response = netforum_xweb_request('GetQuery', $arguments);
994
995 $partial_data = FALSE;
996 if (is_null($response) || $response->attributes()->recordReturn < 400) {
997 // If we can't use the @TOP directive to override the maximum records returned in the query limit, try it the slow way.
998 // according to the avectra wiki, @TOP was added in 2006.01 build
999 $response = netforum_xweb_request('GetFacadeObjectList');
1000 $partial_data = TRUE;
1001 }
1002 if ($response) {
1003 $db_objects = array();
1004 $to_add = array();
1005 $to_update = array();
1006 $db_result = db_query("SELECT ". implode(", ", $columns) ." FROM {netforum_object_cache}");
1007 while ($obj = db_fetch_object($db_result)) {
1008 $db_objects[(string)$obj->obj_key] = $obj;
1009 }
1010 if ($partial_data) {
1011 foreach ($response->ObjectObject as $obj) {
1012 $object_name = (string)$obj->obj_name;
1013 $object_key = (string)$obj->obj_key;
1014 if (array_key_exists($object_key, $db_objects)) {
1015 if ($db_objects[$object_key]->obj_name != $object_name) {
1016 $to_update[$object_key] = $empty_obj_struct;
1017 $to_update[$object_key]['obj_name'] = $object_name;
1018 }
1019 }
1020 else {
1021 $to_add[$object_key] = $empty_obj_struct;
1022 $to_add[$object_key]['obj_name'] = $object_name;
1023 $to_add[$object_key]['obj_key'] = $object_key;
1024 }
1025 }
1026 }
1027 else { //if we got all the data we want, do the same logic but with more data!
1028 foreach ($response->ObjectObject as $obj) {
1029 $object_key = (string)$obj->obj_key;
1030 if (array_key_exists($object_key, $db_objects)) {
1031 foreach ($columns as $obj_col) {
1032 $obj_val = (string)$obj->{$obj_col};
1033 if ($db_objects[$object_key]->{$obj_col} != $obj_val) {
1034 if (!isset($to_update[$object_key])) {
1035 $to_update[$object_key] = array();
1036 }
1037 $to_update[$object_key][$obj_col] = $obj_val;
1038 }
1039 }
1040 }
1041 else {
1042 $to_add[$object_key] = $empty_obj_struct;
1043 foreach ($columns as $obj_col) {
1044 $obj_val = (string)$obj->{$obj_col};
1045 $to_add[$object_key][$obj_col] = $obj_val;
1046 }
1047 }
1048 }
1049 }
1050
1051 foreach ($to_add as $obj_key => $obj) {
1052 $query = "INSERT INTO {netforum_object_cache} (". implode(", ", array_keys($obj)) .") ";
1053 $query .= " VALUES(". implode(", ", array_pad(array(), count($obj), "'%s'")) .");";
1054 db_query($query, array_values($obj) );
1055 }
1056
1057 foreach ($to_update as $obj_key => $obj) {
1058 $updates = array();
1059 $values = array();
1060 foreach ($obj as $k => $v) {
1061 $updates[] = "$k = '%s'";
1062 $values[] = $v;
1063 }
1064 $values[] = $obj_key;
1065 db_query("UPDATE {netforum_object_cache} SET ". implode(", ", $updates) ." WHERE obj_key = '%s' ;", $values );
1066 }
1067 }
1068 }
1069
1070 /**
1071 * Find the name of an object when given the key
1072 *
1073 * @param $obj_key
1074 * String with the object key
1075 * @return
1076 * String with the object name if found, empty string otherwise
1077 */
1078 function netforum_object_name($obj_key) {
1079 static $name_cache = array();
1080 $obj_key = (string)$obj_key;
1081 if ( netforum_is_valid_guid($obj_key) == FALSE ) {
1082 return '';
1083 }
1084 if (array_key_exists($obj_key, $name_cache)) {
1085 return $name_cache[$obj_key];
1086 }
1087 else {
1088 $res = db_query("SELECT obj_name FROM {netforum_object_cache} WHERE obj_key = '%s' ", $obj_key);
1089 if (db_num_rows($res) > 0) {
1090 $obj = db_fetch_object($res);
1091 $name_cache[$obj_key] = $obj->obj_name;
1092 return $obj->obj_name;
1093 }
1094 else{
1095 return '';
1096 }
1097 }
1098 }
1099
1100 /**
1101 * Find the key of an object when given the name
1102 *
1103 * @param $obj_name
1104 * String with the object name
1105 * @return
1106 * String with the object key if found, FALSE otherwise
1107 */
1108 function netforum_object_key($obj_name) {
1109 static $key_cache = array();
1110
1111 if (netforum_is_valid_guid($obj_name)) {
1112 return $obj_name;
1113 }
1114
1115 if ( array_key_exists($obj_name, $key_cache)) {
1116 return $key_cache[$obj_name];
1117 }
1118 else {
1119 $res = db_query("SELECT obj_key FROM {netforum_object_cache} WHERE obj_name = '%s' ", $obj_name);
1120 if (db_num_rows($res) > 0) {
1121 $obj = db_fetch_object($res);
1122 $key_cache[$obj_name] = $obj->obj_key;
1123 return $obj->obj_key;
1124 }
1125 else{
1126 return FALSE;
1127 }
1128 }
1129 }
1130
1131 /**