60, 'display' => __( 'Every minute (Sirius Press export)', 'sirius-press' ), ); return $schedules; } public static function schedule() { if ( ! wp_next_scheduled( self::CRON_HOOK ) ) { wp_schedule_event( time() + 60, 'sirius_minute', self::CRON_HOOK ); } } public static function unschedule() { $timestamp = wp_next_scheduled( self::CRON_HOOK ); if ( $timestamp ) { wp_unschedule_event( $timestamp, self::CRON_HOOK ); } } /** * Process a batch. * * @param int $limit Maximum rows to attempt. * @return array{done:int,failed:int,skipped:int,left:int} */ public static function run_batch( $limit = 10 ) { $result = array( 'done' => 0, 'failed' => 0, 'skipped' => 0, 'left' => 0, ); if ( SP_Settings::MODE_SERVER !== SP_Settings::mode() ) { // Manual mode: the server has no key, so there is nothing for cron // to do. Rows wait for a browser to sign them. $result['left'] = SPE_Queue::counts()[ SPE_Queue::STATUS_PENDING ]; return $result; } if ( ! SP_Settings::is_configured() ) { return $result; } $deadline = microtime( true ) + self::TIME_BUDGET; foreach ( SPE_Queue::claim( $limit ) as $row ) { if ( microtime( true ) > $deadline ) { break; } $outcome = self::process( $row ); if ( 'done' === $outcome ) { $result['done']++; } elseif ( 'skipped' === $outcome ) { $result['skipped']++; } else { $result['failed']++; } } $result['left'] = SPE_Queue::counts()[ SPE_Queue::STATUS_PENDING ]; return $result; } /** * One row. * * @return string 'done', 'skipped' or 'failed'. */ public static function process( $row ) { $body = self::build( $row ); if ( is_wp_error( $body ) ) { SPE_Queue::mark_failed( $row->id, $body->get_error_message() ); return 'failed'; } $hash = hash( 'sha256', $body ); if ( '' !== $row->content_hash && hash_equals( $row->content_hash, $hash ) ) { // Byte-identical to what is already published. SPE_Queue::mark_done( $row->id, $hash ); return 'skipped'; } $upload = SP_Gateway::put( $row->path, $body ); if ( empty( $upload['ok'] ) ) { SPE_Queue::mark_failed( $row->id, $upload['error'] ); self::report_failure( $row, $upload['error'] ); return 'failed'; } SPE_Queue::mark_done( $row->id, $hash ); /** * Fires after a path is published to the name's bucket. * * @param string $path * @param int $bytes */ do_action( 'sirius_press_exported', $row->path, strlen( $body ) ); return 'done'; } /** * Produce the bytes for a row. * * Assets are fetched and passed through untouched. Pages are fetched, * rewritten, and the assets they mention are queued behind them — which * is how a bulk export discovers the theme's stylesheet without anybody * having to list it. * * @return string|WP_Error */ public static function build( $row ) { $fetched = SPE_Renderer::fetch( $row->source_url ); if ( empty( $fetched['ok'] ) ) { return new WP_Error( 'sirius_export_fetch', $fetched['error'] ); } if ( 'asset' === $row->kind || SPE_Mapper::is_asset( $row->path ) ) { return $fetched['body']; } $rewritten = SPE_Renderer::rewrite( $fetched['body'], $row->path ); foreach ( $rewritten['assets'] as $path => $url ) { // Only queue an asset the first time it is seen; re-queueing it on // every page that links the stylesheet would reset its row to // pending forever and the queue would never empty. if ( '' === SPE_Queue::hash_at( $path ) ) { SPE_Queue::add( $path, $url, 'asset' ); } } return $rewritten['html']; } /** * Tell somebody when an export stops working. * * Only on the attempt that exhausts the retries, and only once per hour: * a misconfigured key fails on every row in the queue, and an inbox with * four hundred copies of the same message is indistinguishable from no * message at all. */ private static function report_failure( $row, $error ) { if ( (int) $row->attempts + 1 < SPE_Queue::MAX_ATTEMPTS ) { return; } if ( get_transient( 'sirius_export_failure_notified' ) ) { return; } set_transient( 'sirius_export_failure_notified', 1, HOUR_IN_SECONDS ); SP_Inbox::add( 0, __( 'A page could not be published to your name', 'sirius-press' ), sprintf( /* translators: 1: bucket path, 2: error message, 3: URL of the export screen. */ wp_kses_post( __( '%1$s failed to upload: %2$s

Other pages may be failing for the same reason. Open the export screen to see the queue.', 'sirius-press' ) ), esc_html( $row->path ), esc_html( $error ), esc_url( admin_url( 'admin.php?page=sirius-press-export' ) ) ), 'export' ); } /** Queue every exportable page on the site. */ public static function queue_full_site() { $queued = 0; foreach ( SPE_Mapper::full_site() as $path => $target ) { if ( SPE_Queue::add( $path, $target['url'], $target['kind'] ) ) { $queued++; } } return $queued; } }