Seditio Source
Root |
./othercms/b2evolution_7.2.3/inc/cron/cronjobs.ctrl.php
<?php
/**
 * This file implements the UI controller for Cron table.
 *
 * This file is part of the evoCore framework - {@link http://evocore.net/}
 * See also {@link https://github.com/b2evolution/b2evolution}.
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @package admin
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );

load_funcs( 'cron/_cron.funcs.php' );

// Check minimum permission:
check_user_perm( 'admin', 'normal', true );
check_user_perm( 'options', 'view', true );

$AdminUI->set_path( 'options', 'cron', 'list' );

param( 'action', 'string', 'list' );
param( 'tab', 'string', 'list' );

if(
param( 'ctsk_ID', 'integer', '', true) )
{
// Load cronjob from cache:
   
$CronjobCache = & get_CronjobCache();
    if( (
$edited_Cronjob = & $CronjobCache->get_by_ID( $ctsk_ID, false ) ) === false )
    {
        unset(
$edited_Cronjob );
       
forget_param( 'ctsk_ID' );
       
$Messages->add( sprintf( TB_('Requested &laquo;%s&raquo; object does not exist any longer.'), TB_('Scheduled job') ), 'error' );
       
$action = 'list';
    }
}

switch(
$action )
{
    case
'new':
       
// Check that we have permission to edit options:
       
check_user_perm( 'options', 'edit', true, NULL );

       
load_class( 'cron/model/_cronjob.class.php', 'Cronjob' );
       
$edited_Cronjob = new Cronjob();

       
// Get this param to preselect job type by url param:
       
param( 'cjob_type', 'string' );
        break;

    case
'edit':
    case
'copy':
       
// Check that we have permission to edit options:
       
check_user_perm( 'options', 'edit', true, NULL );

        if( (
$action == 'edit' && $edited_Cronjob->get_status() != 'pending' ) ||
            (
$action == 'copy' && $edited_Cronjob->get_status() != 'error' ) )
        {    
// Don't edit cron jobs with not "pending" status
           
header_redirect( '?ctrl=crontab', 303 ); // Will EXIT
            // We have EXITed already at this point!!
       
}

        if(
$action == 'copy' )
        {    
// Reset time to now for copied cron job
           
global $localtimenow;
           
$edited_Cronjob->start_timestamp = $localtimenow;
        }

        break;

    case
'create':
       
// Check that this action request is not a CSRF hacked request:
       
$Session->assert_received_crumb( 'crontask' );

       
// Check that we have permission to edit options:
       
check_user_perm( 'options', 'edit', true, NULL );

        if( !empty(
$edited_Cronjob ) )
        {
// It is a copy action, we should save the fields "key" & "params"
           
$ctsk_key = $edited_Cronjob->get( 'key' );
           
$ctsk_params = $edited_Cronjob->get( 'params' );
        }

       
// CREATE OBJECT:
       
load_class( '/cron/model/_cronjob.class.php', 'Cronjob' );
       
$edited_Cronjob = new Cronjob();

        if(
$edited_Cronjob->load_from_Request() )
        {    
// We could load data from form without errors:

           
if( !empty( $ctsk_key ) )
            {    
// Save controller field from copied object
               
$edited_Cronjob->set( 'key', $ctsk_key );
            }

            if( !empty(
$ctsk_params ) )
            {    
// Save params field from copied object
               
$edited_Cronjob->set( 'params', $ctsk_params );
            }

           
// Save to DB:
           
$edited_Cronjob->dbinsert();

           
$Messages->add( TB_('New job has been scheduled.'), 'success' );

           
// Redirect so that a reload doesn't write to the DB twice:
           
header_redirect( '?ctrl=crontab', 303 ); // Will EXIT
            // We have EXITed already at this point!!
       
}
        break;

    case
'update':
       
// Check that this action request is not a CSRF hacked request:
       
$Session->assert_received_crumb( 'crontask' );

       
// Check that we have permission to edit options:
       
check_user_perm( 'options', 'edit', true, NULL );

        if(
$edited_Cronjob->load_from_Request() )
        {    
// We could load data from form without errors:

           
if( $edited_Cronjob->dbupdate() )
            {    
// The job was updated successfully
               
$Messages->add( TB_('The scheduled job has been updated.'), 'success' );
            }
            else
            {    
// Errors on updating, probably this job has not "pending" status
               
$Messages->add( TB_('This scheduled job can not be updated.'), 'error' );
            }

           
// Redirect so that a reload doesn't write to the DB twice:
           
header_redirect( '?ctrl=crontab', 303 ); // Will EXIT
            // We have EXITed already at this point!!
       
}
        break;


    case
'delete':
       
// Check that this action request is not a CSRF hacked request:
       
$Session->assert_received_crumb( 'crontask' );

       
// Make sure we got an ord_ID:
       
param( 'ctsk_ID', 'integer', true );

       
// Check that we have permission to edit options:
       
check_user_perm( 'options', 'edit', true, NULL );

       
// TODO: prevent deletion of running tasks.
       
$DB->begin();

       
$tsk_status =    $DB->get_var(
           
'SELECT clog_status
                 FROM T_cron__log
                WHERE clog_ctsk_ID= '
.$ctsk_ID,
           
0, 0, 'Check that task is not running' );

        if(
$tsk_status == 'started' )
        {
           
$DB->rollback();

           
$Messages->add(  sprintf( TB_('Job #%d is currently running. It cannot be deleted.'), $ctsk_ID ), 'error' );
        }
        else
        {
           
// Delete task:
           
$DB->query( 'DELETE FROM T_cron__task
                                        WHERE ctsk_ID = '
.$ctsk_ID );

           
// Delete log (if exists):
           
$DB->query( 'DELETE FROM T_cron__log
                                        WHERE clog_ctsk_ID = '
.$ctsk_ID );

           
$DB->commit();

           
$Messages->add(  sprintf( TB_('Scheduled job #%d deleted.'), $ctsk_ID ), 'success' );
        }

       
//forget_param( 'ctsk_ID' );
        //$action = 'list';
        // Redirect so that a reload doesn't write to the DB twice:
       
header_redirect( regenerate_url( 'action,ctsk_ID', '', '', '&' ), 303 ); // Will EXIT
        // We have EXITed already at this point!!
       
break;

    case
'settings':
       
// Update settings of cron jobs:

        // Check that this action request is not a CSRF hacked request:
       
$Session->assert_received_crumb( 'cronsettings' );

       
// Check permission:
       
check_user_perm( 'options', 'edit', true );

       
$cron_jobs = get_cron_jobs_config( 'name' );
        foreach(
$cron_jobs as $cron_job_key => $cron_job_name )
        {
           
// Max execution time:
           
$Settings->set( 'cjob_timeout_'.$cron_job_key, param_duration( 'cjob_timeout_'.$cron_job_key ) );

           
$cjob_maxemail = param( 'cjob_maxemail_'.$cron_job_key, 'string', NULL );
            if(
$cjob_maxemail !== NULL )
            {    
// Setting only for cron jobs that use email sending:
               
$cjob_maxemail = intval( $cjob_maxemail );
               
$Settings->set( 'cjob_maxemail_'.$cron_job_key, ( $cjob_maxemail > 0 ? $cjob_maxemail : '' ) );
            }

           
$cjob_imap_error = param( 'cjob_imap_error_'.$cron_job_key, 'string', NULL );
            if(
$cjob_imap_error !== NULL )
            {    
// Setting only for cron jobs that use IMAP email sending:
               
$cjob_imap_error = intval( $cjob_imap_error );
               
$Settings->set( 'cjob_imap_error_'.$cron_job_key, ( $cjob_imap_error > 1 ? $cjob_imap_error : 1 ) );
            }

           
// Additional settings per cron job:
           
switch( $cron_job_key )
            {
                case
'send-email-campaign':
                   
// Send a chunk of x emails for the campaign:
                   
if( check_user_perm( 'emails', 'edit' ) )
                    {    
// Allow to edit email cron setting "Chunk Size" only if user has a permission:
                       
$Settings->set( 'email_campaign_chunk_size', param( 'email_campaign_chunk_size', 'integer', 0 ) );
                    }

                   
// Delay between chunks:
                   
$Settings->set( 'email_campaign_cron_repeat', param_duration( 'email_campaign_cron_repeat' ) );

                   
// Delay between chunks in case all remaining recipients have reached max # of emails for the current day:
                   
$Settings->set( 'email_campaign_cron_limited', param_duration( 'email_campaign_cron_limited' ) );

                   
// Max emails to same domain:
                   
$Settings->set( 'email_campaign_max_domain', param( 'email_campaign_max_domain', 'integer', 0 ) );
                    break;

                case
'prune-old-hits-and-sessions':
                   
// Prune old hits & sessions (includes OPTIMIZE):
                   
param( 'auto_prune_stats', 'integer', $Settings->get_default( 'auto_prune_stats' ), false, false, true, false );
                   
$Settings->set( 'auto_prune_stats', get_param( 'auto_prune_stats' ) );
                    break;

                case
'prune-recycled-comments':
                   
// Prune recycled comments:
                   
param( 'auto_empty_trash', 'integer', $Settings->get_default( 'auto_empty_trash' ), false, false, true, false );
                   
$Settings->set( 'auto_empty_trash', get_param( 'auto_empty_trash' ) );
                    break;

                case
'cleanup-scheduled-jobs':
                   
// Clean up scheduled jobs older than a threshold:
                   
$Settings->set( 'cleanup_jobs_threshold', param( 'cleanup_jobs_threshold', 'integer', 0 ) );
                   
$Settings->set( 'cleanup_jobs_threshold_failed', param( 'cleanup_jobs_threshold_failed', 'integer', 0 ) );
                    break;

                case
'cleanup-email-logs':
                   
// Clean up email logs older than a threshold:
                   
$Settings->set( 'cleanup_email_logs_threshold', param_duration( 'cleanup_email_logs_threshold', 'integer', 0 ) );
                    break;

                case
'send-non-activated-account-reminders':
                   
// Send reminders about non-activated accounts:
                   
$Settings->set( 'activate_account_reminder_threshold', param_duration( 'activate_account_reminder_threshold' ) );
                   
// Account activation reminder settings:
                   
$reminder_config = array();
                   
$reminder_config_num = param( 'activate_account_reminder_config_num', 'integer', 0 );
                    for(
$c = 0; $c <= $reminder_config_num; $c++ )
                    {
                       
$reminder_config_value = param_duration( 'activate_account_reminder_config_'.$c );
                        if(
$reminder_config_value > 0 || $c >= $reminder_config_num - 2 )
                        {    
// Store only a selected reminder and 3 last options("Mark as Failed / Pending delete", "Delete warning", "Delete account"):
                           
$reminder_config[ $c ] = $reminder_config_value;
                        }
                    }
                    if(
count( $reminder_config ) < 4 )
                    {    
// If no reminder has been selected:
                       
param_error( 'activate_account_reminder_config_0', TB_('Please select at least one reminder for account activation reminder after subscription.') );
                    }
                    if( empty(
$reminder_config[ $reminder_config_num - 2 ] ) )
                    {    
// If "Mark as Failed / Pending delete" is not selected:
                       
param_error( 'activate_account_reminder_config_'.( $reminder_config_num - 2 ), /* Do NOT translate because it is impossible for normal form */'Please select account activation reminder threshold to "Marked as Failed / Pending delete" after subscription.' );
                    }
                   
$Settings->set( 'activate_account_reminder_config', implode( ',', $reminder_config ) );
                    break;

                case
'send-inactive-account-reminders':
                   
// Send reminders about inactivate accounts:
                   
$Settings->set( 'inactive_account_reminder_threshold', param_duration( 'inactive_account_reminder_threshold' ) );
                    break;

                case
'send-unmoderated-comments-reminders':
                   
// Send reminders about comments awaiting moderation:
                   
$Settings->set( 'comment_moderation_reminder_threshold', param_duration( 'comment_moderation_reminder_threshold' ) );
                    break;

                case
'send-unmoderated-posts-reminders':
                   
// Send reminders about posts awaiting moderation:
                   
$Settings->set( 'post_moderation_reminder_threshold', param_duration( 'post_moderation_reminder_threshold' ) );
                    break;

                case
'send-unread-messages-reminders':
                   
// Send reminders about unread messages:
                   
$Settings->set( 'unread_message_reminder_threshold', param_duration( 'unread_message_reminder_threshold' ) );
                   
// Unread private messages reminder settings:
                   
$reminder_delay = array();
                   
$i = 1;
                   
$prev_reminder_delay_day = 0;
                   
$prev_reminder_delay_spacing = 0;
                    for(
$d = 1; $d <= 10; $d++ )
                    {
                       
$reminder_delay_day = param( 'unread_message_reminder_delay_day_'.$d, 'integer', 0 );
                       
$reminder_delay_spacing = param( 'unread_message_reminder_delay_spacing_'.$d, 'integer', 0 );
                        if(
$reminder_delay_day > 0 || $reminder_delay_spacing > 0 )
                        {    
// Store only a filled reminder:
                           
if( empty( $reminder_delay_day ) )
                            {    
// If one field is not filled:
                               
param_error( 'unread_message_reminder_delay_day_'.$i, sprintf( TB_('Please fill both fields of the unread private messages reminder #%d.'), $i ) );
                               
$reminder_delay_day = 0;
                            }
                            elseif(
$prev_reminder_delay_day >= $reminder_delay_day )
                            {    
// If current value is less than previous:
                               
param_error( 'unread_message_reminder_delay_day_'.$i, TB_('The values of the unread private messages reminder must be ascending.') );
                            }
                            if( empty(
$reminder_delay_spacing ) )
                            {    
// If one field is not filled:
                               
param_error( 'unread_message_reminder_delay_spacing_'.$i, sprintf( TB_('Please fill both fields of the unread private messages reminder #%d.'), $i ) );
                               
$reminder_delay_spacing = 0;
                            }
                            elseif(
$prev_reminder_delay_spacing >= $reminder_delay_spacing )
                            {    
// If current value is less than previous:
                               
param_error( 'unread_message_reminder_delay_spacing_'.$i, TB_('The values of the unread private messages reminder must be ascending.') );
                            }
                           
$reminder_delay[] = $reminder_delay_day.':'.$reminder_delay_spacing;
                           
$prev_reminder_delay_day = $reminder_delay_day;
                           
$prev_reminder_delay_spacing = $reminder_delay_spacing;
                           
$i++;
                        }
                    }
                    if( empty(
$reminder_delay ) )
                    {    
// If no reminder has been selected:
                       
param_error( 'unread_message_reminder_delay_day_1', TB_('Please select at least one reminder for unread private messages.') );
                       
// Set one empty reminder in order to display all 10 reminders on the error form:
                       
$reminder_delay[] = '0:0';
                    }
                   
$Settings->set( 'unread_message_reminder_delay', implode( ',', $reminder_delay ) );
                    break;

                case
'manage-email-statuses':
                   
// Manage email address statuses:
                   
$manage_email_statuses_min_delay = param_duration( 'manage_email_statuses_min_delay' );
                   
param_check_not_empty( 'manage_email_statuses_min_delay', sprintf( TB_('The field &laquo;%s&raquo; cannot be empty.'), TB_('Minimum delay since last error') ) );
                   
$Settings->set( 'manage_email_statuses_min_delay', $manage_email_statuses_min_delay );
                   
param_integer_range( 'manage_email_statuses_min_sends', 1, 999999999, sprintf( TB_('The minimum value of the field "%s" is %d.'), TB_('Minimum sends since last error'), 1 ) );
                   
$Settings->set( 'manage_email_statuses_min_sends', $manage_email_statuses_min_sends );
                    break;
            }
        }

        if(
param_errors_detected() )
        {    
// Don't store settings if errors:
           
break;
        }

       
// Update settings:
       
$Settings->dbupdate();

       
$Messages->add( TB_('Scheduler settings have been updated.'), 'success' );

       
// Redirect so that a reload doesn't write to the DB twice:
       
header_redirect( $admin_url.'?ctrl=crontab&tab='.$tab, 303 ); // Will EXIT
        // We have EXITed already at this point!!
       
break;


    case
'view':
       
$cjob_ID = param( 'cjob_ID', 'integer', true );

       
$sql =  'SELECT *
                             FROM T_cron__task LEFT JOIN T_cron__log ON ctsk_ID = clog_ctsk_ID
                            WHERE ctsk_ID = '
.$cjob_ID;
       
$cjob_row = $DB->get_row( $sql, OBJECT, 0, 'Get cron job and log' );
        if( empty(
$cjob_row ) )
        {
           
$Messages->add( sprintf( TB_('Job #%d does not exist any longer.'), $cjob_ID ), 'error' );
           
$action = 'list';
        }
        break;

    case
'list':
        if(
$tab == 'list' )
        {    
// Detect timed out tasks:
           
detect_timeout_cron_jobs();
        }

        break;
}


$AdminUI->breadcrumbpath_init( false );  // fp> I'm playing with the idea of keeping the current blog in the path here...
$AdminUI->breadcrumbpath_add( TB_('System'), $admin_url.'?ctrl=system' );
$AdminUI->breadcrumbpath_add( TB_('Scheduler'), $admin_url.'?ctrl=crontab' );

// Set an url for manual page:
switch( $action )
{
    case
'new':
    case
'create':
    case
'edit':
    case
'update':
    case
'copy':
       
$AdminUI->set_page_manual_link( 'scheduled-job-form' );
        break;
    case
'view':
       
$AdminUI->set_page_manual_link( 'scheduled-job-info' );
        break;
    default:
        switch(
$tab )
        {
            case
'settings':
               
$AdminUI->set_path( 'options', 'cron', 'settings' );
               
$AdminUI->set_page_manual_link( 'scheduled-jobs-settings' );
                break;
            case
'test':
               
$AdminUI->set_path( 'options', 'cron', 'test' );
               
$AdminUI->set_page_manual_link( 'scheduled-jobs-test' );
                break;
            default:
               
$AdminUI->set_page_manual_link( 'scheduled-jobs-list' );
        }
        break;
}

if(
in_array( $action, array( 'new', 'create', 'edit', 'update', 'copy', 'list' ) ) )
{
// Initialize date picker for cronjob.form.php
   
init_datepicker_js();
}

// Display <html><head>...</head> section! (Note: should be done early if actions do not redirect)
$AdminUI->disp_html_head();

// Display title, menu, messages, etc. (Note: messages MUST be displayed AFTER the actions)
$AdminUI->disp_body_top();

// Begin payload block:
$AdminUI->disp_payload_begin();

switch(
$action )
{
    case
'new':
    case
'create':
    case
'edit':
    case
'update':
    case
'copy':
       
// Display VIEW:
       
$AdminUI->disp_view( 'cron/views/_cronjob.form.php' );
        break;

    case
'view':
       
// Display VIEW:
       
$AdminUI->disp_view( 'cron/views/_cronjob.view.php' ); // uses $cjob_row
       
break;

    default:
       
// Display VIEW:
       
switch( $tab )
        {
            case
'settings':
               
$AdminUI->disp_view( 'cron/views/_cronjob_settings.form.php' );
                break;
            case
'test':
               
// Require this template without function $AdminUI->disp_view() in order to keep all global vars which are used by cron_exec.php:
               
require $inc_path.'cron/views/_cronjob_test.view.php';
                break;
            default:
               
$AdminUI->disp_view( 'cron/views/_cronjob_list.view.php' );
        }
}

// End payload block:
$AdminUI->disp_payload_end();

// Display body bottom, debug info and close </html>:
$AdminUI->disp_global_footer();

?>