b2evolution PHP Cross Reference Blogging Systems

Source: /inc/cron/_cron.funcs.php - 324 lines - 10352 bytes - Summary - Text - Print

Description: This file implements cron (scheduled tasks) handling functions. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements cron (scheduled tasks) handling functions.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   *
  10   * {@internal License choice
  11   * - If you have received this file as part of a package, please find the license.txt file in
  12   *   the same folder or the closest folder above for complete license terms.
  13   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  14   *   then you must choose one of the following licenses before using the file:
  15   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  16   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  17   * }}
  18   *
  19   * {@internal Open Source relicensing agreement:
  20   * }}
  21   *
  22   * @package evocore
  23   *
  24   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  25   * @author fplanque: Francois PLANQUE.
  26   *
  27   * @version $Id: _cron.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  28   */
  29  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  30  
  31  
  32  /**
  33   * Log a message from cron.
  34   * @param string Message
  35   * @param integer Level of importance. The higher the more important.
  36   *        (if $quiet (number of "-q" params passed to cron_exec.php)
  37   *         is higher than this, the message gets skipped)
  38   */
  39  function cron_log( $message, $level = 0 )
  40  {
  41      global $is_web, $quiet;
  42  
  43      if( $quiet > $level )
  44      {
  45          return;
  46      }
  47  
  48      if( $is_web )
  49      {
  50          echo '<p>'.$message.'</p>';
  51      }
  52      else
  53      {
  54          echo "\n".$message."\n";
  55      }
  56  }
  57  
  58  /**
  59   * Extract keyphrases from the hitlog
  60   *
  61   * fp>yura fp>attila : this is not the right place to put this function. Who put it here?
  62   */
  63  function keyphrase_job()
  64  {
  65      global $DB;
  66  
  67      // Look for unextracted keyphrases:
  68      $sql = 'SELECT MIN(h.hit_ID) as min, MAX(h.hit_ID) as max
  69                  FROM T_hitlog as h
  70                  WHERE h.hit_keyphrase IS NOT NULL
  71                      AND h.hit_keyphrase_keyp_ID IS NULL';
  72      $ids = $DB->get_row( $sql, "ARRAY_A", NULL, ' Get max/min hits ids of unextracted keyphrases' );
  73  
  74      if ( ! empty ( $ids['min'] ) && ! empty ( $ids['max'] ) )
  75      { // Extract keyphrases if needed:
  76  
  77          $sql = 'INSERT INTO T_track__keyphrase(keyp_phrase, keyp_count_refered_searches)
  78                      SELECT h.hit_keyphrase, 1
  79                      FROM T_hitlog as h
  80                      WHERE
  81                          (h.hit_ID >= '.$ids['min'].' AND h.hit_ID <= '.$ids['max'].')
  82                          AND h.hit_keyphrase IS NOT NULL
  83                          AND h.hit_keyphrase_keyp_ID IS NULL
  84                          AND h.hit_referer_type = "search"
  85                  ON DUPLICATE KEY UPDATE
  86                  T_track__keyphrase.keyp_count_refered_searches = T_track__keyphrase.keyp_count_refered_searches + 1';
  87          $DB->query( $sql, ' Insert/Update external keyphrase' );
  88  
  89          $sql = 'INSERT INTO T_track__keyphrase(keyp_phrase, keyp_count_internal_searches)
  90                      SELECT h.hit_keyphrase, 1
  91                      FROM T_hitlog as h
  92                      WHERE
  93                          (h.hit_ID >= '.$ids['min'].' AND h.hit_ID <= '.$ids['max'].')
  94                          AND h.hit_keyphrase IS NOT NULL
  95                          AND h.hit_keyphrase_keyp_ID IS NULL
  96                          AND h.hit_referer_type != "search"
  97                  ON DUPLICATE KEY UPDATE
  98                  T_track__keyphrase.keyp_count_internal_searches = T_track__keyphrase.keyp_count_internal_searches + 1';
  99          $DB->query( $sql, 'Insert/Update  internal keyphrase' );
 100  
 101      // fp> This is dirty! No transaction -> no consistency! :(
 102      // fp> Not possible to do transactions on myisam
 103      // fp> workaround: in the queries above: set hit_keyphrase_keyp_ID = 0 on a TEMPORARY BASIS
 104  
 105      // fp> double dirty: this also updates rows that are already up to date.
 106      // fp> Add an additional WHERE condition: hit_keyphrase_keyp_ID = 0
 107  
 108          $sql = 'UPDATE T_hitlog as h, T_track__keyphrase as k
 109                  SET h.hit_keyphrase_keyp_ID = k.keyp_ID
 110                  WHERE
 111                      h.hit_keyphrase = k.keyp_phrase
 112                      AND (h.hit_ID >= '.$ids['min'].' AND h.hit_ID <= '.$ids['max'].')';
 113          $DB->query( $sql, 'Update hitlogs keyphrase id' );
 114      }
 115  }
 116  
 117  
 118  /**
 119   * Call a cron job.
 120   *
 121   * @param string Name of the job
 122   * @param string Params for the job:
 123   *               'ctsk_ID'   - task ID
 124   *               'ctsk_name' - task name
 125   * @return string Error message
 126   */
 127  function call_job( $job_name, $job_params = array() )
 128  {
 129      global $DB, $inc_path, $Plugins, $admin_url;
 130  
 131      global $result_message, $result_status, $timestop, $time_difference;
 132  
 133      $error_message = '';
 134      $result_message = NULL;
 135      $result_status = 'error';
 136  
 137      apm_name_transaction( 'CRON/'.$job_name );
 138  
 139      if( preg_match( '~^plugin_(\d+)_(.*)$~', $job_name, $match ) )
 140      { // Cron job provided by a plugin:
 141          if( ! is_object($Plugins) )
 142          {
 143              load_class( 'plugins/model/_plugins.class.php', 'Plugins' );
 144              $Plugins = new Plugins();
 145          }
 146  
 147          $Plugin = & $Plugins->get_by_ID( $match[1] );
 148          if( ! $Plugin )
 149          {
 150              $result_message = 'Plugin for controller ['.$job_name.'] could not get instantiated.';
 151              cron_log( $result_message, 2 );
 152              return $result_message;
 153          }
 154  
 155          // CALL THE PLUGIN TO HANDLE THE JOB:
 156          $tmp_params = array( 'ctrl' => $match[2], 'params' => $job_params );
 157          $sub_r = $Plugins->call_method( $Plugin->ID, 'ExecCronJob', $tmp_params );
 158  
 159          $error_code = (int)$sub_r['code'];
 160          $result_message = $sub_r['message'];
 161      }
 162      else
 163      {
 164          $controller = $inc_path.$job_name;
 165          if( ! is_file( $controller ) )
 166          {
 167              $result_message = 'Controller ['.$job_name.'] does not exist.';
 168              cron_log( $result_message, 2 );
 169              return $result_message;
 170          }
 171  
 172          // INCLUDE THE JOB FILE AND RUN IT:
 173          $error_code = require $controller;
 174      }
 175  
 176      if( is_array( $result_message ) )
 177      {    // If result is array (we should store it as serialized data later)
 178          // array keys: 'message' - Result message
 179          //             'table_cols' - Columns names of the table to display on the Execution details of the cron job log
 180          //             'table_data' - Array
 181          $result_message_text = $result_message['message'];
 182      }
 183      else
 184      {    // Result is text string
 185          $result_message_text = $result_message;
 186      }
 187  
 188      if( $error_code != 1 )
 189      {    // We got an error
 190          $result_status = 'error';
 191          $result_message_text = '[Error code: '.$error_code.' ] '.$result_message_text;
 192          if( is_array( $result_message ) )
 193          { // If result is array
 194              $result_message['message'] = $result_message_text;
 195          }
 196          $cron_log_level = 2;
 197  
 198          $error_message = $result_message_text;
 199      }
 200      else
 201      {
 202          $result_status = 'finished';
 203          $cron_log_level = 1;
 204      }
 205  
 206      $timestop = time() + $time_difference;
 207      cron_log( 'Task finished at '.date( 'H:i:s', $timestop ).' with status: '.$result_status
 208          ."\nMessage: $result_message_text", $cron_log_level );
 209  
 210      return $error_message;
 211  }
 212  
 213  
 214  /**
 215   * Get status color of sheduled job by status value
 216   *
 217   * @param string Status value
 218   * @return string Color value
 219   */
 220  function cron_status_color( $status )
 221  {
 222      $colors = array(
 223              'pending'  => '808080',
 224              'started'  => 'FFFF00',
 225              'finished' => '008000',
 226              'error'    => 'FF0000',
 227              'timeout'  => 'FFA500',
 228          );
 229  
 230      return isset( $colors[ $status ] ) ? '#'.$colors[ $status ] : 'none';
 231  }
 232  
 233  
 234  /**
 235   * Get the manual page link for the requested cron job, given by the controller path
 236   *
 237   * @param string job ctrl path
 238   * @return string NULL when the corresponding manual topic was not defined, the manual link otherwise
 239   */
 240  function cron_job_manual_link( $job_ctrl )
 241  {
 242      $manual_topics = array(
 243          'cron/jobs/_activate_account_reminder.job.php' => 'task-send-non-activated-account-reminders',
 244          'cron/jobs/_antispam_poll.job.php' => 'task-poll-antispam-blacklist',
 245          'cron/jobs/_cleanup_jobs.job.php' => 'task-cleanup-scheduled-jobs',
 246          'cron/jobs/_comment_moderation_reminder.job.php' => 'task-send-unmoderated-comments-reminders',
 247          'cron/jobs/_comment_notifications.job.php' => 'task-send-comment-notifications',
 248          'cron/jobs/_decode_returned_emails.job.php' => 'task-process-return-path-inbox',
 249          'cron/jobs/_error_test.job.php' => 'task-error-test',
 250          'cron/jobs/_heavy_db_maintenance.job.php' => 'task-heavy-db-maintenance',
 251          'cron/jobs/_light_db_maintenance.job.php' => 'task-light-db-maintenance',
 252          'cron/jobs/_post_by_email.job.php' => 'task-create-post-by-email',
 253          'cron/jobs/_post_notifications.job.php' => 'task-send-post-notifications',
 254          'cron/jobs/_process_hitlog.job.php' => 'task-process-hit-log',
 255          'cron/jobs/_prune_hits_sessions.job.php' => 'task-prune-old-hits-and-sessions',
 256          'cron/jobs/_prune_page_cache.job.php' => 'task-prune-old-files-from-page-cache',
 257          'cron/jobs/_prune_recycled_comments.job.php' => 'task-prune-recycled-comments',
 258          'cron/jobs/_test.job.php' => 'task-test',
 259          'cron/jobs/_unread_message_reminder.job.php' => 'task-send-unread-messages-reminders',
 260      );
 261  
 262      if( isset( $manual_topics[$job_ctrl] ) )
 263      { // return the corresponding manual page topic
 264          return get_manual_link( $manual_topics[$job_ctrl] );
 265      }
 266  
 267      // The topic was not defined
 268      return NULL;
 269  }
 270  
 271  
 272  /**
 273   * Detect timed out cron jobs and Send notifications
 274   *
 275   * @param array Task with error
 276   *             'name'
 277   *             'message'
 278   */
 279  function detect_timeout_cron_jobs( $error_task = NULL )
 280  {
 281      global $DB, $time_difference, $cron_timeout_delay, $admin_url;
 282  
 283      $SQL = new SQL( 'Find cron timeouts' );
 284      $SQL->SELECT( 'ctsk_ID, ctsk_name' );
 285      $SQL->FROM( 'T_cron__log' );
 286      $SQL->FROM_add( 'INNER JOIN T_cron__task ON ctsk_ID = clog_ctsk_ID' );
 287      $SQL->WHERE( 'clog_status = "started"' );
 288      $SQL->WHERE_and( 'clog_realstart_datetime < '.$DB->quote( date2mysql( time() + $time_difference - $cron_timeout_delay ) ) );
 289      $SQL->GROUP_BY( 'ctsk_ID' );
 290      $timeouts = $DB->get_assoc( $SQL->get(), OBJECT, $SQL->title );
 291  
 292      $tasks = array();
 293  
 294      if( count( $timeouts ) > 0 )
 295      {
 296          foreach( $timeouts as $task_ID => $task_name )
 297          {
 298              $tasks[ $task_ID ] = array(
 299                      'name'    => $task_name,
 300                      'message' => T_('Cron job was timed out.'),
 301                  );
 302          }
 303  
 304          // Update timed out cron jobs
 305          $DB->query( 'UPDATE T_cron__log
 306                SET clog_status = "timeout"
 307              WHERE clog_ctsk_ID IN ( '.$DB->quote( array_keys( $tasks ) ).' )', 'Detect cron timeouts.' );
 308      }
 309  
 310      if( !is_null( $error_task ) )
 311      { // Send notification with error task
 312          $tasks[ $error_task['ID'] ] = $error_task;
 313      }
 314  
 315      if( count( $tasks ) > 0 )
 316      { // Send notification email about timed out and error cron jobs to users with edit options permission
 317          $email_template_params = array(
 318                  'tasks' => $tasks,
 319              );
 320          send_admin_notification( NT_('Scheduled task error'), 'scheduled_task_error_report', $email_template_params );
 321      }
 322  }
 323  
 324  ?>

title

Description

title

Description

title

Description

title

title

Body