'logs', 'singular' => 'log', 'ajax' => true, ] ); $this->log_folder = get_site_option( 'backwpup_cfg_logfolder' ); $this->log_folder = BackWPup_File::get_absolute_path( $this->log_folder ); $this->log_folder = untrailingslashit( $this->log_folder ); } /** * 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; } /** * User can. * * @return bool True if user has the right capabilities, false otherwise */ public function ajax_user_can() { return current_user_can( 'backwpup_logs' ); } /** * Prepare Items. */ public function prepare_items() { $this->job_types = BackWPup::get_job_types(); $per_page = $this->get_items_per_page( 'backwpuplogs_per_page' ); if ( empty( $per_page ) || $per_page < 1 ) { $per_page = 20; } // Load logs. $logfiles = []; if ( is_readable( $this->log_folder ) ) { $dir = new BackWPup_Directory( $this->log_folder ); foreach ( $dir as $file ) { if ( $file->isFile() && $file->isReadable() && strpos( $file->getFilename(), 'backwpup_log_' ) !== false && strpos( $file->getFilename(), '.html' ) !== false ) { $logfiles[ $file->getMTime() ] = $file->getFilename(); } } } // Ordering. $order = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $order = $order ? sanitize_key( $order ) : 'desc'; $order = in_array( $order, [ 'asc', 'desc' ], true ) ? $order : 'desc'; $orderby = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $orderby = $orderby ? sanitize_key( $orderby ) : 'time'; $orderby = in_array( $orderby, [ 'time' ], true ) ? $orderby : 'time'; if ( 'time' === $orderby ) { if ( 'asc' === $order ) { ksort( $logfiles ); } else { krsort( $logfiles ); } } // By page. $start = intval( ( $this->get_pagenum() - 1 ) * $per_page ); $end = $start + $per_page; if ( $end > count( $logfiles ) ) { $end = count( $logfiles ); } $this->items = []; $i = -1; foreach ( $logfiles as $mtime => $logfile ) { ++$i; if ( $i < $start ) { continue; } if ( $i >= $end ) { break; } $this->items[ $mtime ] = BackWPup_Job::read_logheader( $this->log_folder . '/' . $logfile ); $this->items[ $mtime ]['file'] = $logfile; } $this->set_pagination_args( [ 'total_items' => count( $logfiles ), 'per_page' => $per_page, 'orderby' => $orderby, 'order' => $order, ] ); } /** * {@inheritdoc} */ public function get_sortable_columns() { return [ 'time' => [ 'time', false ], ]; } /** * {@inheritdoc} */ public function no_items() { esc_html_e( 'No Logs.', 'backwpup' ); } /** * {@inheritdoc} */ public function get_bulk_actions() { if ( ! $this->has_items() ) { return []; } $actions = []; $actions['delete'] = __( 'Delete', 'backwpup' ); return $actions; } /** * {@inheritdoc} */ public function get_columns() { $posts_columns = []; $posts_columns['cb'] = ''; $posts_columns['time'] = __( 'Time', 'backwpup' ); $posts_columns['job'] = __( 'Job', 'backwpup' ); $posts_columns['status'] = __( 'Status', 'backwpup' ); $posts_columns['type'] = __( 'Type', 'backwpup' ); $posts_columns['size'] = __( 'Size', 'backwpup' ); $posts_columns['runtime'] = __( 'Runtime', 'backwpup' ); return $posts_columns; } /** * Render the bulk actions checkbox. * * @param array $item Item data. * * @return string */ public function column_cb( $item ) { return ''; } /** * The time column. * * @param array $item Item data. * * @return string */ public function column_time( $item ) { return sprintf( // translators: %1$s: date, %2$s: time. esc_html__( '%1$s at %2$s', 'backwpup' ), esc_html( self::format_log_date( (int) $item['logtime'] ) ), esc_html( self::format_log_time( (int) $item['logtime'] ) ) ); } /** * The type column. * * @param array $item Item data. * * @return string */ public function column_type( $item ) { $r = ''; $types = explode( '+', (string) $item['type'] ); if ( ! empty( $types ) ) { foreach ( $types as $type ) { if ( isset( $this->job_types[ $type ] ) ) { $r .= esc_html( $this->job_types[ $type ]->info['name'] ) . '
'; } else { $r .= esc_html( $type ) . '
'; } } } return $r; } /** * The job column. * * @param array $item Item data. * * @return string */ public function column_job( $item ) { $log_name = str_replace( [ '.html', '.gz' ], '', basename( (string) $item['file'] ) ); $view_url = admin_url( 'admin-ajax.php' ) . '?&action=backwpup_view_log&log=' . $log_name . '&_ajax_nonce=' . wp_create_nonce( 'view-log_' . $log_name ) . '&TB_iframe=true&width=640&height=440'; // translators: %d: job ID. $job_id_label = sprintf( __( 'Job ID: %d', 'backwpup' ), absint( $item['jobid'] ) ); $title = esc_attr( $item['file'] . "\n" . $job_id_label ); $r = '' . esc_html( ! empty( $item['name'] ) ? $item['name'] : $item['file'] ) . ''; $actions = []; $actions['view'] = '' . esc_html__( 'View', 'backwpup' ) . ''; if ( current_user_can( 'backwpup_logs_delete' ) ) { $actions['delete'] = '' . esc_html__( 'Delete', 'backwpup' ) . ''; } $actions['download'] = '' . esc_html__( 'Download', 'backwpup' ) . ''; $r .= $this->row_actions( $actions ); return $r; } /** * The status column. * * @param array $item Item data. * * @return string */ public function column_status( $item ) { $r = ''; $backup_status = $this->get_backup_status_for_item( $item ); if ( 'failed' === $backup_status ) { $r .= '' . esc_html__( 'Failed', 'backwpup' ) . ''; if ( ! empty( $item['errors'] ) || ! empty( $item['warnings'] ) ) { $r .= '
'; } } if ( $item['errors'] ) { $errors_text = sprintf( // translators: %d: number of errors. _n( '%d ERROR', '%d ERRORS', $item['errors'], 'backwpup' ), absint( $item['errors'] ) ); $r .= sprintf( '%s
', esc_html( $errors_text ) ); } if ( $item['warnings'] ) { $warnings_text = sprintf( // translators: %d: number of warnings. _n( '%d WARNING', '%d WARNINGS', $item['warnings'], 'backwpup' ), absint( $item['warnings'] ) ); $r .= sprintf( '%s
', esc_html( $warnings_text ) ); } if ( ! $item['errors'] && ! $item['warnings'] && 'failed' !== $backup_status ) { $r .= '' . esc_html__( 'O.K.', 'backwpup' ) . ''; } return $r; } /** * Resolve the persisted backup status for a log item. * * @param array $item Item data. * * @return string */ private function get_backup_status_for_item( array $item ): string { $logfile = isset( $item['file'] ) ? basename( (string) $item['file'] ) : ''; if ( '' === $logfile || '' === $this->log_folder ) { return ''; } $container = wpm_apply_filters_typed( '?object', 'backwpup_container', null ); if ( ! $container || ! method_exists( $container, 'get' ) ) { return ''; } $database = $container->get( 'backwpup_database' ); if ( ! $database || ! method_exists( $database, 'get_status_by_logfile' ) ) { return ''; } return (string) $database->get_status_by_logfile( $this->log_folder . '/' . $logfile ); } /** * The size column. * * @param array $item Item data. * * @return string */ public function column_size( $item ) { if ( ! empty( $item['backupfilesize'] ) ) { return esc_html( size_format( $item['backupfilesize'], 2 ) ); } return esc_html__( 'Log only', 'backwpup' ); } /** * The runtime column. * * @param array $item Item data. * * @return string */ public function column_runtime( $item ) { return absint( $item['runtime'] ) . ' ' . esc_html__( 'seconds', 'backwpup' ); } /** * Load. */ public static function load() { global $current_user; // Create table. self::$listtable = new BackWPup_Page_Logs(); switch ( self::$listtable->current_action() ) { // Delete log. case 'delete': if ( ! current_user_can( 'backwpup_logs_delete' ) ) { break; } if ( isset( $_GET['logfiles'] ) && is_array( $_GET['logfiles'] ) ) { check_admin_referer( 'bulk-logs' ); // Unslash and sanitize input. $logfiles = array_map( 'sanitize_text_field', wp_unslash( $_GET['logfiles'] ) ); foreach ( $logfiles as $logfile ) { $logfile = basename( (string) $logfile ); $file_path = self::$listtable->log_folder . '/' . $logfile; $is_writable = wp_is_writable( $file_path ); if ( $is_writable && ! is_dir( $file_path ) && ! is_link( $file_path ) ) { wp_delete_file( $file_path ); } } } break; // Download log. case 'download': check_admin_referer( 'download_backwpup_logs', 'download_backwpup_logs' ); $file = filter_input( INPUT_GET, 'file', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( empty( $file ) ) { break; } $log_file = trailingslashit( self::$listtable->log_folder ) . basename( trim( (string) $file ) ); $log_file = realpath( BackWPup_Sanitize_Path::sanitize_path( $log_file ) ); if ( ! $log_file || ! is_readable( $log_file ) || is_dir( $log_file ) || is_link( $log_file ) ) { header( 'HTTP/1.0 404 Not Found' ); exit(); } $capability = 'backwpup_logs'; $download_handler = new BackWpup_Download_Handler( new BackWPup_Download_File( $log_file, function ( BackWPup_Download_File_Interface $obj ) { $obj->clean_ob() ->headers(); // phpcs:ignore echo backwpup_wpfilesystem()->get_contents($obj->filepath()); exit(); }, $capability ), 'download_backwpup_logs', $capability, 'download' ); $download_handler->handle(); break; } // Save per page. $screen_options_apply = filter_input( INPUT_POST, 'screen-options-apply', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $screen_options = filter_input( INPUT_POST, 'wp_screen_options', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY ); if ( ! empty( $screen_options_apply ) && is_array( $screen_options ) ) { $screen_options = wp_unslash( $screen_options ); $option = isset( $screen_options['option'] ) ? sanitize_text_field( $screen_options['option'] ) : ''; $value = isset( $screen_options['value'] ) ? absint( $screen_options['value'] ) : 0; if ( 'backwpuplogs_per_page' === $option ) { check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' ); if ( $value > 0 && $value < 1000 ) { update_user_option( $current_user->ID, 'backwpuplogs_per_page', $value ); wp_safe_redirect( remove_query_arg( [ 'pagenum', 'apage', 'paged' ], wp_get_referer() ) ); exit; } } } add_screen_option( 'per_page', [ 'label' => __( 'Logs', 'backwpup' ), 'default' => 20, 'option' => 'backwpuplogs_per_page', ] ); self::$listtable->prepare_items(); } /** * Output css. */ public static function admin_print_styles() { ?>

display(); ?>
[ 'lang' => true, ], 'head' => [], 'meta' => [ 'charset' => true, 'name' => true, 'content' => true, 'http-equiv' => true, ], 'title' => [], 'body' => [ 'style' => true, ], 'span' => [ 'style' => true, 'datetime' => true, 'title' => true, ], 'br' => [], ]; if ( file_exists( $log_file . '.html' ) && is_readable( $log_file . '.html' ) ) { $content = (string) $filesystem->get_contents( $log_file . '.html' ); echo wp_kses( self::render_log_content( $content ), $allowed_html ); } elseif ( file_exists( $log_file . '.html.gz' ) && is_readable( $log_file . '.html.gz' ) ) { $content = (string) $filesystem->get_contents( 'compress.zlib://' . $log_file . '.html.gz' ); echo wp_kses( self::render_log_content( $content ), $allowed_html ); } else { exit( esc_html__( 'Logfile not found!', 'backwpup' ) ); } exit(); } /** * Render log HTML through the shared log renderer when available. * * @param string $content Raw HTML log file content. * * @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 ); } /** * Format a UTC timestamp for the site date format. * * @param int $timestamp UTC timestamp. * * @return string */ private static function format_log_date( int $timestamp ): 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, 'format_date' ) ) { return $log_facade->format_date( $timestamp ); } } $log_facade = new \WPMedia\BackWPup\Log\LogFacade(); return $log_facade->format_date( $timestamp ); } /** * Format a UTC timestamp for the site time format. * * @param int $timestamp UTC timestamp. * * @return string */ private static function format_log_time( int $timestamp ): 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, 'format_time' ) ) { return $log_facade->format_time( $timestamp ); } } $log_facade = new \WPMedia\BackWPup\Log\LogFacade(); return $log_facade->format_time( $timestamp ); } }