Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
hipo_herder.inc
1 <?php
17 require_once SQ_SYSTEM_ROOT.'/core/hipo/hipo_job.inc';
18 
28 {
29 
33  var $_jobs = Array();
34 
38  var $_queued_jobs = Array();
39 
44  var $ho = NULL;
45 
50  var $threshold = 0;
51 
52 
59  function HIPO_Herder()
60  {
61 
62  }//end constructor
63 
64 
71  function &getBackendOutputter()
72  {
73  if (!isset($this->ho) || get_class($this->ho) != 'Hipo_Backend_Outputter') {
74  require_once SQ_SYSTEM_ROOT.'/core/hipo/hipo_backend_outputter.inc';
75  $this->ho = new Hipo_Backend_Outputter();
76  }
77  return $this->ho;
78 
79  }//end getBackendOutputter()
80 
81 
92  function &getJob($code_name)
93  {
94  if (empty($this->_jobs[$code_name])) {
95 
96  // we need to get the hipo_dir from the db so we know
97  // where to look for the include file
98  $db = MatrixDAL::getDb();
99 
100  try {
101  $bind_vars['code_name'] = $code_name;
102  $hipo_vars_all = MatrixDAL::executeAssoc('core', 'getHipoJobByCodeName', $bind_vars);
103  $hipo_vars = Array();
104  if (!empty($hipo_vars_all) && isset($hipo_vars_all[0])) {
105  $hipo_vars = $hipo_vars_all[0];
106  }
107  } catch (Exception $e) {
108  throw new Exception('Unable to get HIPO job from code name: '.$code_name.$e->getMessage());
109  }//end try catch
110 
111  if (!isset($hipo_vars['job_type']) || empty($hipo_vars['job_type'])) {
112  $null = NULL;
113  return $null;
114  }
115  $job_type = strtolower($hipo_vars['job_type']);
116 
117  $vars = unserialize($hipo_vars['hipo_vars']);
118  if (empty($vars['job_dir'])) {
119  $job_dir = SQ_SYSTEM_ROOT.'/core/hipo/jobs';
120  } else {
121  $job_dir = $vars['job_dir'];
122  }
123 
124  $this->_includeHipoJobLocales($job_dir);
125 
126  require_once $job_dir.'/'.$job_type.'.inc';
127 
128  $this->_jobs[$code_name] = new $job_type($code_name);
129 
130  if (!$this->_jobs[$code_name]->code_name) {
131  // something broke, not our problem
132  $null = NULL;
133  return $null;
134  }
135 
136  }//end if
137 
138  return $this->_jobs[$code_name];
139 
140  }//end getJob()
141 
142 
157  public function uncacheJob($code_name)
158  {
159  $job = $this->getJob($code_name);
160  if ($job === NULL) {
161  return FALSE;
162  }
163 
164  unset($this->_jobs[$code_name]);
165  return TRUE;
166 
167  }//end uncacheJob()
168 
169 
181  function getProcessURL($code_name)
182  {
183  $hipo = $this->getJob($code_name);
184  if (is_null($hipo)) return '';
185 
186  if ($hipo->complete()) {
187  // this HIPO is finsihed
188  return $hipo->getOption('on_complete_url');
189  } else {
190  return current_protocol().'://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?SQ_ACTION=hipo&hipo_source_code_name='.$hipo->source_code_name;
191  }
192 
193  }//end getProcessURL()
194 
195 
202  function processWeb()
203  {
204  if (empty($_GET['hipo_source_code_name'])) {
205  trigger_localised_error('HIPO0058', E_USER_WARNING);
206  return FALSE;
207  }
208 
209  $source_job = $this->getJob($_GET['hipo_source_code_name']);
210  if (is_null($source_job)) {
211  // if we aren't actually waiting for an aborted hipo to remove itself then, display an error
212  if (empty($_POST['aborting_hipo'])) {
213  trigger_localised_error('HIPO0057', E_USER_WARNING, $_GET['hipo_source_code_name']);
214  return FALSE;
215  // else the server has finally killed the job so we are now done
216  } else {
217  return TRUE;
218  }
219  }
220 
221  // they want to stop all processing and cancel the HIPO
222  if (!empty($_POST['cancel_hipo'])) {
223  if ($source_job->abort()) {
224  $_POST['aborting_hipo'] = $source_job->code_name;
225  $_POST['aborting_hipo_url'] = $source_job->getOption('on_complete_url');
226  }
227  return TRUE;
228  }
229 
230  if (!empty($_GET['return_to_herder'])) {
231  $source_job->_options['on_complete_url'] = './?SQ_BACKEND_PAGE=main&backend_section=hipo_herder';
232  }
233 
234  $o = $this->getBackendOutputter();
235 
236  if (!$source_job->process()) {
237  $error_details = '';
238  $o->setFormData('', '');
239  $o->openRaw();
240  $source_job->_paintErrorReport();
241  $o->closeRaw();
242  $o->paint();
243 
244  $source_job->abort();
245 
246  return FALSE;
247  }
248 
249  return TRUE;
250 
251  }//end processWeb()
252 
253 
260  function paintWeb()
261  {
262  require_once SQ_LIB_PATH.'/html_form/html_form.inc';
263 
264  if (empty($_GET['hipo_source_code_name'])) {
265  trigger_localised_error('HIPO0058', E_USER_WARNING);
266  return FALSE;
267  }
268 
269  $source_job = $this->getJob($_GET['hipo_source_code_name']);
270  if (is_null($source_job) && empty($_POST['aborting_hipo'])) {
271  trigger_localised_error('HIPO0057', E_USER_WARNING, $_GET['hipo_source_code_name']);
272  return FALSE;
273  }
274 
275  // if this job is completed but has errors, just paint the job to print the error
276  // report because we dont need the additonal content
277  if (!is_null($source_job) && $source_job->complete() && !empty($source_job->_hipo_vars['errors'])) {
278  $o = $this->getBackendOutputter();
279  $o->setFormData('main_form', '');
280  $o->openRaw();
281  $source_job->paint($o);
282  $o->closeRaw();
283  $o->paint();
284  return;
285  }
286 
287  $onload = '';
288  $delay = FALSE;
289  $auto_step = FALSE;
290  $form_name = 'main_form';
291 
292  // if the source job is null it means that an abort finally finished
293  // (we couldn't get here otherwise) so print that and get out of here
294  if (is_null($source_job)) {
295  $url = $_POST['aborting_hipo_url'];
296  $onload = 'self.location = "'.$url.'";';
297  $auto_step = TRUE;
298  $percent_done = 100;
299  $status_msg = 'HIPO Aborted';
300  } else {
301  $url = $this->getProcessURL($source_job->code_name);
302  $auto_step = $source_job->autoStep();
303  $percent_done = $source_job->percentDone();
304 
305  if ($source_job->complete()) {
306  $onload = 'self.location = "'.$url.'";';
307  } else {
308  $delay = ($source_job->getRunningMode() == 'server');
309  $onload = 'document.'.$form_name.'.submit();';
310  }
311  $status_msg = $source_job->complete() ? translate('hipo_complete', $source_job->getHipoName()) : translate('hipo_progress', $source_job->getHipoName());
312  }
313 
314  $o = $this->getBackendOutputter();
315  $o->setFormData($form_name, $url);
316 
317  $o->openRaw();
318  ?>
319  <script language="JavaScript" type="text/javascript">
320  function next_page() {
321  <?php echo str_replace('\\', '\\\\', $onload); ?>
322  }//end next_page()
323 
324  function send_abort() {
325  if (ON_LOAD_TIME_OUT != null) {
326  clearTimeout(ON_LOAD_TIME_OUT);
327  }
328  if (confirm(js_translate('confirm_cancel'))) {
329  document.<?php echo $form_name; ?>.cancel_hipo.value = "1";
330  document.<?php echo $form_name; ?>.submit();
331  } else {
332  <?php
333  if (!is_null($source_job) && $source_job->getRunningMode() == 'server') {
334  ?>next_page();<?php
335  } else {
336  ?>alert(js_translate('action_cannot_be_performed'));<?php
337  }
338  ?>
339  }
340  }//end send_abort()
341  </script>
342  <?php
343 
344  if ($auto_step) {
345  if ($delay) {
346  $o->addOnLoad('ON_LOAD_TIME_OUT = setTimeout("next_page();", 2000);');
347  } else {
348  $o->addOnLoad('ON_LOAD_TIME_OUT = setTimeout("next_page();", 100);');
349  }
350  }
351 
352  ?>
353  <table border="0" cellspacing="0" cellpadding="0" width="500">
354  <tr>
355  <td class="sq-hipo-header">
356  <table class="sq-hipo-header">
357  <tr>
358  <td valign="middle" width="100%"><img src="<?php echo sq_web_path('lib').'/web/images/icons/hipo.gif'; ?>" width="71" height="26" alt="Powered by Highly Intensive Processing Object (HIPO) Technology" /></td>
359  </tr>
360  <tr>
361  <td class="sq-hipo-header">
362  <?php HIPO_Job::paintProgressBar($percent_done, $status_msg, 'sq-hipo-header-progress-bar-label', 'sq-hipo-header-progress-bar-percent', 'sq-hipo-header-progress-bar-main', 'sq-hipo-header-progress-bar-done'); ?>
363  </td>
364  </tr>
365  </table>
366  </td>
367  </tr>
368  <tr>
369  <td class="sq-hipo-main" valign="top">
370  <?php
371  $buttons_to_print = Array();
372 
373  // if we aren't waiting for an abort, print the progress stuff
374  if (empty($_POST['aborting_hipo'])) {
375 
376  // Paint the source job, this will take care of any sub jobs if needed
377  $o->closeRaw();
378  $source_job->paint($o);
379  $o->openRaw();
380 
381  if (!$source_job->complete()) {
382  // basically what happens here is that if cancel has already been pressed we increment the counter
383  // this means that the cancel button only get's pressed once
384  hidden_field('cancel_hipo', (empty($_POST['cancel_hipo'])) ? 0 : ((int) $_POST['cancel_hipo'] + 1));
385  $allow_cancel = $source_job->_steps[$source_job->_hipo_vars['current_step']]['allow_cancel'];
386  if ($allow_cancel) {
387  $buttons_to_print['cancel'] = Array('value' => 'Cancel', 'action' => 'send_abort();');
388  }
389  }
390 
391  // print the NEXT or FINISH button if we are not auto stepping through the HIPO
392  if (!$auto_step) {
393  // they dont want us to refresh automatically, so we need to provide a manual NEXT button
394  $button_text = ($source_job->complete()) ? 'Finish' : 'Next >>';
395  $buttons_to_print['next'] = Array('value' => $button_text, 'action' => 'next_page();');
396  }
397 
398  } else {
399  // we are aborting from the server
400 
401  hidden_field('aborting_hipo', $_POST['aborting_hipo']);
402  hidden_field('aborting_hipo_url', $_POST['aborting_hipo_url']);
403 
404  // if the source job is null it means that an abort finally finished
405  // (we couldn't get here otherwise) so print that and get out of here
406  if (is_null($source_job)) {
407  echo translate('hipo_abort_complete');
408  $button_text = 'OK';
409  } else {
410  echo translate('hipo_aborting');
411  $button_text = 'Next >>';
412  }
413 
414  // print the NEXT or FINISH button if we are not auto stepping through the HIPO
415  if (!$auto_step) {
416  // they dont want us to refresh automatically, so we need to provide a manual NEXT button
417  $buttons_to_print['next'] = Array('value' => $button_text, 'action' => 'next_page();');
418  }
419 
420  }// end if
421 
422  if (count($buttons_to_print)) {
423 
424  // work out if we should print the 'Next >>' button
425  $print_next = TRUE;
426  if (isset($source_job->_hipo_vars['no_next']) && $source_job->_hipo_vars['no_next']) {
427  $print_next = FALSE;
428  }
429 
430  ?>
431  <p>
432  <table border="0" width="100%" cellspacing="2" cellpadding="1">
433  <tr>
434  <td width="100%">&nbsp;</td>
435  <?php
436  foreach ($buttons_to_print as $btn_code => $btn_data) {
437  ?><td valign="top" align="center"><?php
438  // access key 'n' for next button
439  if ($btn_code == 'next') {
440  // if we should no proceed any further, then do not print the next button
441  if ($print_next) {
442  normal_button($btn_code, $btn_data['value'], $btn_data['action'], 'class="sq-hipo-button-enabled", accesskey="n""');
443  }
444  } else {
445  normal_button($btn_code, $btn_data['value'], $btn_data['action'], 'class="sq-hipo-button-enabled"');
446  }
447  ?></td><?php
448  }
449  ?>
450  </tr>
451  </table>
452  </p>
453  <?php
454  }//end if
455  ?>
456  </td>
457  </tr>
458  </table>
459  <?php
460 
461  $o->closeRaw();
462  $o->paint();
463 
464  }//end paintWeb()
465 
466 
479  function prepareProcessServer(&$input_data, &$output_data, $taskid)
480  {
481  $this->_tmp['prepared_server'] = Array();
482 
483  if (empty($input_data['userid'])) {
484  $output_data['result'] = 'error';
485  $output_data['msg'] = 'userid not supplied on Input Data';
486  return FALSE;
487  }
488 
489  $user = $GLOBALS['SQ_SYSTEM']->am->getAsset($input_data['userid']);
490  if (is_null($user) || !$GLOBALS['SQ_SYSTEM']->setCurrentUser($user)) {
491  $output_data['result'] = 'error';
492  $output_data['msg'] = 'Unable to find User #'.$input_data['userid'].' to run as or unable to set them to the current user';
493  return FALSE;
494  }
495 
496  if (empty($input_data['source_code_name'])) {
497  $output_data['result'] = 'error';
498  $output_data['msg'] = 'source_code_name not supplied on Input Data';
499  return FALSE;
500  }
501 
502  $source_job = $this->getJob($input_data['source_code_name']);
503  if (is_null($source_job)) {
504  $output_data['result'] = 'error';
505  $output_data['msg'] = 'Unable to find a source job with the codename "'.$input_data['source_code_name'].'"';
506  return FALSE;
507  }
508 
509  if (!$source_job->setRunningMode('server', $taskid)) {
510  $output_data['result'] = 'error';
511  $output_data['msg'] = 'Unable to set the running mode to server for "'.$source_job->code_name.'"';
512  return FALSE;
513  }
514 
515  $this->_tmp['prepared_server']['source_code_name'] = $source_job->code_name;
516 
517  pcntl_signal(SIGTERM, Array(&$this, 'serverSigHandler'));
518  pcntl_signal(SIGINT, Array(&$this, 'serverSigHandler'));
519 
520  $output_data['result'] = 'ack';
521  return TRUE;
522 
523  }//end prepareProcessServer()
524 
525 
537  function processServer()
538  {
539  // oracle + fork has some problems with commiting changes, have to force it
540  if (MatrixDAL::getDbType() === 'oci') {
542  }
543 
544  if (empty($this->_tmp['prepared_server'])) {
545  return FALSE;
546  }
547 
548  $source_job = $this->getJob($this->_tmp['prepared_server']['source_code_name']);
549  if (is_null($source_job)) {
550  trigger_localised_error('HIPO0057', E_USER_WARNING, $this->_tmp['prepared_server']['source_code_name']);
551  return FALSE;
552  }
553 
554  if ($source_job->uses_trans) { // only encase in a transaction if source job wants it
555  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
556  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
557  }
558 
559  $this->_tmp['processing_server'] = TRUE;
560 
561  $ret_val = TRUE;
562  while (!$source_job->complete() && $source_job->getRunningMode() == 'server') {
563  if (!$source_job->process()) {
564  $ret_val = FALSE;
565  break;
566  }
567  }// end while
568 
569  unset($this->_tmp['prepared_server']);
570  unset($this->_tmp['processing_server']);
571 
572  if ($source_job->uses_trans) { // complete transaction - only if desired by job
573  if ($ret_val) {
574  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
575  } else {
576  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
577  }
578  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
579  }
580 
581  if (MatrixDAL::getDbType() === 'oci') {
583  }
584 
585  return $ret_val;
586 
587  }//end processServer()
588 
589 
598  function serverSigHandler($signo)
599  {
600  switch ($signo) {
601  case SIGINT:
602  case SIGTERM:
603  if (!empty($this->_tmp['processing_server'])) {
604  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db'); // just to make sure
605  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
606  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
607  }
608  if (!empty($this->_tmp['prepared_server'])) {
609  $source_job = $this->getJob($this->_tmp['prepared_server']['source_code_name']);
610  if (!is_null($source_job)) $source_job->abort();
611  }// end if
612  exit();
613  default:
614  trigger_localised_error('HIPO0056', E_USER_NOTICE, $signo);
615  break;
616  }// end switch
617 
618  }//end serverSigHandler()
619 
620 
634  function queueHipo($job_type, $vars=Array(), $on_complete_url='', $job_dir='')
635  {
636  if (empty($on_complete_url)) {
637  $on_complete_url = $_SERVER['REQUEST_URI'];
638  }
639  if (empty($job_dir)) {
640  $job_dir = SQ_SYSTEM_ROOT.'/core/hipo/jobs';
641  }
642 
643  $this->_includeHipoJobLocales($job_dir);
644 
645  require_once $job_dir.'/'.$job_type.'.inc';
646  $init_hipo = new $job_type();
647  $init_hipo->setRunningVars($vars);
648 
649  // prepare() check the integrity of the the running vars
650  if (!$init_hipo->prepare()) return FALSE;
651  $init_hipo->_hipo_vars['job_dir'] = $job_dir;
652  $job_threshold = $init_hipo->getThresholdPercentageRequired();
653 
654  // if the job has not explictly asked to be run in hipo mode
655  // see if we can freestyle it
656  if ($job_threshold > 0) {
657  if (($this->threshold + $job_threshold) <= SQ_HIPO_TOTAL_THRESHOLD) {
658  $this->threshold += $job_threshold;
659  $init_hipo->_steps = $init_hipo->getInitialStepData();
660  return $init_hipo->freestyle();
661  }
662  }
663 
664  $init_hipo->setOption('on_complete_url', $on_complete_url);
665 
666  // check if this hipo has already been queued up by this person
667  // may happen when trying to perform the same task multiple times
668  // in a single script execution
669  $hipo_code = $init_hipo->getCodeName();
670  if (isset($this->_queued_jobs[$GLOBALS['SQ_SYSTEM']->currentUserId()])) {
671  if (in_array($hipo_code, $this->_queued_jobs[$GLOBALS['SQ_SYSTEM']->currentUserId()])) {
672  return TRUE;
673  }
674  }
675 
676  if (!$init_hipo->initialise()) return FALSE;
677 
678  $this->_queued_jobs[$GLOBALS['SQ_SYSTEM']->currentUserId()][] = $hipo_code;
679  return TRUE;
680 
681  }//end queueHipo()
682 
683 
694  function runQueuedJobs($on_complete_url='')
695  {
696  // note that we do not check rollback tables here because the
697  // hipo job table doesn't need one and doesn't want one!!
698  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
699  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
700  $db = MatrixDAL::getDb();
701  try {
702  $bind_vars['current_user'] = $GLOBALS['SQ_SYSTEM']->currentUserId();
703  $queued_jobs = MatrixDAL::executeAssoc('core', 'getQueuedHipoJobs', $bind_vars);
704  } catch (Exception $e) {
705  throw new Exception('Unable to get current queued jobs for user: '.$GLOBALS['SQ_SYSTEM']->currentUserId().' due to database error: '.$e->getMessage());
706  return '';
707  }
708 
709  if (empty($queued_jobs)) {
710  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
711  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
712  return '';
713  }
714 
715  $job_codes = Array();
716 
717  for ($i = 0; $i < count($queued_jobs); $i++) {
718  $job_data = $queued_jobs[$i];
719  $job = $this->getJob($job_data['code_name']);
720  $job_codes[] = $job_data['code_name'];
721 
722  if (isset($queued_jobs[($i+1)])) {
723  $next_url = $this->getProcessURL($queued_jobs[($i+1)]['code_name']);
724  $job->setOption('on_complete_url', $next_url);
725  if (!$job->save()) {
726  trigger_localised_error('HIPO0055', E_USER_WARNING);
727  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
728  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
729  return '';
730  }
731  } else if (!empty($on_complete_url)) {
732  $job->setOption('on_complete_url', $on_complete_url);
733  if (!$job->save()) {
734  trigger_localised_error('HIPO0055', E_USER_WARNING);
735  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
736  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
737  return '';
738  }
739  }
740  }
741 
742  try {
743  // run query from DAL
744  $bind_vars = Array(
745  'running' => 1,
746  'code_names' => $job_codes,
747  );
748  $result = MatrixDAL::executeQuery('core', 'updateHipoRunningStatus', $bind_vars);
749  } catch (Exception $e) {
750  throw new Exception('Unable to update HIPO jobs table due to database error: '.$e->getMessage());
751  }
752 
753  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
754  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
755  return $this->getProcessURL($queued_jobs[0]['code_name']);
756 
757  }//end runQueuedJobs()
758 
759 
771  function freestyleHipo($job_type, &$vars, $job_dir='')
772  {
773  if (empty($job_dir)) {
774  $job_dir = SQ_SYSTEM_ROOT.'/core/hipo/jobs';
775  }
776 
777  require_once $job_dir.'/'.$job_type.'.inc';
778  $init_hipo = new $job_type();
779  $init_hipo->setRunningVars($vars);
780  $init_hipo->_steps = $init_hipo->getInitialStepData();
781 
782  // prepare() check the integrity of the the running vars
783  if (!$init_hipo->prepare()) return FALSE;
784  set_error_handler(Array(&$init_hipo, '_errorHandler'));
785  $init_hipo->freestyle();
786  restore_error_handler();
787  return $init_hipo->getErrors();
788 
789  }//end freestyleHipo()
790 
791 
800  function _includeHipoJobLocales($job_dir)
801  {
802  // if a default hipo job location, nothing to do
803  if ($job_dir == SQ_SYSTEM_ROOT.'/core/hipo/jobs') {
804  return;
805  }
806 
807  // If this is not in the core/hipo/jobs it has to be under one of the packages
808  // Because the hipo jobs are not assets the locale strings will be part of
809  // the packages locales
810  if (strpos($job_dir, str_replace('\\', '/', SQ_CORE_PACKAGE_PATH)) !== FALSE) {
811  $GLOBALS['SQ_SYSTEM']->lm->includePackageStrings('__core__');
812  } else {
813  $stripped_dir = str_replace(str_replace('\\', '/', SQ_PACKAGES_PATH).'/', '', $job_dir);
814  $package = substr($stripped_dir, 0, strpos($stripped_dir, '/'));
815  $GLOBALS['SQ_SYSTEM']->lm->includePackageStrings($package);
816  }
817 
818 
819  }//end _includeHipoJobLocales()
820 
821 
830  function paintBackend(&$backend)
831  {
832  $hipo_admin = $GLOBALS['SQ_SYSTEM']->userRoot() || $GLOBALS['SQ_SYSTEM']->userSystemAdmin();
833  $current_user_id = $GLOBALS['SQ_SYSTEM']->currentUserId();
834 
835  $lock = $GLOBALS['SQ_SYSTEM']->getLockInfo(get_class_lower($this));
836  $have_lock = (!empty($lock) && ($current_user_id == $lock['userid']));
837 
838  $o =& $backend->out;
839 
840  if (!empty($_POST['herder_posted'])) {
841 
842  if ($have_lock && !empty($_POST['process_form']) && !empty($_POST['force_kill'])) {
843  $message_body = '';
844  foreach ($_POST['force_kill'] as $source_code_name) {
845  $source_job = $this->getJob($source_code_name);
846  if (is_null($source_job)) continue;
847 
848  if ($hipo_admin) {
849  // if the user is admin, temporarily pretend that
850  // the current user is the owner of the job
851  $user = $GLOBALS['SQ_SYSTEM']->am->getAsset($source_job->userid);
852  if (is_null($user)) continue;
853  $GLOBALS['SQ_SYSTEM']->setCurrentUser($user);
854  $source_job->abort();
855  $GLOBALS['SQ_SYSTEM']->restoreCurrentUser();
856  } else if ($source_job->userid == $current_user_id) {
857  $source_job->abort();
858  } else {
859  continue;
860  }
861 
862  $message_body .= 'FORCE KILL '.$source_code_name."\n";
863 
864  }
865 
866  $ms = $GLOBALS['SQ_SYSTEM']->getMessagingService();
867  $msg_reps = Array(
868  'hipo_code_name' => $source_code_name,
869  );
870  $message = $ms->newMessage(Array(), 'hipo.force_kill', $msg_reps);
871  $message->send();
872 
873  }//end if have lock, and forece kill is set
874 
875  // we want to remove some job records from the database, that is all
876  if ($have_lock && !empty($_POST['process_form']) && !empty($_POST['remove_job'])) {
877  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db3');
878  $db = $GLOBALS['SQ_SYSTEM']->db;
879 
880  $jobs_to_remove = Array();
881  foreach ($_POST['remove_job'] as $code_name) {
882  $jobs_to_remove[] = MatrixDAL::quote($code_name);
883  }
884  $sql = 'DELETE FROM sq_hipo_job
885  WHERE code_name IN ('.implode(',', $jobs_to_remove).')';
886  try {
887  $query = MatrixDAL::preparePdoQuery($sql);
888  $result = MatrixDAL::execPdoQuery($query);
889  } catch (Exception $e) {
890  throw new Exception('Unable to delete HIPO job(s) due to database error: '.$e->getMessage());
891  }
892  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
893 
894  // log each job that was cleared
895  $ms = $GLOBALS['SQ_SYSTEM']->getMessagingService();
896  foreach ($_POST['remove_job'] as $code_name) {
897  $msg_reps = Array(
898  'hipo_code_name' => $code_name,
899  );
900  $message = $ms->newMessage(Array(), 'hipo.orphan_kill', $msg_reps);
901  $message->send();
902  }
903 
904  }// end if have lock, and removing orphaned jobs
905 
906  if (!empty($_POST['sq_lock_release']) || !empty($_POST['sq_lock_release_manual'])) {
907  $GLOBALS['SQ_SYSTEM']->releaseLock(get_class_lower($this));
908  }
909 
910  // if there is no lock currently and we want it
911  if (!empty($_POST['sq_lock_acquire'])) {
912  $GLOBALS['SQ_SYSTEM']->acquireLock(get_class_lower($this));
913  }
914 
915  // just to make sure
916  $lock = $GLOBALS['SQ_SYSTEM']->getLockInfo(get_class_lower($this));
917  $have_lock = (!empty($lock) && ($current_user_id == $lock['userid']));
918 
919  }//end if not empty herder posted
920 
921  require_once SQ_FUDGE_PATH.'/general/datetime.inc';
922  $o->setHeading('HIPO Herder', sq_get_icon($o->filesPath('/images/icons/header/hipo_herder.png'), 20, 20, 'HIPO Icon'));
923  $o->setPageTitle('HIPO Herder');
924  $o->addHiddenField('herder_posted', '1');
925 
926  // lets tell the user if the asset is locked for editing
927  // or if they can lock it to edit it
928  $o->openSection(translate('locking_/_editing'));
929  $o->openRaw();
930  ?>
931  <table border="0" cellspacing="3" cellpadding="1">
932  <tr>
933  <td>
934  <?php sq_print_icon(sq_web_path('lib').'/web/images/icons/'.((empty($lock)) ? 'un' : '').'locked.png', 16, 16); ?>
935  </td>
936  <td>
937  <p class="sq-lock-message">
938  <?php
939  if (!empty($lock)) {
940  // this asset is currently locked
941  // so display message to the user
942  $user = $GLOBALS['SQ_SYSTEM']->am->getAsset($lock['userid']);
943 
944  $now = time();
945 
946  require_once SQ_FUDGE_PATH.'/general/datetime.inc';
947  $expires_in = easy_time_total(($lock['expires'] - $now), TRUE);
948  if (!$expires_in) $expires_in = '1 second';
949  $expires_in = translate('due_to_expire', $expires_in);
950  if ($have_lock) {
951  echo translate('release_lock', translate('release_lock_button'), translate('hipo_herder'));
952  echo '<br />';
953  }
954  echo $expires_in;
955 
956  } else {
957  echo translate('acquire_lock', translate('acquire_lock_button'), translate('hipo_herder'));
958  }
959  echo '</p>';
960  if (!empty($lock)) {
961  if ($have_lock) {
962  submit_button('sq_lock_release_manual', translate('release_lock_button'), 'set_hidden_field("process_form", "0");', 'accesskey="r"');
963  }
964  } else {
965  submit_button('sq_lock_acquire', translate('acquire_lock_button'), 'set_hidden_field("process_form", "0");', 'accesskey="a"');
966  }
967  ?>
968  </td>
969  </tr>
970  </table>
971  <?php
972  $o->closeRaw();
973  $o->closeSection();
974 
975  $o->openSection(translate('current_hipo_jobs'));
976  $o->openField('', 'new_line');
977 
978  $source_jobs = Array();
979 
980  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db3');
981  $db = MatrixDAL::getDb();
982  $sql = 'SELECT source_code_name, code_name, job_type
983  FROM sq_hipo_job';
984 
985  // regular users can only see own HIPO
986  if (!$hipo_admin) {
987  $sql .= ' WHERE userid = :current_user_id';
988  }
989  try {
990  $query = MatrixDAL::preparePdoQuery($sql);
991  if (!$hipo_admin) {
992  MatrixDAL::bindValueToPdo($query, 'current_user_id', $current_user_id);
993  }
994  $result = MatrixDAL::executePdoAssoc($query);
995  } catch (Exception $e) {
996  throw new Exception('Unable to get HIPO jobs for user: '.$current_user_id.' due to database error: '.$e->getMessage());
997  }
998  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
999  foreach ($result as $key => $row) {
1000  $source_code_name = ($row['source_code_name'] == $row['code_name']) ? '' : $row['source_code_name'];
1001  if (!isset($source_jobs[$source_code_name])) {
1002  $source_jobs[$source_code_name] = Array();
1003  }
1004  $source_jobs[$source_code_name][$row['code_name']] = $row['job_type'];
1005  }
1006 
1007  if (empty($source_jobs[''])) {
1008  echo translate('hipo_currently_no_jobs');
1009 
1010  } else {
1011  ?>
1012  <table class="sq-backend-table">
1013  <tr>
1014  <?php
1015  if ($have_lock) {
1016  ?>
1017  <td class="sq-backend-table-header" style="width: 30px;"><?php echo translate('hipo_herder_resume'); ?></td>
1018  <?php
1019  }
1020  ?>
1021  <td class="sq-backend-table-header"><?php echo translate('hipo_herder_job_type'); ?></td>
1022  <td class="sq-backend-table-header"><?php echo translate('owner'); ?></td>
1023  <td class="sq-backend-table-header"><?php echo translate('hipo_last_updated'); ?></td>
1024  <td class="sq-backend-table-header" style="text-align: center;"><?php echo translate('percent_complete'); ?></td>
1025  <?php
1026  if ($have_lock) {
1027  ?>
1028  <td class="sq-backend-table-header" style="text-align: center;"><?php echo translate('delete_question'); ?></td>
1029  <?php
1030  } // end if have lock
1031  ?>
1032  </tr>
1033  <?php
1034  $now = time();
1035  $current_user_id = $GLOBALS['SQ_SYSTEM']->currentUserId();
1036  foreach ($source_jobs[''] as $source_code_name => $source_job_type) {
1037  $source_job = $this->getJob($source_code_name);
1038  if (is_null($source_job)) continue;
1039  ?>
1040  <tr>
1041  <?php
1042  if ($have_lock) {
1043  ?>
1044  <td class="sq-backend-table-cell" style="text-align: center;">
1045  <?php
1046  // if the current user is the owner of the hipo job or the current user is a sysadmin or a root
1047  // then display the resume button
1048  // also once the user clicked on the resume button, the hipo job will be resumed in a popup window
1049  // and the resume button will be hidden
1050  if (($source_job->userid === $current_user_id) || $GLOBALS['SQ_SYSTEM']->userRoot() || $GLOBALS['SQ_SYSTEM']->userSystemAdmin()) {
1051  ?>
1052  <p style="text-align:center"><a href="./?SQ_ACTION=hipo&hipo_source_code_name=<?php echo $source_code_name; ?>&return_to_herder=1" style="text-decoration: none;"><img style="border: none;" src="<?php echo sq_web_path('lib'); ?>/web/images/hipo_resume.png" title="<?php echo translate('hipo_herder_continue'); ?>" alt="Resume <?php echo $source_code_name; ?>" /></a>&nbsp;<a href="#" onclick="var result = window.open('./?SQ_ACTION=hipo&hipo_source_code_name=<?php echo $source_code_name; ?>', 'hipo_job<?php echo $source_code_name; ?>', 'width=650,height=400,scrollbars=1,toolbar=0,menubar=0,location=0,resizable=1');return false;" style="text-decoration: none;" ><img style="border: none;" src="<?php echo sq_web_path('lib'); ?>/web/images/hipo_resume_popup.png" title="<?php echo translate('hipo_herder_continue'); ?>" alt="Resume <?php echo $source_code_name; ?>" /></a></p>
1053  <?php
1054  }
1055  ?>
1056  </td>
1057  <?php
1058  }
1059  ?>
1060  <td class="sq-backend-table-cell">
1061  <?php echo ucwords(str_replace('_', ' ', $source_job_type)); ?>
1062  </td>
1063  <td class="sq-backend-table-cell">
1064  <?php
1065  $user = $GLOBALS['SQ_SYSTEM']->am->getAsset($source_job->userid);
1066  echo $user->name.' (Id #'.$user->id.')';
1067  ?>
1068  </td>
1069  <td class="sq-backend-table-cell">
1070  <?php echo easy_time_total($source_job->last_updated - $now, TRUE); ?>
1071 
1072  </td>
1073  <td class="sq-backend-table-cell" style="text-align: center;">
1074  <?php echo $source_job->percentDone(); ?>%
1075  </td>
1076  <?php
1077  if ($have_lock) {
1078  ?>
1079  <td class="sq-backend-table-cell" style="text-align: center;">
1080  <?php check_box('force_kill[]', $source_job->code_name); ?>
1081  </td>
1082  <?php
1083  }// end if have_lock
1084  ?>
1085  </tr>
1086  <?php
1087 
1088  if (!empty($source_jobs[$source_code_name])) {
1089  foreach ($source_jobs[$source_code_name] as $code_name => $job_type) {
1090  $job = $this->getJob($code_name);
1091  if (is_null($job)) continue;
1092  ?>
1093  <tr>
1094  <td class="sq-backend-table-cell">
1095  &nbsp;
1096  </td>
1097  <td class="sq-backend-table-cell">
1098  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<?php echo ucwords(str_replace('_', ' ', $job_type)); ?>
1099  </td>
1100  <td class="sq-backend-table-cell">
1101  &nbsp;
1102  </td>
1103  <td class="sq-backend-table-cell">
1104  <?php echo easy_time_total($source_job->last_updated - $now, TRUE); ?>
1105  </td>
1106  <td class="sq-backend-table-cell" style="text-align: center;">
1107  <?php echo $job->percentDone(); ?>%
1108  </td>
1109  <td class="sq-backend-table-cell" style="text-align: center;">
1110  &nbsp;
1111  </td>
1112  </tr>
1113  <?php
1114 
1115  }// end foreach
1116  }// end if
1117  }//end foreach
1118  ?>
1119 
1120  </table>
1121  <?php
1122  }//end else
1123 
1124 
1125  $o->closeSection();
1126 
1127  // get jobs that have a non-existant source job
1128  $o->openSection(translate('orphaned_hipo_jobs'));
1129  $o->openField('', 'new_line');
1130 
1131  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db3');
1132  $db = MatrixDAL::getDb();
1133 
1134  $sql = 'SELECT code_name, source_code_name, job_type, userid, last_updated
1135  FROM sq_hipo_job
1136  WHERE source_code_name NOT IN (SELECT code_name FROM sq_hipo_job)';
1137 
1138  // regular users can only see own HIPO
1139  if (!$hipo_admin) {
1140  $sql .= ' AND userid = :current_user_id';
1141  }
1142 
1143  try {
1144  $query = MatrixDAL::preparePdoQuery($sql);
1145  if (!$hipo_admin) {
1146  MatrixDAL::bindValueToPdo($query, 'current_user_id', $current_user_id);
1147  }
1148  $orphaned_jobs = MatrixDAL::executePdoAssoc($query);
1149  } catch (Exception $e) {
1150  throw new Exception('Unable to get orphaned jobs for user: '.$current_user_id.' due to database error: '.$e->getMessage());
1151  }
1152 
1153  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
1154 
1155  // no orphaned jobs, yay
1156  if (empty($orphaned_jobs)) {
1157  echo translate('hipo_currently_no_oprhaned_jobs');
1158  } else {
1159  ?>
1160  <table class="sq-backend-table">
1161  <tr>
1162  <td class="sq-backend-table-header"><?php echo translate('hipo_herder_job_type'); ?></td>
1163  <td class="sq-backend-table-header"><?php echo translate('hipo_herder_parent_job_type'); ?></td>
1164  <td class="sq-backend-table-header"><?php echo translate('owner'); ?></td>
1165  <td class="sq-backend-table-header"><?php echo translate('hipo_last_updated'); ?></td>
1166  <?php
1167  if ($have_lock) {
1168  ?>
1169  <td class="sq-backend-table-header" style="text-align: center;"><?php echo translate('delete_question'); ?></td>
1170  <?php
1171  } // end if have lock
1172  ?>
1173  </tr>
1174  <?php
1175 
1176  // display some information on each orphaned job. we only use the info
1177  // we pulled earlier because calling something like getJob() may result in
1178  // attempt to abort the job in hipo_job::load(). we don't really want any
1179  // error messages to be displayed, just a way to remove this from the db
1180  foreach ($orphaned_jobs as $job_info) {
1181  ?>
1182  <tr>
1183  <td><?php echo ucwords(str_replace('_', ' ', $job_info['job_type'])); ?></td>
1184  <td><?php
1185  $parent_job_type = reset(explode('-', $job_info['source_code_name']));
1186  echo ucwords(str_replace('_', ' ', $parent_job_type));
1187  ?></td>
1188  <td><?php
1189  $user = $GLOBALS['SQ_SYSTEM']->am->getAsset($job_info['userid']);
1190  echo $user->name.' (Id #'.$user->id.')';
1191  ?></td>
1192  <td><?php
1193  $now = time();
1194  echo easy_time_total(strtotime($job_info['last_updated']) - $now, TRUE);
1195  ?></td>
1196  <?php
1197  if ($have_lock) {
1198  ?>
1199  <td class="sq-backend-table-cell" style="text-align: center;">
1200  <?php check_box('remove_job[]', $job_info['code_name']); ?>
1201  </td>
1202  <?php
1203  }// end if have_lock
1204  ?>
1205  </tr>
1206  <?php
1207  }
1208  }//end else
1209 
1210  $o->closeField();
1211  $o->closeSection();
1212 
1213  if ($have_lock) $o->commitButton('', TRUE);
1214 
1215  }//end paintBackend()
1216 
1217 
1226  function getJobsForUser($userid)
1227  {
1228  try {
1229  $bind_vars['userid'] = $userid;
1230  $jobsforuser = MatrixDAL::executeAssoc('core', 'selectJobsForUser', $bind_vars);
1231  } catch (Exception $e) {
1232  throw new Exception('Failed to get HIPO jobs for user due to database error: '.$e->getMessage());
1233  }
1234 
1235  // now get the percentage done for each job code we have
1236  for ($i = 0; $i < count($jobsforuser); $i++) {
1237  $source_job = $this->getJob($jobsforuser[$i]['code_name']);
1238  $jobsforuser[$i]['percent_done'] = $source_job->percentDone();
1239  }
1240  return $jobsforuser;
1241 
1242  }//end getJobsForUser()
1243 
1244 
1245 }//end class
1246 
1247 ?>