notices = $notices; $this->scan = $scan; $this->menu_card = $menu_card; $this->blocks_card = $blocks_card; $this->schedules_card = $schedules_card; $this->content_card = $content_card; $this->shortcodes_card = $shortcodes_card; } /** * Registers hooks. * * @return void */ public function register(): void { add_action( 'rest_api_init', array( $this, 'register_routes' ) ); } /** * Registers REST routes. * * @return void */ public function register_routes(): void { register_rest_route( SettingsController::ROUTE_NAMESPACE, '/dashboard', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_dashboard' ), 'permission_callback' => array( $this, 'can_view' ), ), ) ); register_rest_route( SettingsController::ROUTE_NAMESPACE, '/dashboard/dismiss', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'dismiss' ), 'permission_callback' => array( $this, 'can_view' ), 'args' => array( 'key' => array( 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_key', ), ), ), ) ); $this->register_rescan_route(); $this->register_menu_items_route(); $this->register_list_route( '/dashboard/blocks', 'get_blocks', array( $this->blocks_card, 'can_view' ) ); $this->register_list_route( '/dashboard/widgets', 'get_widgets', array( $this->blocks_card, 'can_view_widgets' ) ); $this->register_list_route( '/dashboard/schedules', 'get_schedules', array( $this->schedules_card, 'can_view' ) ); $this->register_list_route( '/dashboard/scheduled-content', 'get_scheduled_content', array( $this->content_card, 'can_view' ) ); $this->register_list_route( '/dashboard/shortcodes', 'get_shortcodes', array( $this->shortcodes_card, 'can_view' ) ); $this->register_list_route( '/dashboard/saved-rules', 'get_saved_rules', array( $this->shortcodes_card, 'can_view' ) ); } /** * Registers the route for one of a card's full lists. * * @param string $route Route, e.g. "/dashboard/blocks". * @param string $callback Method that returns a page. * @param callable $permission Who can read it. * @return void */ private function register_list_route( string $route, string $callback, callable $permission ): void { register_rest_route( SettingsController::ROUTE_NAMESPACE, $route, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, $callback ), 'permission_callback' => $permission, 'args' => array( 'page' => array( 'type' => 'integer', 'minimum' => 1, 'default' => 1, ), ), ), ) ); } /** * One page of the content with block rules that the user can edit. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_blocks( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->blocks_card->blocks_data( (int) $request->get_param( 'page' ), BlocksWidgetsCard::PER_PAGE ) ); } /** * One page of the active and upcoming schedules. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_schedules( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->schedules_card->data( (int) $request->get_param( 'page' ), ScheduledElementsCard::PER_PAGE ) ); } /** * One page of the active and upcoming scheduled content. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_scheduled_content( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->content_card->data( (int) $request->get_param( 'page' ), ScheduledContentCard::PER_PAGE ) ); } /** * One page of the posts that use the conditional shortcode. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_shortcodes( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->shortcodes_card->data( (int) $request->get_param( 'page' ), ShortcodesCard::PER_PAGE ) ); } /** * One page of the saved rules that are on, with how many shortcodes use each. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_saved_rules( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->shortcodes_card->saved_rules_data( (int) $request->get_param( 'page' ), ShortcodesCard::PER_PAGE ) ); } /** * One page of the classic widgets with rules. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_widgets( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->blocks_card->widgets_data( (int) $request->get_param( 'page' ), BlocksWidgetsCard::PER_PAGE ) ); } /** * Registers the route for the menu card's full list. * * @return void */ private function register_menu_items_route(): void { register_rest_route( SettingsController::ROUTE_NAMESPACE, '/dashboard/menu-items', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_menu_items' ), 'permission_callback' => array( $this->menu_card, 'can_view' ), 'args' => array( 'page' => array( 'type' => 'integer', 'minimum' => 1, 'default' => 1, ), ), ), ) ); } /** * One page of the menu items with rules. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response */ public function get_menu_items( WP_REST_Request $request ): WP_REST_Response { return new WP_REST_Response( $this->menu_card->data( (int) $request->get_param( 'page' ), MenuItemsCard::PER_PAGE ) ); } /** * Registers the rescan route. * * @return void */ private function register_rescan_route(): void { register_rest_route( SettingsController::ROUTE_NAMESPACE, '/dashboard/rescan', array( array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'rescan' ), 'permission_callback' => array( $this, 'can_rescan' ), ), ) ); } /** * Only administrators can rescan the content. * * @return bool */ public function can_rescan(): bool { return current_user_can( 'manage_options' ); } /** * Starts a new content index scan, replacing a running one. * * @return WP_REST_Response */ public function rescan(): WP_REST_Response { $this->scan->start(); return $this->get_dashboard(); } /** * Users who can open the Dashboard. * * @return bool */ public function can_view(): bool { return current_user_can( DashboardPage::CAPABILITY ); } /** * The Dashboard's data. `cards` only holds the cards the user can see; * more are added here as features are built. * * @return WP_REST_Response */ public function get_dashboard(): WP_REST_Response { // The Dashboard checks on a running scan every few seconds; each check // makes sure the scan's next batch is still scheduled. $this->scan->resume(); $cards = array(); if ( $this->menu_card->can_view() ) { $cards['menus'] = $this->menu_card->data(); } if ( $this->blocks_card->can_view() ) { $cards['blocks'] = $this->blocks_card->data(); } if ( $this->schedules_card->can_view() ) { $cards['schedules'] = $this->schedules_card->data(); } if ( $this->content_card->can_view() ) { $cards['content'] = $this->content_card->data(); } if ( $this->shortcodes_card->can_view() ) { $cards['shortcodes'] = $this->shortcodes_card->data(); } $can_rescan = $this->can_rescan(); return new WP_REST_Response( array( 'notices' => $this->notices->for_current_user(), 'scan' => $this->scan->progress(), 'canRescan' => $can_rescan, 'index' => $can_rescan ? $this->index_data() : null, 'cards' => (object) $cards, ) ); } /** * What the content index found, and when the last scan finished, for the * "Content index" card. Only for administrators, who can edit all content, * so the totals don't need checking per post. * * @return array{posts: int, blocks: int, menuItems: int, schedules: int, shortcodes: int, unpublished: int, lastScan: array{scanned: int, timeLabel: string}|null} */ private function index_data(): array { $summary = $this->scan->summary(); $last = $this->scan->last_scan(); return array( 'posts' => $summary['posts'], 'blocks' => $summary['blocks'], 'menuItems' => $summary['menu_items'], 'schedules' => $summary['schedules'], 'shortcodes' => $summary['shortcodes'], 'unpublished' => $summary['unpublished'], 'lastScan' => null === $last ? null : array( 'scanned' => $last['scanned'], 'timeLabel' => (string) wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $last['finished'] ), ), ); } /** * Dismisses a notice the current user can see. * * @param WP_REST_Request $request Request. * @phpstan-param WP_REST_Request> $request * @return WP_REST_Response|WP_Error */ public function dismiss( WP_REST_Request $request ) { if ( ! $this->notices->dismiss( (string) $request->get_param( 'key' ) ) ) { return new WP_Error( 'yangra_conditional_unknown_notice', __( 'This notice is not shown to you.', 'yangra-conditional' ), array( 'status' => 404 ) ); } return $this->get_dashboard(); } }