` the tab's script moves by * calling the step action in a loop. Without the script the run still * finishes through WP-Cron, and reloading the tab shows how far it got. */ final class ImportTab { /** Script handle. */ public const SCRIPT = 'ops404-al-history-import'; /** * Enqueue the progress script. Called by SettingsPage::assets() on this * plugin's screen only. */ public static function enqueue(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- which tab is showing; display only. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; if ( 'import' !== $tab ) { return; } wp_enqueue_script( self::SCRIPT, plugins_url( 'assets/js/history-import.js', OPS_ACTIVITY_LOG_FILE ), array(), OPS_ACTIVITY_LOG_VERSION, true ); wp_localize_script( self::SCRIPT, 'ops404AlImport', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'action' => ImportActions::STEP_ACTION, 'failed' => __( 'The import paused because a step failed. It will carry on in the background, or when you reload this page.', '404ops-activity-log' ), /* translators: 1: rows processed so far, 2: rows in all. */ 'counter' => __( '%1$s of %2$s processed', '404ops-activity-log' ), ) ); } /** * Render the whole tab. */ public static function render(): void { self::render_result_notice(); echo '

' . esc_html__( 'Import history from another activity log', '404ops-activity-log' ) . '

'; echo '

' . esc_html__( 'Switching from Simple History, WP Activity Log or Stream? Bring their history with you. The other plugin\'s tables are only read, never changed, and it can be deactivated first.', '404ops-activity-log' ) . '

'; self::render_rules(); foreach ( Importer::sources() as $source ) { // The network source (#8) is the super admins': a main-site // administrator does not see it (ImportActions refuses it too). if ( ! current_user_can( Importer::capability( $source->key() ) ) ) { continue; } self::render_source( $source ); } } /** * What every imported row is held to, stated before anyone presses Import. */ private static function render_rules(): void { $cutoff = Importer::retention_cutoff(); echo ''; } /** * The notice for the result an action redirected back with. Only a known * code is ever shown. */ private static function render_result_notice(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only. $code = isset( $_GET[ ImportActions::RESULT_ARG ] ) ? sanitize_key( wp_unslash( $_GET[ ImportActions::RESULT_ARG ] ) ) : ''; if ( ! in_array( $code, ImportActions::RESULTS, true ) ) { return; } $messages = array( 'started' => __( 'Import started. It runs in the background; keep this page open to watch it, or come back later.', '404ops-activity-log' ), 'running' => __( 'That import is already running.', '404ops-activity-log' ), 'empty' => __( 'That source has no events to import.', '404ops-activity-log' ), 'unsupported' => __( 'That source cannot be imported. See the reason below.', '404ops-activity-log' ), 'cancelled' => __( 'Import stopped. Events already imported are kept; starting again skips them.', '404ops-activity-log' ), 'not_running' => __( 'That import was not running.', '404ops-activity-log' ), 'bad_request' => __( 'That request was not understood.', '404ops-activity-log' ), 'bad_nonce' => __( 'The link has expired. Reload the page and try again.', '404ops-activity-log' ), 'forbidden' => __( 'You are not allowed to import history.', '404ops-activity-log' ), ); $is_error = ! in_array( $code, array( 'started', 'cancelled', 'empty' ), true ); echo '

' . esc_html( $messages[ $code ] ) . '

'; } /** * One source's panel. * * @param Source $source The source. */ private static function render_source( Source $source ): void { $key = $source->key(); $detection = $source->detect(); echo '
'; echo '

' . esc_html( $source->label() ) . '

'; if ( Importer::targets_network( $key ) ) { echo '

' . esc_html__( 'What Stream recorded in the network admin (network settings, network plugin activation, sites). These events belong to no site: they are imported into the network log, under the main site\'s settings.', '404ops-activity-log' ) . '

'; } if ( Detection::ABSENT === $detection->status ) { echo '

' . esc_html( sprintf( /* translators: %s: plugin name. */ __( 'No %s tables were found on this site.', '404ops-activity-log' ), $source->label() ) ) . '

'; return; } if ( Detection::UNSUPPORTED === $detection->status ) { echo '

' . esc_html( $detection->reason ) . '

'; return; } $preview = $source->preview(); $state = Importer::state( $key ); echo ''; self::row( __( 'Tables', '404ops-activity-log' ), $detection->shape ); self::row( __( 'Events', '404ops-activity-log' ), number_format_i18n( $preview['count'] ) ); self::row( __( 'Date range (UTC)', '404ops-activity-log' ), null === $preview['first'] ? '—' : $preview['first'] . ' – ' . $preview['last'] ); echo '
'; self::render_progress( $key, $state ); if ( Importer::RUNNING === $state['status'] ) { self::action_form( ImportActions::OP_CANCEL, $key, __( 'Stop import', '404ops-activity-log' ), 'button-secondary' ); } elseif ( $preview['count'] > 0 ) { $label = Importer::IDLE === $state['status'] ? __( 'Import history', '404ops-activity-log' ) : __( 'Import new events', '404ops-activity-log' ); self::action_form( ImportActions::OP_START, $key, $label, 'button-primary' ); } echo ''; } /** * The progress bar and the run's counts. * * @param string $key Source key. * @param array $state Importer::state(). */ private static function render_progress( string $key, array $state ): void { if ( Importer::IDLE === $state['status'] ) { return; } $progress = ImportActions::progress( $state ); $labels = array( Importer::RUNNING => __( 'Importing…', '404ops-activity-log' ), Importer::DONE => __( 'Last import finished.', '404ops-activity-log' ), Importer::CANCELLED => __( 'Last import was stopped.', '404ops-activity-log' ), ); echo '
'; echo '

' . esc_html( $labels[ $progress['status'] ] ?? '' ) . '

'; echo ''; echo '

' . esc_html( sprintf( /* translators: 1: rows processed so far, 2: rows in all. */ __( '%1$s of %2$s processed', '404ops-activity-log' ), number_format_i18n( $progress['processed'] ), number_format_i18n( $progress['total'] ) ) ) . '

'; echo '
'; } /** * @param string $label Header cell. * @param string $value Value cell. */ private static function row( string $label, string $value ): void { echo '' . esc_html( $label ) . '' . esc_html( $value ) . ''; } /** * @param string $op Operation. * @param string $key Source key. * @param string $label Button label. * @param string $button Button class. */ private static function action_form( string $op, string $key, string $label, string $button ): void { echo '
'; echo ''; echo ''; echo ''; wp_nonce_field( ImportActions::nonce_action( $op, $key ), '_wpnonce', false ); echo ''; echo '
'; } }