pcoming'] ), $_GET['report_category'] ); if ( $echo ) { wp_send_json( $response ); die(); } return wp_json_encode( $response ); } /** * Responds to the paginated requests. * * @since 6.0.0 * * @param int $page The page of results we are fetching. * @param int $count The number of events we are requesting. * @param bool $upcoming If we want upcoming or past events. * @param string $category The category of event reports we are searching. * * @return mixed[] */ public function get_paginated_response( $page, $count, $upcoming, $category ) { $phase = $this->state->get_phase(); $filter = [ Event_Report::META_KEY_MIGRATION_CATEGORY => $category, Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_SUCCESS, 'upcoming' => $upcoming ]; $event_details = $this->get_events_and_has_more( $page, $count, $filter ); $renderer_args = [ 'state' => $this->state, 'report' => $this->site_report, 'text' => $this->text, 'event_reports' => $event_details['event_reports'] ]; $renderer = new Phase_View_Renderer( $phase . '-paginated', '/partials/event-items.php', $renderer_args, [ 'has_more' => $event_details['has_more'], 'append' => $upcoming, 'prepend' => ! $upcoming ] ); return $renderer->compile(); } /** * Builds the structured report HTML. * * @since 6.0.0 * * @return array */ protected function get_report() { // What phase are we in? $state = $this->state; $phase = $state->get_phase(); // Short-circuit if migration is not required. if ( $phase === State::PHASE_MIGRATION_NOT_REQUIRED ) { return [ 'key' => 'stop', 'html' => '', 'nodes' => [], 'poll' => false, ]; } $renderer = $this->get_renderer_for_phase( $phase ); return $renderer->compile(); } /** * Will fetch event reports for a particular filter, and check if there are more to request for that filter. * * @since 6.0.0 * * @param int $page Which page we are on. * @param int $count How many we want. * @param $filter * * @return array{ has_more:bool, event_reports:array } */ protected function get_events_and_has_more( $page, $count, $filter ) { $event_reports = $this->site_report->get_event_reports( $page, $count, $filter ); // Did we even have enough to fill our request? if ( count( $event_reports ) < $count ) { $has_more = false; } else { // If we did, lets see if there is another page. $has_more = ! empty( $this->events_repository->get_events_migrated( $page + 1, $count, $filter ) ); } return [ 'has_more' => $has_more, 'event_reports' => $event_reports ]; } /** * Construct the query args for the primary renderer template (not used for the node templates). * * @since 6.0.0 * * @param string $phase The current phase. * * @return array The primary renderer template args. */ protected function get_renderer_args( $phase ) { $count = 25; $renderer_args = [ 'state' => $this->state, 'report' => $this->site_report, 'text' => $this->text ]; switch ( $phase ) { case State::PHASE_MIGRATION_COMPLETE: case State::PHASE_MIGRATION_PROMPT: $renderer_args['preview_unsupported'] = (bool) $this->state->get( 'preview_unsupported' ); if ( $this->site_report->has_errors ) { $filter = [ Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_FAILURE, ]; $renderer_args['event_reports'] = $this->site_report->get_event_reports( 1, $count, $filter ); } else { // This should only handle first render - pagination should be handled elsewhere. $event_categories = tribe( Event_Report_Categories::class )->get_categories(); foreach ( $event_categories as $i => $category ) { $upcoming_filter = [ Event_Report::META_KEY_MIGRATION_CATEGORY => $category['key'], Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_SUCCESS, 'upcoming' => true ]; $past_filter = $upcoming_filter; $past_filter['upcoming'] = false; $upcoming_events = $this->get_events_and_has_more( 1, $count, $upcoming_filter ); $past_events = $this->get_events_and_has_more( 1, $count, $past_filter ); // Grab upcoming if any, else grab past events. $event_categories[ $i ] ['event_reports'] = empty( $upcoming_events['event_reports'] ) ? $past_events['event_reports'] : $upcoming_events['event_reports']; // No reports? Skip this category. if ( empty( $event_categories[ $i ] ['event_reports'] ) ) { unset( $event_categories[ $i ] ); continue; } $event_categories[ $i ]['has_upcoming'] = $upcoming_events['has_more']; $event_categories[ $i ]['upcoming_start_page'] = $upcoming_events['has_more'] ? 2 : 1; $event_categories[ $i ]['past_start_page'] = $past_events['has_more'] ? 2 : 1; // By default we show upcoming, but will fall back to past events if no upcoming. // We need to validate when we do this flip and confirm which we are showing and when we should show a "show more" button. $event_categories[ $i ]['has_past'] = ! empty( $upcoming_events['event_reports'] ) && ! empty( $past_events['event_reports'] ); } $renderer_args['event_categories'] = $event_categories; } break; case State::PHASE_CANCEL_COMPLETE: case State::PHASE_REVERT_COMPLETE: case State::PHASE_PREVIEW_PROMPT: case State::PHASE_MIGRATION_FAILURE_COMPLETE: $renderer_args['event_reports'] = $this->site_report->get_event_reports( 1, $count, [ Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_FAILURE ] ); break; } return $renderer_args; } /** * Based on the current phase, find the correct template file for the renderer. * * @since 6.0.0 * * @param string $phase The current phase. * * @return string|void The primary template file to load for this phase. */ protected function get_renderer_template( $phase ) { $phase = $phase === null ? State::PHASE_PREVIEW_PROMPT : $phase; // Is the Maintenance Mode view requesting the report? This changes how we handle the views. $is_maintenance_mode = ! empty( $_GET["is_maintenance_mode"] ); // Determine base directory for templates. $base_dir = $is_maintenance_mode ? "/maintenance-mode/phase" : "/phase"; // Base template is phase name. Some phases might change it with other logic. $template = $phase; switch ( $phase ) { case State::PHASE_MIGRATION_FAILURE_IN_PROGRESS: $template = State::PHASE_MIGRATION_IN_PROGRESS; case State::PHASE_PREVIEW_IN_PROGRESS: case State::PHASE_MIGRATION_IN_PROGRESS: return "$base_dir/$template.php"; case State::PHASE_CANCEL_COMPLETE: case State::PHASE_REVERT_COMPLETE: case State::PHASE_PREVIEW_PROMPT: case State::PHASE_MIGRATION_FAILURE_COMPLETE: // Maintenance mode and migration failure has templates for each phase. $specific_template = ( State::PHASE_MIGRATION_FAILURE_COMPLETE === $phase || $is_maintenance_mode ); if ( $specific_template ) { $template = $phase; } else { // Other phases / views have this specific template. $template = State::PHASE_PREVIEW_PROMPT; } return "$base_dir/$template.php"; default: return "$base_dir/$template.php"; } } /** * Determines if the frontend should poll for updates from the backend. * * @since 6.0.0 * * @param string $phase The current phase. * * @return bool Whether the frontend should continue polling. */ protected function should_renderer_poll( $phase ) { switch ( $phase ) { case State::PHASE_MIGRATION_COMPLETE: case State::PHASE_MIGRATION_PROMPT: case State::PHASE_CANCEL_COMPLETE: case State::PHASE_REVERT_COMPLETE: case State::PHASE_PREVIEW_PROMPT: case State::PHASE_MIGRATION_FAILURE_COMPLETE: return false; default: return true; } } /** * Will construct the appropriate templates and nodes to be compiled, for this phase in the migration. * * @since 6.0.0 * * @param string $phase The current phase of the migration. * * @return Phase_View_Renderer The configured Phase_View_Renderer for this particular phase. */ public function get_renderer_for_phase( $phase ) { /** * Filters the Phase_View_Renderer being constructed for this phase. * * @since 6.0.0 * * @param Phase_View_Renderer A reference to the Phase_View_Renderer that should be used. * Initially `null`. * @param string $phase The current phase we are in. */ $renderer = apply_filters( "tec_events_custom_tables_v1_migration_ajax_ui_renderer", null, $phase ); if ( $renderer instanceof Phase_View_Renderer ) { return $renderer; } $phase = $phase === null ? State::PHASE_PREVIEW_PROMPT : $phase; // Get the args. $renderer_args = $this->get_renderer_args( $phase ); $template = $this->get_renderer_template( $phase ); $renderer = new Phase_View_Renderer( $phase, $template, $renderer_args ); $renderer->should_poll( $this->should_renderer_poll( $phase ) ); switch ( $phase ) { case State::PHASE_MIGRATION_FAILURE_IN_PROGRESS: case State::PHASE_PREVIEW_IN_PROGRESS: case State::PHASE_MIGRATION_IN_PROGRESS: // * Warning, need a new report object here, state will have changed. $site_report = Site_Report::build(); $renderer->register_node( 'progress-bar', '.tec-ct1-upgrade-update-bar-container', '/partials/progress-bar.php', [ 'phase' => $phase, 'report' => $site_report, 'text' => $this->text ] ); break; } return $renderer; } /** * Handles the request from the Admin UI to start the migration and returns * a first report about its progress. * * @since 6.0.0 * * @param bool $echo Flag whether we echo or return json string. * * @return void|string The JSON-encoded data for the front-end. */ public function start_migration( $echo = true ) { check_ajax_referer( self::NONCE_ACTION ); $dry_run = ! empty( $_REQUEST['tec_events_custom_tables_v1_migration_dry_run'] ); // Log our start do_action( 'tribe_log', 'debug', 'Ajax: Start migration', [ 'source' => __CLASS__ . ' ' . __METHOD__ . ' ' . __LINE__, 'dry_run' => $dry_run, ] ); $this->process->start( $dry_run ); $response = $this->get_report(); // Make sure we flush before we start the migration. flush_rewrite_rules(); if ( $echo ) { wp_send_json( $response ); } return wp_json_encode( $response ); } /** * Handles the request from the Admin UI to cancel the migration and returns * a first report about its progress. * * @since 6.0.0 * * @param bool $echo Flag whether we echo or return json string. * * @return void|string The JSON-encoded data for the front-end. * */ public function cancel_migration( $echo = true ) { check_ajax_referer( self::NONCE_ACTION ); // Log our start do_action( 'tribe_log', 'debug', 'Ajax: Cancel migration', [ 'source' => __CLASS__ . ' ' . __METHOD__ . ' ' . __LINE__, ] ); // A cancel action is identical to an undo. $this->process->cancel(); $response = $this->get_report(); if ( $echo ) { wp_send_json( $response ); die(); } return wp_json_encode( $response ); } /** * Handles the request from the Admin UI to undo the migration and returns * a first report about its progress. * * @since 6.0.0 * * @param bool $echo Flag whether we echo or return json string. * * @return void|string The JSON-encoded data for the front-end. */ public function revert_migration( $echo = true ) { check_ajax_referer( self::NONCE_ACTION ); // Log our start do_action( 'tribe_log', 'debug', 'Ajax: Undo migration', [ 'source' => __CLASS__ . ' ' . __METHOD__ . ' ' . __LINE__, ] ); $this->process->revert(); $response = $this->get_report(); if ( $echo ) { wp_send_json( $response ); die(); } return wp_json_encode( $response ); } }pcoming'] ), $_GET['report_category'] ); if ( $echo ) { wp_send_json( $response ); die(); } return wp_json_encode( $response ); } /** * Responds to the paginated requests. * * @since 6.0.0 * * @param int $page The page of results we are fetching. * @param int $count The number of events we are requesting. * @param bool $upcoming If we want upcoming or past events. * @param string $category The category of event reports we are searching. * * @return mixed[] */ public function get_paginated_response( $page, $count, $upcoming, $category ) { $phase = $this->state->get_phase(); $filter = [ Event_Report::META_KEY_MIGRATION_CATEGORY => $category, Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_SUCCESS, 'upcoming' => $upcoming ]; $event_details = $this->get_events_and_has_more( $page, $count, $filter ); $renderer_args = [ 'state' => $this->state, 'report' => $this->site_report, 'text' => $this->text, 'event_reports' => $event_details['event_reports'] ]; $renderer = new Phase_View_Renderer( $phase . '-paginated', '/partials/event-items.php', $renderer_args, [ 'has_more' => $event_details['has_more'], 'append' => $upcoming, 'prepend' => ! $upcoming ] ); return $renderer->compile(); } /** * Builds the structured report HTML. * * @since 6.0.0 * * @return array */ protected function get_report() { // What phase are we in? $state = $this->state; $phase = $state->get_phase(); // Short-circuit if migration is not required. if ( $phase === State::PHASE_MIGRATION_NOT_REQUIRED ) { return [ 'key' => 'stop', 'html' => '', 'nodes' => [], 'poll' => false, ]; } $renderer = $this->get_renderer_for_phase( $phase ); return $renderer->compile(); } /** * Will fetch event reports for a particular filter, and check if there are more to request for that filter. * * @since 6.0.0 * * @param int $page Which page we are on. * @param int $count How many we want. * @param $filter * * @return array{ has_more:bool, event_reports:array } */ protected function get_events_and_has_more( $page, $count, $filter ) { $event_reports = $this->site_report->get_event_reports( $page, $count, $filter ); // Did we even have enough to fill our request? if ( count( $event_reports ) < $count ) { $has_more = false; } else { // If we did, lets see if there is another page. $has_more = ! empty( $this->events_repository->get_events_migrated( $page + 1, $count, $filter ) ); } return [ 'has_more' => $has_more, 'event_reports' => $event_reports ]; } /** * Construct the query args for the primary renderer template (not used for the node templates). * * @since 6.0.0 * * @param string $phase The current phase. * * @return array The primary renderer template args. */ protected function get_renderer_args( $phase ) { $count = 25; $renderer_args = [ 'state' => $this->state, 'report' => $this->site_report, 'text' => $this->text ]; switch ( $phase ) { case State::PHASE_MIGRATION_COMPLETE: case State::PHASE_MIGRATION_PROMPT: $renderer_args['preview_unsupported'] = (bool) $this->state->get( 'preview_unsupported' ); if ( $this->site_report->has_errors ) { $filter = [ Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_FAILURE, ]; $renderer_args['event_reports'] = $this->site_report->get_event_reports( 1, $count, $filter ); } else { // This should only handle first render - pagination should be handled elsewhere. $event_categories = tribe( Event_Report_Categories::class )->get_categories(); foreach ( $event_categories as $i => $category ) { $upcoming_filter = [ Event_Report::META_KEY_MIGRATION_CATEGORY => $category['key'], Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_SUCCESS, 'upcoming' => true ]; $past_filter = $upcoming_filter; $past_filter['upcoming'] = false; $upcoming_events = $this->get_events_and_has_more( 1, $count, $upcoming_filter ); $past_events = $this->get_events_and_has_more( 1, $count, $past_filter ); // Grab upcoming if any, else grab past events. $event_categories[ $i ] ['event_reports'] = empty( $upcoming_events['event_reports'] ) ? $past_events['event_reports'] : $upcoming_events['event_reports']; // No reports? Skip this category. if ( empty( $event_categories[ $i ] ['event_reports'] ) ) { unset( $event_categories[ $i ] ); continue; } $event_categories[ $i ]['has_upcoming'] = $upcoming_events['has_more']; $event_categories[ $i ]['upcoming_start_page'] = $upcoming_events['has_more'] ? 2 : 1; $event_categories[ $i ]['past_start_page'] = $past_events['has_more'] ? 2 : 1; // By default we show upcoming, but will fall back to past events if no upcoming. // We need to validate when we do this flip and confirm which we are showing and when we should show a "show more" button. $event_categories[ $i ]['has_past'] = ! empty( $upcoming_events['event_reports'] ) && ! empty( $past_events['event_reports'] ); } $renderer_args['event_categories'] = $event_categories; } break; case State::PHASE_CANCEL_COMPLETE: case State::PHASE_REVERT_COMPLETE: case State::PHASE_PREVIEW_PROMPT: case State::PHASE_MIGRATION_FAILURE_COMPLETE: $renderer_args['event_reports'] = $this->site_report->get_event_reports( 1, $count, [ Event_Report::META_KEY_MIGRATION_PHASE => Event_Report::META_VALUE_MIGRATION_PHASE_MIGRATION_FAILURE ] ); break; } return $renderer_args; } /** * Based on the current phase, find the correct template file for the renderer. * * @since 6.0.0 * * @param string $phase The current phase. * * @return string|void The primary template file to load for this phase. */ protected function get_renderer_template( $phase ) { $phase = $phase === null ? State::PHASE_PREVIEW_PROMPT : $phase; // Is the Maintenance Mode view requesting the report? This changes how we handle the views. $is_maintenance_mode = ! empty( $_GET["is_maintenance_mode"] ); // Determine base directory for templates. $base_dir = $is_maintenance_mode ? "/maintenance-mode/phase" : "/phase"; // Base template is phase name. Some phases might change it with other logic. $template = $phase; switch ( $phase ) { case State::PHASE_MIGRATION_FAILURE_IN_PROGRESS: $template = State::PHASE_MIGRATION_IN_PROGRESS; case State::PHASE_PREVIEW_IN_PROGRESS: case State::PHASE_MIGRATION_IN_PROGRESS: return "$base_dir/$template.php"; case State::PHASE_CANCEL_COMPLETE: case State::PHASE_REVERT_COMPLETE: case State::PHASE_PREVIEW_PROMPT: case State::PHASE_MIGRATION_FAILURE_COMPLETE: // Maintenance mode and migration failure has templates for each phase. $specific_template = ( State::PHASE_MIGRATION_FAILURE_COMPLETE === $phase || $is_maintenance_mode ); if ( $specific_template ) { $template = $phase; } else { // Other phases / views have this specific template. $template = State::PHASE_PREVIEW_PROMPT; } return "$base_dir/$template.php"; default: return "$base_dir/$template.php"; } } /** * Determines if the frontend should poll for updates from the backend. * * @since 6.0.0 * * @param string $phase The current phase. * * @return bool Whether the frontend should continue polling. */ protected function should_renderer_poll( $phase ) { switch ( $phase ) { case State::PHASE_MIGRATION_COMPLETE: case State::PHASE_MIGRATION_PROMPT: case State::PHASE_CANCEL_COMPLETE: case State::PHASE_REVERT_COMPLETE: case State::PHASE_PREVIEW_PROMPT: case State::PHASE_MIGRATION_FAILURE_COMPLETE: return false; default: return true; } } /** * Will construct the appropriate templates and nodes to be compiled, for this phase in the migration. * * @since 6.0.0 * * @param string $phase The current phase of the migration. * * @return Phase_View_Renderer The configured Phase_View_Renderer for this particular phase. */ public function get_renderer_for_phase( $phase ) { /** * Filters the Phase_View_Renderer being constructed for this phase. * * @since 6.0.0 * * @param Phase_View_Renderer A reference to the Phase_View_Renderer that should be used. * Initially `null`. * @param string $phase The current phase we are in. */ $renderer = apply_filters( "tec_events_custom_tables_v1_migration_ajax_ui_renderer", null, $phase ); if ( $renderer instanceof Phase_View_Renderer ) { return $renderer; } $phase = $phase === null ? State::PHASE_PREVIEW_PROMPT : $phase; // Get the args. $renderer_args = $this->get_renderer_args( $phase ); $template = $this->get_renderer_template( $phase ); $renderer = new Phase_View_Renderer( $phase, $template, $renderer_args ); $renderer->should_poll( $this->should_renderer_poll( $phase ) ); switch ( $phase ) { case State::PHASE_MIGRATION_FAILURE_IN_PROGRESS: case State::PHASE_PREVIEW_IN_PROGRESS: case State::PHASE_MIGRATION_IN_PROGRESS: // * Warning, need a new report object here, state will have changed. $site_report = Site_Report::build(); $renderer->register_node( 'progress-bar', '.tec-ct1-upgrade-update-bar-container', '/partials/progress-bar.php', [ 'phase' => $phase, 'report' => $site_report, 'text' => $this->text ] ); break; } return $renderer; } /** * Handles the request from the Admin UI to start the migration and returns * a first report about its progress. * * @since 6.0.0 * * @param bool $echo Flag whether we echo or return json string. * * @return void|string The JSON-encoded data for the front-end. */ public function start_migration( $echo = true ) { check_ajax_referer( self::NONCE_ACTION ); $dry_run = ! empty( $_REQUEST['tec_events_custom_tables_v1_migration_dry_run'] ); // Log our start do_action( 'tribe_log', 'debug', 'Ajax: Start migration', [ 'source' => __CLASS__ . ' ' . __METHOD__ . ' ' . __LINE__, 'dry_run' => $dry_run, ] ); $this->process->start( $dry_run ); $response = $this->get_report(); // Make sure we flush before we start the migration. flush_rewrite_rules(); if ( $echo ) { wp_send_json( $response ); } return wp_json_encode( $response ); } /** * Handles the request from the Admin UI to cancel the migration and returns * a first report about its progress. * * @since 6.0.0 * * @param bool $echo Flag whether we echo or return json string. * * @return void|string The JSON-encoded data for the front-end. * */ public function cancel_migration( $echo = true ) { check_ajax_referer( self::NONCE_ACTION ); // Log our start do_action( 'tribe_log', 'debug', 'Ajax: Cancel migration', [ 'source' => __CLASS__ . ' ' . __METHOD__ . ' ' . __LINE__, ] ); // A cancel action is identical to an undo. $this->process->cancel(); $response = $this->get_report(); if ( $echo ) { wp_send_json( $response ); die(); } return wp_json_encode( $response ); } /** * Handles the request from the Admin UI to undo the migration and returns * a first report about its progress. * * @since 6.0.0 * * @param bool $echo Flag whether we echo or return json string. * * @return void|string The JSON-encoded data for the front-end. */ public function revert_migration( $echo = true ) { check_ajax_referer( self::NONCE_ACTION ); // Log our start do_action( 'tribe_log', 'debug', 'Ajax: Undo migration', [ 'source' => __CLASS__ . ' ' . __METHOD__ . ' ' . __LINE__, ] ); $this->process->revert(); $response = $this->get_report(); if ( $echo ) { wp_send_json( $response ); die(); } return wp_json_encode( $response ); } }