'jobs',
'singular' => 'job',
'ajax' => true,
]
);
}
/**
* Check if the current user has backwpup capability.
*
* @return bool
*/
public function ajax_user_can() {
return current_user_can( 'backwpup' );
}
/**
* Get filtered jobs based on the status UI filter.
*
* @return array
*/
private function get_filtered_jobs() {
$jobs = array_filter(
BackWPup_Option::get_job_ids(),
function ( $job_id ) {
return BackWPup_Option::get( $job_id, 'legacy', false );
}
);
// Get the "status" filter value from the GET request.
$status_filter = '';
$nonce = $this->get_list_nonce();
if ( $nonce && wp_verify_nonce( $nonce, self::LIST_NONCE_ACTION ) ) {
$status_filter = filter_input( INPUT_GET, 'status', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( null === $status_filter && isset( $_GET['status'] ) ) {
$status_filter = sanitize_text_field( wp_unslash( $_GET['status'] ) );
} else {
$status_filter = $status_filter ? sanitize_text_field( $status_filter ) : '';
}
}
if ( empty( $status_filter ) ) {
return $jobs;
}
return array_filter(
$jobs,
function ( $job_id ) use ( $status_filter ) {
switch ( $status_filter ) {
case 'disabled':
return empty( BackWPup_Option::get( $job_id, 'activetype', '' ) );
}
return true;
}
);
}
/**
* Prepares the list of items for displaying.
*
* @return void
*/
public function prepare_items() {
$this->items = $this->get_filtered_jobs();
$this->job_object = BackWPup_Job::get_working_data();
$this->job_types = BackWPup::get_job_types();
$this->destinations = BackWPup::get_registered_destinations();
$order_param = '';
$orderby_param = '';
$nonce = $this->get_list_nonce();
if ( $nonce && wp_verify_nonce( $nonce, self::LIST_NONCE_ACTION ) ) {
$order_param = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( null === $order_param && isset( $_GET['order'] ) ) {
$order_param = sanitize_text_field( wp_unslash( $_GET['order'] ) );
} else {
$order_param = $order_param ? sanitize_text_field( $order_param ) : '';
}
$order_param = $order_param ? sanitize_key( $order_param ) : '';
$orderby_param = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( null === $orderby_param && isset( $_GET['orderby'] ) ) {
$orderby_param = sanitize_text_field( wp_unslash( $_GET['orderby'] ) );
} else {
$orderby_param = $orderby_param ? sanitize_text_field( $orderby_param ) : '';
}
$orderby_param = $orderby_param ? sanitize_key( $orderby_param ) : '';
}
if ( empty( $order_param ) || empty( $orderby_param ) ) {
return;
}
if ( 'asc' === strtolower( (string) $order_param ) ) {
$order = SORT_ASC;
} else {
$order = SORT_DESC;
}
if ( empty( $orderby_param ) || ! in_array( strtolower( (string) $orderby_param ), [ 'jobname', 'type', 'dest', 'next', 'last' ], true ) ) {
$orderby = 'jobname';
} else {
$orderby = strtolower( (string) $orderby_param );
}
// Sorting.
$job_configs = [];
$i = 0;
foreach ( $this->items as $item ) {
$job_configs[ $i ]['jobid'] = $item;
$job_configs[ $i ]['jobname'] = BackWPup_Option::get( $item, 'name' );
$job_configs[ $i ]['type'] = BackWPup_Option::get( $item, 'type' );
$job_configs[ $i ]['dest'] = BackWPup_Option::get( $item, 'destinations' );
if ( SORT_ASC === $order ) {
sort( $job_configs[ $i ]['type'] );
sort( $job_configs[ $i ]['dest'] );
} else {
rsort( $job_configs[ $i ]['type'] );
rsort( $job_configs[ $i ]['dest'] );
}
$job_configs[ $i ]['type'] = array_shift( $job_configs[ $i ]['type'] );
$job_configs[ $i ]['dest'] = array_shift( $job_configs[ $i ]['dest'] );
$job_configs[ $i ]['next'] = (int) wp_next_scheduled( 'backwpup_cron', [ 'arg' => $item ] );
$job_configs[ $i ]['last'] = BackWPup_Option::get( $item, 'lastrun' );
++$i;
}
$tmp = [];
foreach ( $job_configs as &$ma ) {
$tmp[] = &$ma[ $orderby ];
}
array_multisort( $tmp, $order, $job_configs );
$this->items = [];
foreach ( $job_configs as $item ) {
$this->items[] = $item['jobid'];
}
}
/**
* Message to be displayed when there are no items.
*
* @return void
*/
public function no_items() {
esc_html_e( 'No Legacy Jobs.', 'backwpup' );
}
/**
* Retrieves the list of bulk actions available for this table.
*
* @return array
*/
public function get_bulk_actions() {
if ( ! $this->has_items() ) {
return [];
}
$actions = [];
$actions['delete'] = __( 'Delete', 'backwpup' );
return wpm_apply_filters_typed( 'array', 'backwpup_page_jobs_get_bulk_actions', $actions );
}
/**
* Gets a list of columns.
*
* @return array
*/
public function get_columns() {
$jobs_columns = [];
$jobs_columns['cb'] = ' ';
$jobs_columns['jobname'] = __( 'Job Name', 'backwpup' );
$jobs_columns['type'] = __( 'Type', 'backwpup' );
$jobs_columns['dest'] = __( 'Destinations', 'backwpup' );
$jobs_columns['next'] = __( 'Next Run', 'backwpup' );
$jobs_columns['last'] = __( 'Last Run', 'backwpup' );
return $jobs_columns;
}
/**
* Gets a list of sortable columns.
*
* @return array
*/
public function get_sortable_columns() {
return [
'jobname' => 'jobname',
'type' => 'type',
'dest' => 'dest',
'next' => 'next',
'last' => 'last',
];
}
/**
* Retrieves the table classes for the job page.
*
* This method constructs an array of CSS classes to be applied to the table
* on the job page. The 'fixed' class is intentionally omitted from the default
* classes.
*
* @return array An array of CSS classes for the table.
*/
protected function get_table_classes() {
// Remove 'fixed' from the default classes.
$classes = [ 'widefat', 'striped', $this->_args['plural'] ];
return $classes;
}
/**
* The checkbox column.
*
* @param int $item Job ID.
*
* @return string
*/
public function column_cb( $item ) {
return ' ';
}
/**
* The job name column.
*
* @param int $item Job ID.
*
* @return string
*/
public function column_jobname( $item ) {
$job_normal_hide = '';
if ( is_object( $this->job_object ) ) {
$job_normal_hide = ' style="display:none;"';
}
$r = '' . esc_html( BackWPup_Option::get( $item, 'name' ) ) . ' ';
$actions = [];
if ( current_user_can( 'backwpup_jobs_edit' ) ) {
$actions['edit'] = '' . esc_html__( 'Edit', 'backwpup' ) . ' ';
$actions['delete'] = '' . esc_html__( 'Delete', 'backwpup' ) . ' ';
}
if ( current_user_can( 'backwpup_jobs_start' ) ) {
$url = BackWPup_Job::get_jobrun_url( 'runnowlink', $item );
$actions['runnow'] = '' . esc_html__( 'Run now', 'backwpup' ) . ' ';
}
if ( current_user_can( 'backwpup_logs' ) && BackWPup_Option::get( $item, 'logfile' ) ) {
$logfile = basename( (string) BackWPup_Option::get( $item, 'logfile' ) );
if ( is_object( $this->job_object ) && (int) $this->job_object->job['jobid'] === (int) $item ) {
$logfile = basename( (string) $this->job_object->logfile );
}
$log_name = str_replace( [ '.html', '.gz' ], '', basename( $logfile ) );
$actions['lastlog'] = '' . __( 'Last log', 'backwpup' ) . ' ';
}
$actions = wpm_apply_filters_typed( 'array', 'backwpup_page_jobs_actions', $actions, $item, false );
$r .= '
' . $this->row_actions( $actions ) . '
';
if ( is_object( $this->job_object ) ) {
$actionsrun = [];
$actionsrun = wpm_apply_filters_typed( 'array', 'backwpup_page_jobs_actions', $actionsrun, $item, true );
$r .= '' . $this->row_actions( $actionsrun ) . '
';
}
return $r;
}
/**
* The type column.
*
* @param int $item Job ID.
*
* @return string
*/
public function column_type( $item ) {
$r = '';
$types = BackWPup_Option::get( $item, 'type' );
if ( $types ) {
foreach ( $types as $type ) {
if ( isset( $this->job_types[ $type ] ) ) {
$r .= $this->job_types[ $type ]->info['name'] . ' ';
} else {
$r .= $type . ' ';
}
}
}
return $r;
}
/**
* The destination column.
*
* @param int $item Job ID.
*
* @return string
*/
public function column_dest( $item ) {
$r = '';
$backup_to = false;
foreach ( BackWPup_Option::get( $item, 'type' ) as $typeid ) {
if ( isset( $this->job_types[ $typeid ] ) && $this->job_types[ $typeid ]->creates_file() ) {
$backup_to = true;
break;
}
}
if ( $backup_to ) {
foreach ( BackWPup_Option::get( $item, 'destinations' ) as $destid ) {
if ( isset( $this->destinations[ $destid ]['info']['name'] ) ) {
$r .= $this->destinations[ $destid ]['info']['name'] . ' ';
} else {
$r .= $destid . ' ';
}
}
} else {
$r .= '' . __( 'Not needed or set', 'backwpup' ) . ' ';
}
return $r;
}
/**
* The next column.
*
* @param int $item Job ID.
*
* @return string
*/
public function column_next( $item ) {
$r = '';
$job_normal_hide = '';
if ( is_object( $this->job_object ) ) {
$job_normal_hide = ' style="display:none;"';
}
if ( is_object( $this->job_object ) && $this->job_object->job['jobid'] === $item ) {
$current_timestamp = time() + (int) ( (float) get_option( 'gmt_offset' ) * HOUR_IN_SECONDS );
$runtime = $current_timestamp - $this->job_object->start_time;
$r .= '' . sprintf(
/* translators: %s: runtime in seconds. */
esc_html__( 'Running for: %s seconds', 'backwpup' ),
'' . $runtime . ' '
) . '
';
}
if ( is_object( $this->job_object ) && (int) $this->job_object->job['jobid'] === (int) $item ) {
$r .= '';
}
if ( 'wpcron' === BackWPup_Option::get( $item, 'activetype' ) ) {
$nextrun = wp_next_scheduled( 'backwpup_cron', [ 'arg' => $item ] ) + ( get_option( 'gmt_offset' ) * 3600 );
if ( $nextrun ) {
// translators: %s: cron expression.
$title = sprintf( esc_html__( 'Cron: %s', 'backwpup' ), BackWPup_Option::get( $item, 'cron' ) );
// translators: %1$s: date, %2$s: time.
$content = sprintf( __( '%1$s at %2$s', 'backwpup' ), wp_date( get_option( 'date_format' ), $nextrun ), wp_date( get_option( 'time_format' ), $nextrun ) );
$r .= '' . $content . ' ';
} else {
$r .= __( 'Not scheduled!', 'backwpup' ) . ' ';
}
} elseif ( 'link' === BackWPup_Option::get( $item, 'activetype' ) ) {
$r .= __( 'External link', 'backwpup' ) . ' ';
} else {
$r .= __( 'Inactive', 'backwpup' );
}
if ( is_object( $this->job_object ) && (int) $this->job_object->job['jobid'] === (int) $item ) {
$r .= '
';
}
return $r;
}
/**
* The last column.
*
* @param int $item Job ID.
*
* @return string
*/
public function column_last( $item ) {
$r = '';
if ( BackWPup_Option::get( $item, 'lastrun' ) ) {
$lastrun = BackWPup_Option::get( $item, 'lastrun' );
$r .= sprintf(
// translators: %1$s: date, %2$s: time.
__( '%1$s at %2$s', 'backwpup' ),
wp_date( get_option( 'date_format' ), $lastrun ),
wp_date( get_option( 'time_format' ), $lastrun )
);
if ( BackWPup_Option::get( $item, 'lastruntime' ) ) {
$r .= ' ' . sprintf(
// translators: %d: seconds.
__( 'Runtime: %d seconds', 'backwpup' ),
BackWPup_Option::get( $item, 'lastruntime' )
);
}
} else {
$r .= __( 'not yet', 'backwpup' );
}
$r .= '';
$download_url = BackWPup_Option::get( $item, 'lastbackupdownloadurl' );
if ( current_user_can( 'backwpup_backups_download' ) && ! empty( $download_url ) ) {
// If it's not a direct link, but will be run through the downloader, then process differently.
if ( strpos( $download_url, 'backwpupbackups' ) === false ) {
$r .= '' . esc_html__( 'Download', 'backwpup' ) . ' | ';
} else {
$r .= self::generate_download_link( $download_url ) . ' | ';
}
}
if ( current_user_can( 'backwpup_logs' ) && BackWPup_Option::get( $item, 'logfile' ) ) {
$logfile = basename( (string) BackWPup_Option::get( $item, 'logfile' ) );
if ( is_object( $this->job_object ) && (int) $this->job_object->job['jobid'] === (int) $item ) {
$logfile = basename( (string) $this->job_object->logfile );
}
$log_name = str_replace( [ '.html', '.gz' ], '', basename( $logfile ) );
$r .= '' . esc_html__( 'Log', 'backwpup' ) . ' ';
}
$r .= ' ';
return $r;
}
/**
* Generates download link from url.
*
* @param string $download_url Download url.
* @return string
*/
private static function generate_download_link( $download_url ) {
$params = [];
parse_str( wp_parse_url( $download_url, PHP_URL_QUERY ), $params );
$file = $params['file'];
$local_file = untrailingslashit( BackWPup::get_plugin_data( 'TEMP' ) ) . '/' . basename( $params['local_file'] ?? $file );
$jobid = $params['jobid'];
$destination = strtoupper( str_replace( 'download', '', $params['action'] ) );
// Construct the link.
return sprintf(
'%7$s ',
intval( $jobid ),
esc_attr( $destination ),
esc_attr( $file ),
esc_attr( $local_file ),
wp_create_nonce( 'backwpup_action_nonce' ),
wp_nonce_url( $download_url, 'backwpup_action_nonce' ),
__( 'Download', 'backwpup' )
);
}
/**
* Load listing page.
*
* @return void
*/
public static function load() {
// Create table.
self::$listtable = new self();
if ( empty( self::$listtable->current_action() ) ) {
self::ensure_list_nonce();
}
switch ( self::$listtable->current_action() ) {
case 'delete': // Delete job.
if ( ! current_user_can( 'backwpup_jobs_edit' ) ) {
break;
}
$jobs = filter_input( INPUT_GET, 'jobs', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
if ( ! empty( $jobs ) ) {
check_admin_referer( 'bulk-jobs' );
foreach ( array_map( 'absint', $jobs ) as $jobid ) {
wp_clear_scheduled_hook( 'backwpup_cron', [ 'arg' => (int) $jobid ] );
BackWPup_Option::delete_job( $jobid );
}
}
break;
case 'copy': // Copy job.
if ( ! current_user_can( 'backwpup_jobs_edit' ) ) {
break;
}
$old_job_id = absint( filter_input( INPUT_GET, 'jobid', FILTER_SANITIZE_NUMBER_INT ) );
if ( $old_job_id < 1 ) {
break;
}
check_admin_referer( 'copy-job_' . $old_job_id );
// Create new.
$newjobid = BackWPup_Option::get_job_ids();
sort( $newjobid );
$newjobid = end( $newjobid ) + 1;
$old_options = BackWPup_Option::get_job( $old_job_id );
foreach ( $old_options as $key => $option ) {
if ( 'jobid' === $key ) {
$option = $newjobid;
}
if ( 'name' === $key ) {
$option = __( 'Copy of', 'backwpup' ) . ' ' . $option;
}
if ( 'activetype' === $key ) {
$option = '';
}
if ( 'archivename' === $key ) {
$option = str_replace( $old_job_id, $newjobid, (string) $option );
}
if ( 'logfile' === $key || 'lastbackupdownloadurl' === $key || 'lastruntime' === $key || 'lastrun' === $key ) {
continue;
}
BackWPup_Option::update( $newjobid, $key, $option );
}
break;
case 'runnow':
$jobid = absint( filter_input( INPUT_GET, 'jobid', FILTER_SANITIZE_NUMBER_INT ) );
if ( $jobid ) {
if ( ! current_user_can( 'backwpup_jobs_start' ) ) {
wp_die( esc_html__( 'Sorry, you don\'t have permissions to do that.', 'backwpup' ) );
}
check_admin_referer( 'backwpup_job_run-runnowlink' );
// Check temp folder.
$temp_folder_message = BackWPup_File::check_folder( BackWPup::get_plugin_data( 'TEMP' ), true );
BackWPup_Admin::message( $temp_folder_message, true );
// Check log folder.
$log_folder = get_site_option( 'backwpup_cfg_logfolder' );
$log_folder = BackWPup_File::get_absolute_path( $log_folder );
$log_folder_message = BackWPup_File::check_folder( $log_folder );
BackWPup_Admin::message( $log_folder_message, true );
// Check backup destinations.
$job_types = BackWPup::get_job_types();
$job_conf_types = BackWPup_Option::get( $jobid, 'type' );
$creates_file = false;
foreach ( $job_types as $id => $job_type_class ) {
if ( in_array( $id, $job_conf_types, true ) && $job_type_class->creates_file() ) {
$creates_file = true;
break;
}
}
if ( $creates_file ) {
$job_conf_dests = BackWPup_Option::get( $jobid, 'destinations' );
$destinations = 0;
foreach ( BackWPup::get_registered_destinations() as $id => $dest ) {
if ( ! in_array( $id, $job_conf_dests, true ) || empty( $dest['class'] ) ) {
continue;
}
/* @var BackWPup_Destinations $dest_class */
$dest_class = BackWPup::get_destination( $id );
$job_settings = BackWPup_Option::get_job( $jobid );
if ( ! $dest_class->can_run( $job_settings, false ) ) {
BackWPup_Admin::message(
sprintf(
/* translators: 1: job name, 2: destination name. */
__( 'The job "%1$s" destination "%2$s" is not configured properly', 'backwpup' ),
esc_attr( BackWPup_Option::get( $jobid, 'name' ) ),
$id
),
true
);
}
++$destinations;
}
if ( $destinations < 1 ) {
BackWPup_Admin::message(
sprintf(
/* translators: %s: job name. */
__( 'The job "%s" needs properly configured destinations to run!', 'backwpup' ),
esc_attr( BackWPup_Option::get( $jobid, 'name' ) )
),
true
);
}
}
// Only start job if messages empty.
$log_messages = BackWPup_Admin::get_messages();
if ( empty( $log_messages ) ) {
$old_log_file = BackWPup_Option::get( $jobid, 'logfile' );
BackWPup_Job::get_jobrun_url( 'runnow', $jobid );
usleep( 250000 ); // Wait a quarter second.
$new_log_file = BackWPup_Option::get( $jobid, 'logfile', null, false );
// Sleep as long as job not started.
$i = 0;
while ( $old_log_file === $new_log_file ) {
usleep( 250000 ); // Wait a quarter second for next try.
$new_log_file = BackWPup_Option::get( $jobid, 'logfile', null, false );
// Wait maximal 10 sec.
if ( $i >= 40 ) {
BackWPup_Admin::message(
sprintf(
/* translators: 1: Job name, 2: Information URL */
__( 'Job "%1$s" has started, but not responded for 10 seconds. Please check information .', 'backwpup' ),
esc_attr( BackWPup_Option::get( $jobid, 'name' ) ),
network_admin_url( 'admin.php' ) . '?page=backwpupsettings#backwpup-tab-information'
),
true
);
break 2;
}
++$i;
}
BackWPup_Admin::message(
sprintf(
/* translators: %s: Job name */
__( 'Job "%s" started.', 'backwpup' ),
esc_attr( BackWPup_Option::get( $jobid, 'name' ) )
)
);
}
}
break;
case 'abort': // Abort job.
if ( ! current_user_can( 'backwpup_jobs_start' ) ) {
break;
}
check_admin_referer( 'abort-job' );
if ( ! file_exists( BackWPup::get_plugin_data( 'running_file' ) ) ) {
break;
}
// Abort.
BackWPup_Job::user_abort();
BackWPup_Admin::message( __( 'Job will be terminated.', 'backwpup' ) );
break;
default:
do_action( 'backwpup_page_jobs_load', self::$listtable->current_action() );
break;
}
self::$listtable->prepare_items();
}
/**
* Print admin styles
*
* @return void
*/
public static function admin_print_styles() {
?>
';
// translators: %s: plugin name.
echo '' . esc_html( sprintf( __( '%s › Legacy Jobs', 'backwpup' ), BackWPup::get_plugin_data( 'name' ) ) ) . ' ';
BackWPup_Admin::display_messages();
$job_object = BackWPup_Job::get_working_data();
if ( current_user_can( 'backwpup_jobs_start' ) && is_object( $job_object ) ) {
// Read existing logfile.
$filesystem = backwpup_wpfilesystem();
$logfiledata = $filesystem->get_contents( $job_object->logfile );
preg_match( '/]*>/si', $logfiledata, $match );
if ( ! empty( $match[0] ) ) {
$startpos = strpos( $logfiledata, $match[0] ) + strlen( $match[0] );
} else {
$startpos = 0;
}
$endpos = stripos( $logfiledata, '' );
if ( empty( $endpos ) ) {
$endpos = strlen( $logfiledata );
}
$length = strlen( $logfiledata ) - ( strlen( $logfiledata ) - $endpos ) - $startpos;
?>
job['name'] ) ); ?>
warnings ); ?>
errors ); ?>
steps_data[ $job_object->step_working ]['NAME'] ); ?>
lastmsg ); ?>
decrypt_key_input();
}
?>
logfile ) ) {
?>
$carry || current_user_can( $cap ), false );
if ( ! $can_access ) {
http_response_code( 403 );
echo wp_json_encode(
[
'success' => false,
'data' => [
'message' => __( 'You do not have sufficient permissions to access this function.', 'backwpup' ),
'job_done' => 1,
],
]
);
throw new Exception( 'ajax_exit' );
}
// Nonce verification.
if ( ! check_ajax_referer( 'backwpupworking_ajax_nonce', '_ajax_nonce', false ) ) {
http_response_code( 401 );
echo wp_json_encode(
[
'success' => false,
'data' => [
'message' => __( 'Security check failed. Please refresh the page and try again.', 'backwpup' ),
'job_done' => 1,
],
]
);
throw new Exception( 'ajax_exit' );
}
$log_folder = get_site_option( 'backwpup_cfg_logfolder' );
$log_folder = BackWPup_File::get_absolute_path( $log_folder );
$job_object = BackWPup_Job::get_working_data();
$logfile = basename( (string) $job_object->logfile );
try {
$logfile_param = filter_input( INPUT_GET, 'logfile', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$logfile_param = $logfile_param ? sanitize_text_field( $logfile_param ) : $logfile;
$logfile = self::get_logfile_path( $log_folder, $logfile_param );
} catch ( \InvalidArgumentException $e ) {
// Logfile either not passed or is invalid.
echo 'error';
exit( 0 );
}
$logpos = absint( filter_input( INPUT_GET, 'logpos', FILTER_SANITIZE_NUMBER_INT ) );
$restart_url = '';
// Check if logfile renamed.
if ( file_exists( $logfile . '.gz' ) ) {
$logfile .= '.gz';
}
if ( ! is_readable( $logfile ) ) {
exit( '0' );
}
$job_object = BackWPup_Job::get_working_data();
$done = 0;
if ( is_object( $job_object ) ) {
$warnings = $job_object->warnings;
$errors = $job_object->errors;
$step_percent = $job_object->step_percent;
$substep_percent = $job_object->substep_percent;
$runtime = current_time( 'timestamp' ) - $job_object->start_time; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$onstep = $job_object->steps_data[ $job_object->step_working ]['NAME'];
$lastmsg = $job_object->lastmsg;
$lasterrormsg = $job_object->lasterrormsg;
$step_done = count( $job_object->steps_done );
$step_todo = count( $job_object->steps_todo );
$substeps_todo = $job_object->substeps_todo;
$substeps_done = $job_object->substeps_done;
} else {
$logheader = BackWPup_Job::read_logheader( $logfile );
$warnings = $logheader['warnings'];
$runtime = $logheader['runtime'];
$errors = $logheader['errors'];
$step_percent = 100;
$substep_percent = 100;
$step_done = 100;
$step_todo = 100;
$substeps_todo = 100;
$substeps_done = 100;
$onstep = esc_html__( 'Job completed', 'backwpup' );
if ( $errors > 0 ) {
// Translators: %s is duration in seconds.
$lastmsg = '' . esc_html__( 'ERROR:', 'backwpup' ) . ' ' . sprintf( esc_html__( 'Job has ended with errors in %s seconds. You must resolve the errors for correct execution.', 'backwpup' ), $logheader['runtime'] ) . '
';
} elseif ( $warnings > 0 ) {
// Translators: %s is duration in seconds.
$lastmsg = '' . esc_html__( 'WARNING:', 'backwpup' ) . ' ' . sprintf( esc_html__( 'backup created with warnings in %s seconds. Please resolve them for correct execution.', 'backwpup' ), $logheader['runtime'] ) . '
';
} else {
// Translators: %s is duration in seconds.
$lastmsg = sprintf( esc_html__( 'Backup created in %s seconds.', 'backwpup' ), $logheader['runtime'] );
}
$lasterrormsg = '';
$done = 1;
}
$filesystem = backwpup_wpfilesystem();
if ( '.gz' === substr( $logfile, -3 ) ) {
$logfiledata = $filesystem->get_contents( 'compress.zlib://' . $logfile );
} else {
$logfiledata = $filesystem->get_contents( $logfile );
}
if ( $logpos > 0 ) {
$logfiledata = substr( $logfiledata, $logpos );
}
preg_match( '/]*>/si', $logfiledata, $match );
if ( ! empty( $match[0] ) ) {
$startpos = strpos( $logfiledata, $match[0] ) + strlen( $match[0] );
} else {
$startpos = 0;
}
$endpos = stripos( $logfiledata, '' );
if ( false === $endpos ) {
$endpos = strlen( $logfiledata );
}
$length = strlen( $logfiledata ) - ( strlen( $logfiledata ) - $endpos ) - $startpos;
// Check if restart must done on ALTERNATE_WP_CRON.
if ( is_object( $job_object ) && defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) {
$restart = BackWPup_Job::get_jobrun_url( 'restartalt' );
if ( 0 === $job_object->pid && '' === $job_object->uniqid ) {
$restart_url = $restart['url'];
}
$last_update = microtime( true ) - $job_object->timestamp_last_update;
if ( empty( $job_object->pid ) && $last_update > 10 ) {
$restart_url = $restart['url'];
}
}
$data_ro_return = [
'log_pos' => strlen( $logfiledata ) + $logpos,
'log_text' => self::render_log_content( substr( $logfiledata, $startpos, $length ) ),
'warning_count' => $warnings,
'error_count' => $errors,
'running_time' => $runtime,
'step_percent' => $step_percent,
'on_step' => $onstep,
'last_msg' => $lastmsg,
'last_error_msg' => $lasterrormsg,
'sub_step_percent' => $substep_percent,
'restart_url' => $restart_url,
'job_done' => $done,
'step_done' => $step_done,
'step_todo' => $step_todo,
'substeps_todo' => $substeps_todo,
'substeps_done' => $substeps_done,
'job_id' => $job_object->job['jobid'],
];
wp_send_json( $data_ro_return );
}
/**
* Render log HTML through the shared log renderer when available.
*
* @param string $content Raw HTML log file fragment.
*
* @return string
*/
private static function render_log_content( string $content ): string {
$container = wpm_apply_filters_typed( '?object', 'backwpup_container', null );
if ( is_object( $container ) && method_exists( $container, 'has' ) && $container->has( 'log_facade' ) ) {
$log_facade = $container->get( 'log_facade' );
if ( is_object( $log_facade ) && method_exists( $log_facade, 'render_html' ) ) {
return $log_facade->render_html( $content );
}
}
$log_facade = new \WPMedia\BackWPup\Log\LogFacade();
return $log_facade->render_html( $content );
}
/**
* Get log file path.
*
* @param string $folder Folder Path.
* @param string|null $filename Filename.
* @return string
*
* @throws \InvalidArgumentException When empty filename.
*/
private static function get_logfile_path( string $folder, ?string $filename ): string {
if ( ! $filename ) {
throw new \InvalidArgumentException( 'Log file cannot be null.' );
}
$filename = basename( trim( $filename ) );
if (
preg_match( '/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9]{1,5}$/', $filename ) === 0
|| strpos( $filename, 'backwpup_log_' ) === false
) {
throw new \InvalidArgumentException( 'Invalidly formatted log filename passed.' );
}
return $folder . $filename;
}
/**
* Render extra controls in the table navigation area (above or below the table).
*
* @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
*/
protected function extra_tablenav( $which ) {
if ( 'top' !== $which ) { // Add the filter only at the top of the table.
return;
}
// Get the current "status" filter from the GET request.
$current_status_filter = '';
$nonce = $this->get_list_nonce();
if ( $nonce && wp_verify_nonce( $nonce, self::LIST_NONCE_ACTION ) ) {
$current_status_filter = filter_input( INPUT_GET, 'status', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
$current_status_filter = $current_status_filter ? sanitize_text_field( $current_status_filter ) : '';
}
?>
>