checklist = $checklist; $this->promos = $promos; $this->growth_tools = $growth_tools; } /** * Register hooks, only while the current request is the checklist page. * * @since 4.10.0 */ public function hooks(): void { if ( ! wp_mail_smtp()->get_admin()->is_admin_page( 'setup-checklist' ) ) { return; } add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'in_admin_header', [ $this, 'hide_admin_notices' ], PHP_INT_MAX ); } /** * Suppress all admin notices on the checklist page. * * WordPress relocates a notice printed after the first

into the title bar, so * this runs on `in_admin_header`, before the `admin_notices` hooks fire. * * @since 4.10.0 */ public function hide_admin_notices(): void { remove_all_actions( 'admin_notices' ); remove_all_actions( 'all_admin_notices' ); } /** * Get the page's stable admin URL. * * @since 4.10.0 * * @return string */ public static function get_url(): string { return wp_mail_smtp()->get_admin()->get_admin_page_url( self::SLUG ); } /** * Enqueue the page assets. * * @since 4.10.0 */ public function enqueue_assets(): void { wp_enqueue_style( 'wp-mail-smtp-setup-checklist', wp_mail_smtp()->assets_url . '/css/smtp-setup-checklist.min.css', [ 'wp-mail-smtp-admin' ], WPMS_PLUGIN_VER ); wp_enqueue_script( 'wp-mail-smtp-setup-checklist', wp_mail_smtp()->assets_url . '/js/smtp-setup-checklist.min.js', [ 'jquery', 'wp-mail-smtp-admin', 'wp-mail-smtp-admin-jconfirm' ], WPMS_PLUGIN_VER, true ); wp_localize_script( 'wp-mail-smtp-setup-checklist', 'wp_mail_smtp_setup_checklist', [ 'ajax_url' => admin_url( 'admin-ajax.php' ), 'error' => esc_html__( 'Something went wrong. Please try again.', 'wp-mail-smtp' ), 'dismiss' => [ 'title' => esc_html__( 'Are you sure?', 'wp-mail-smtp' ), 'content' => esc_html__( 'This will permanently dismiss the Setup Checklist, and you will not be able to bring it back. This cannot be undone.', 'wp-mail-smtp' ), 'confirm' => esc_html__( 'Yes, dismiss', 'wp-mail-smtp' ), 'cancel' => esc_html__( 'Cancel', 'wp-mail-smtp' ), 'error' => esc_html__( 'Something went wrong dismissing the checklist. Please try again.', 'wp-mail-smtp' ), ], 'plugins' => [ 'installing' => esc_html__( 'Installing…', 'wp-mail-smtp' ), 'activating' => esc_html__( 'Activating…', 'wp-mail-smtp' ), 'activate' => esc_html__( 'Activate', 'wp-mail-smtp' ), 'installed' => esc_html__( 'Installed', 'wp-mail-smtp' ), ], ] ); } /** * Render the page. * * @since 4.10.0 */ public function output(): void { echo '
'; $this->render_page( $this->view_sections(), wp_mail_smtp()->is_pro() ? [] : [ 'tiles' => ( new ProFeatures() )->get_tiles(), 'upgrade_cta' => $this->promos->upgrade_cta(), ], [ 'tiles' => $this->growth_tools->get_tiles(), ] ); echo '
'; } /** * Render the checklist page markup: the section list plus the promo sections. * * @since 4.10.0 * * @param array $sections Sections, each with `id`, `title`, `is_complete`, and `items` * (item view-models tagged `type` => `item`). * @param array $features Feature promo (Lite only, empty on Pro): `tiles` and * `upgrade_cta` (`label`, `url`, `chip`). * @param array $growth_tools Growth-tools promo: `tiles`. */ private function render_page( array $sections, array $features, array $growth_tools ): void { ?>

%2$s', esc_attr( $badge_classes ), esc_html( $badge_label ) ); ?>
render_item( $item ); } ?>
promos->render_header( 'wpms-setup-checklist-promo-features', __( 'Take Your Email Deliverability to the Next Level', 'wp-mail-smtp' ) ); ?>
$tile ], true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_mail_smtp_render() returns escaped HTML. } ?>
promos->render_footer( $features['upgrade_cta'] ); ?>
promos->render_header( 'wpms-setup-checklist-promo-growth-tools', __( 'Set Up Recommended Growth Tools', 'wp-mail-smtp' ), __( 'Take your website to the next level with our sister plugins.', 'wp-mail-smtp' ) ); ?>
$tile ], true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_mail_smtp_render() returns escaped HTML. } ?>

render_item_form_field( $item['form'] ); } foreach ( $item['ctas'] as $cta ) { $this->render_item_button( $cta ); } ?>
'wp-mail-smtp-btn-secondary', 'grey' => 'wp-mail-smtp-btn-light-grey', ]; $classes = [ 'wp-mail-smtp-btn', 'wp-mail-smtp-btn-md', $variants[ $cta['modifier'] ?? '' ] ?? 'wp-mail-smtp-btn-orange', 'wpms-setup-checklist-item__button', $cta['class'] ?? '', ]; $has_tooltip = ! empty( $cta['tooltip'] ); if ( $has_tooltip ) { echo ''; } ?> render_item_button_attributes( $cta ); ?>> %s', esc_html( $cta['tooltip'] ) ); } } /** * Print a CTA anchor's attributes, the class aside. * * @since 4.10.0 * * @param array $cta CTA data, as described on {@see self::render_item_button()}. */ private function render_item_button_attributes( array $cta ): void { if ( empty( $cta['disabled'] ) ) { printf( ' href="%s"', esc_url( $cta['url'] ?? '#' ) ); foreach ( $this->get_item_button_data_attributes( $cta ) as $name => $value ) { printf( ' data-%1$s="%2$s"', esc_attr( $name ), esc_attr( $value ) ); } } else { // No href: an anchor without one is neither clickable nor focusable. echo ' aria-disabled="true"'; } } /** * The data attributes an active CTA hands to its JS handler. * * @since 4.10.0 * * @param array $cta CTA data, as described on {@see self::render_item_button()}. * * @return array Attribute values keyed by name, without the `data-` prefix. */ private function get_item_button_data_attributes( array $cta ): array { $data = []; foreach ( [ 'action', 'plugin' ] as $key ) { if ( ! empty( $cta[ $key ] ) ) { $data[ $key ] = $cta[ $key ]; } } if ( ! empty( $cta['reload'] ) ) { $data['reload'] = '1'; } return array_merge( $data, $cta['data'] ?? [] ); } /** * Build the per-section view-model: completion state plus each item resolved for render. * * @since 4.10.0 * * @return array */ private function view_sections(): array { $sections = []; foreach ( $this->checklist->get_sections() as $section ) { $items = []; foreach ( $section['items'] as $item ) { $items[] = $this->view_item( $item ); } $sections[] = [ 'id' => $section['id'], 'title' => $section['title'], 'is_complete' => ! empty( $section['complete'] ), 'items' => $items, ]; } return $sections; } /** * Build a single item view-model. * * @since 4.10.0 * * @param array $item Item enriched with completion state. * * @return array */ private function view_item( array $item ): array { $is_complete = ! empty( $item['complete'] ); return [ 'type' => 'item', 'id' => $item['id'], 'title' => $item['title'], 'description' => $item['description'], 'is_complete' => $is_complete, 'status_label' => $is_complete ? __( 'Completed', 'wp-mail-smtp' ) : __( 'Not completed', 'wp-mail-smtp' ), 'ctas' => $is_complete ? [] : $this->resolve_ctas( $item ), 'form' => $item['form'] ?? '', 'learn_more' => $item['learn_more'] ?? null, ]; } /** * Resolve an item's CTAs to their renderable shapes, dropping the ones that resolve empty. * * @since 4.10.0 * * @param array $item Item enriched with completion state. * * @return array */ private function resolve_ctas( array $item ): array { $ctas = $item['ctas'] ?? ( ! empty( $item['cta'] ) ? [ $item['cta'] ] : [] ); $resolved = []; foreach ( $ctas as $cta ) { $cta = $this->resolve_cta( $cta ); if ( ! empty( $cta ) ) { $resolved[] = $cta; } } return $resolved; } /** * Resolve a CTA to its renderable shape. * * @since 4.10.0 * * @param array $cta CTA data. * * @return array Renderable CTA data for the item-button template, or empty. */ private function resolve_cta( array $cta ): array { return empty( $cta['label'] ) ? [] : $cta; } }