post_type = DeveloperSD_LS_Post_Type::POST_TYPE; add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_init', array( $this, 'register_settings' ) ); add_action( 'admin_notices', array( $this, 'render_notices' ) ); // List table. add_filter( "manage_{$this->post_type}_posts_columns", array( $this, 'columns' ) ); add_action( "manage_{$this->post_type}_posts_custom_column", array( $this, 'render_column' ), 10, 2 ); add_filter( 'post_row_actions', array( $this, 'row_actions' ), 10, 2 ); add_filter( "bulk_actions-edit-{$this->post_type}", array( $this, 'bulk_actions' ) ); add_filter( "handle_bulk_actions-edit-{$this->post_type}", array( $this, 'handle_bulk_actions' ), 10, 3 ); add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) ); // Meta boxes + saving. add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) ); add_action( 'save_post_' . $this->post_type, array( $this, 'save_slider' ) ); // Row/admin-post actions. add_action( 'admin_post_developersd_ls_duplicate', array( $this, 'handle_duplicate' ) ); add_action( 'admin_post_developersd_ls_toggle_status', array( $this, 'handle_toggle_status' ) ); add_action( 'admin_post_developersd_ls_preview', array( $this, 'handle_preview' ) ); add_action( 'admin_post_developersd_ls_export', array( $this, 'handle_export' ) ); add_action( 'admin_post_developersd_ls_export_all', array( $this, 'handle_export_all' ) ); add_action( 'admin_post_developersd_ls_import', array( $this, 'handle_import' ) ); add_action( 'admin_post_developersd_ls_reset_settings', array( $this, 'handle_reset_settings' ) ); } /* ------------------------------------------------------------------ * * Menu * ------------------------------------------------------------------ */ /** * Register the admin menu and submenus. */ public function register_menu() { $capability = DeveloperSD_LS_Security::capability(); $parent = 'edit.php?post_type=' . $this->post_type; add_menu_page( __( 'DeveloperSD Logo Slider', 'developersd-logo-slider' ), __( 'Logo Slider', 'developersd-logo-slider' ), $capability, $parent, '', 'dashicons-images-alt2', 75 ); add_submenu_page( $parent, __( 'All Sliders', 'developersd-logo-slider' ), __( 'All Sliders', 'developersd-logo-slider' ), $capability, $parent ); add_submenu_page( $parent, __( 'Add New Slider', 'developersd-logo-slider' ), __( 'Add New Slider', 'developersd-logo-slider' ), $capability, 'post-new.php?post_type=' . $this->post_type ); add_submenu_page( $parent, __( 'Settings', 'developersd-logo-slider' ), __( 'Settings', 'developersd-logo-slider' ), $capability, self::SETTINGS_SLUG, array( $this, 'render_settings_page' ) ); add_submenu_page( $parent, __( 'Documentation / Help', 'developersd-logo-slider' ), __( 'Documentation / Help', 'developersd-logo-slider' ), $capability, self::HELP_SLUG, array( $this, 'render_help_page' ) ); } /** * Friendlier "updated" messages for the slider post type. * * @param array $messages Existing messages. * @return array */ public function post_updated_messages( $messages ) { $messages[ $this->post_type ] = array( 0 => '', 1 => __( 'Slider updated.', 'developersd-logo-slider' ), 4 => __( 'Slider updated.', 'developersd-logo-slider' ), 6 => __( 'Slider published.', 'developersd-logo-slider' ), 7 => __( 'Slider saved.', 'developersd-logo-slider' ), 8 => __( 'Slider submitted.', 'developersd-logo-slider' ), 10 => __( 'Slider draft updated.', 'developersd-logo-slider' ), ); return $messages; } /* ------------------------------------------------------------------ * * List table * ------------------------------------------------------------------ */ /** * Define custom list table columns. * * @param array $columns Existing columns. * @return array */ public function columns( $columns ) { $new = array(); foreach ( $columns as $key => $label ) { $new[ $key ] = $label; if ( 'title' === $key ) { $new['shortcode'] = __( 'Shortcode', 'developersd-logo-slider' ); $new['logo_count'] = __( 'Logos', 'developersd-logo-slider' ); $new['status'] = __( 'Status', 'developersd-logo-slider' ); } } if ( isset( $new['date'] ) ) { $date = $new['date']; unset( $new['date'] ); $new['date'] = __( 'Date Created', 'developersd-logo-slider' ); } return $new; } /** * Render custom column content. * * @param string $column Column key. * @param int $post_id Post ID. */ public function render_column( $column, $post_id ) { $slider = new DeveloperSD_LS_Slider( $post_id ); switch ( $column ) { case 'shortcode': echo '
' . esc_html( $slider->get_shortcode() ) . ' '; echo '
'; break; case 'logo_count': echo esc_html( number_format_i18n( count( $slider->get_logos() ) ) ); break; case 'status': $is_active = $slider->is_active(); $label = $is_active ? __( 'Active', 'developersd-logo-slider' ) : __( 'Inactive', 'developersd-logo-slider' ); $aria_label = sprintf( /* translators: %s: current slider status (Active or Inactive). */ __( 'Set slider status. Current status: %s', 'developersd-logo-slider' ), $label ); printf( '', esc_attr( $is_active ? 'is-active' : 'is-inactive' ), esc_attr( $is_active ? 'true' : 'false' ), esc_attr( $aria_label ), absint( $post_id ), esc_html( $label ) ); break; } } /** * Add Duplicate / Preview / Activate-Deactivate row actions. * * @param array $actions Existing row actions. * @param WP_Post $post Current post. * @return array */ public function row_actions( $actions, $post ) { if ( $this->post_type !== $post->post_type || ! DeveloperSD_LS_Security::current_user_can_manage() ) { return $actions; } $slider = new DeveloperSD_LS_Slider( $post->ID ); $duplicate_url = wp_nonce_url( admin_url( 'admin-post.php?action=developersd_ls_duplicate&post=' . $post->ID ), 'developersd_ls_duplicate_' . $post->ID ); $preview_url = wp_nonce_url( admin_url( 'admin-post.php?action=developersd_ls_preview&post=' . $post->ID ), 'developersd_ls_preview_' . $post->ID ); $toggle_url = wp_nonce_url( admin_url( 'admin-post.php?action=developersd_ls_toggle_status&post=' . $post->ID ), 'developersd_ls_toggle_' . $post->ID ); $export_url = wp_nonce_url( admin_url( 'admin-post.php?action=developersd_ls_export&post=' . $post->ID ), 'developersd_ls_export_' . $post->ID ); $new_actions = array(); foreach ( $actions as $key => $markup ) { $new_actions[ $key ] = $markup; if ( 'edit' === $key ) { $new_actions['duplicate'] = '' . esc_html__( 'Duplicate', 'developersd-logo-slider' ) . ''; $new_actions['preview'] = '' . esc_html__( 'Preview', 'developersd-logo-slider' ) . ''; $new_actions['toggle'] = '' . ( $slider->is_active() ? esc_html__( 'Deactivate', 'developersd-logo-slider' ) : esc_html__( 'Activate', 'developersd-logo-slider' ) ) . ''; $new_actions['export'] = '' . esc_html__( 'Export', 'developersd-logo-slider' ) . ''; } } return $new_actions; } /** * Add bulk actions for activate/deactivate. * * @param array $actions Existing bulk actions. * @return array */ public function bulk_actions( $actions ) { $actions['developersd_ls_activate'] = __( 'Activate', 'developersd-logo-slider' ); $actions['developersd_ls_deactivate'] = __( 'Deactivate', 'developersd-logo-slider' ); return $actions; } /** * Handle bulk activate/deactivate actions. * * @param string $redirect_to Redirect URL. * @param string $action Action key. * @param array $post_ids Selected post IDs. * @return string */ public function handle_bulk_actions( $redirect_to, $action, $post_ids ) { if ( ! in_array( $action, array( 'developersd_ls_activate', 'developersd_ls_deactivate' ), true ) ) { return $redirect_to; } if ( ! DeveloperSD_LS_Security::current_user_can_manage() ) { return $redirect_to; } $status = 'developersd_ls_activate' === $action ? 'active' : 'inactive'; foreach ( $post_ids as $post_id ) { if ( $this->post_type === get_post_type( $post_id ) ) { ( new DeveloperSD_LS_Slider( $post_id ) )->set_status( $status ); } } return add_query_arg( 'developersd_ls_bulk_updated', count( $post_ids ), $redirect_to ); } /* ------------------------------------------------------------------ * * Meta boxes * ------------------------------------------------------------------ */ /** * Register meta boxes on the slider edit screen. */ public function add_meta_boxes() { add_meta_box( 'developersd_ls_shortcode', __( 'Shortcode & Status', 'developersd-logo-slider' ), array( $this, 'render_shortcode_box' ), $this->post_type, 'side', 'high' ); add_meta_box( 'developersd_ls_logos', __( 'Slider Logos', 'developersd-logo-slider' ), array( $this, 'render_logos_box' ), $this->post_type, 'normal', 'high' ); add_meta_box( 'developersd_ls_settings', __( 'Slider Settings', 'developersd-logo-slider' ), array( $this, 'render_settings_box' ), $this->post_type, 'normal', 'default' ); add_meta_box( 'developersd_ls_preview', __( 'Live Preview', 'developersd-logo-slider' ), array( $this, 'render_preview_box' ), $this->post_type, 'normal', 'default' ); // Hide the default WordPress "Publish" box complexity by keeping // the native one (it already provides Save/Publish + Trash), no // custom replacement needed. } /** * Render the shortcode/status side meta box. * * @param WP_Post $post Current post. */ public function render_shortcode_box( $post ) { $slider = new DeveloperSD_LS_Slider( $post->ID ); include DEVELOPERSD_LS_PATH . 'admin/views/slider-edit-shortcode.php'; } /** * Render the logo repeater meta box. * * @param WP_Post $post Current post. */ public function render_logos_box( $post ) { wp_nonce_field( DeveloperSD_LS_Security::NONCE_ACTION_SAVE, 'developersd_ls_nonce' ); $slider = new DeveloperSD_LS_Slider( $post->ID ); $logos = $slider->get_logos(); include DEVELOPERSD_LS_PATH . 'admin/views/slider-edit-logos.php'; } /** * Render the tabbed settings meta box. * * @param WP_Post $post Current post. */ public function render_settings_box( $post ) { $slider = new DeveloperSD_LS_Slider( $post->ID ); $settings = $slider->get_settings(); include DEVELOPERSD_LS_PATH . 'admin/views/slider-edit-settings.php'; } /** * Render the live preview meta box. * * @param WP_Post $post Current post. */ public function render_preview_box( $post ) { include DEVELOPERSD_LS_PATH . 'admin/views/slider-edit-preview.php'; } /** * Save slider logos, settings, and status on post save. * * @param int $post_id Post ID being saved. */ public function save_slider( $post_id ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( ! isset( $_POST['developersd_ls_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['developersd_ls_nonce'] ) ), DeveloperSD_LS_Security::NONCE_ACTION_SAVE ) ) { return; } if ( ! DeveloperSD_LS_Security::current_user_can_manage() ) { return; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } $slider = new DeveloperSD_LS_Slider( $post_id ); if ( isset( $_POST['developersd_ls_logos_json'] ) ) { // The JSON container must remain intact until decoded. Every decoded // logo field is sanitized by save_logos()/sanitize_logos() below. $logos_json = wp_unslash( $_POST['developersd_ls_logos_json'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Structured JSON is sanitized field-by-field after decoding. $decoded = json_decode( $logos_json, true ); $slider->save_logos( is_array( $decoded ) ? $decoded : array() ); } if ( isset( $_POST['developersd_ls_settings'] ) && is_array( $_POST['developersd_ls_settings'] ) ) { $settings_input = map_deep( wp_unslash( $_POST['developersd_ls_settings'] ), 'sanitize_text_field' ); $slider->save_settings( $settings_input ); } $status_input = isset( $_POST['developersd_ls_status'] ) ? sanitize_key( wp_unslash( $_POST['developersd_ls_status'] ) ) : 'inactive'; $status = 'active' === $status_input ? 'active' : 'inactive'; $slider->set_status( $status ); } /* ------------------------------------------------------------------ * * Row action handlers (admin-post) * ------------------------------------------------------------------ */ /** * Handle the "Duplicate" row action. */ public function handle_duplicate() { $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; check_admin_referer( 'developersd_ls_duplicate_' . $post_id ); $this->guard_capability(); $new_id = ( new DeveloperSD_LS_Slider( $post_id ) )->duplicate(); if ( is_wp_error( $new_id ) ) { wp_die( esc_html( $new_id->get_error_message() ) ); } wp_safe_redirect( admin_url( 'post.php?post=' . $new_id . '&action=edit&developersd_ls_duplicated=1' ) ); exit; } /** * Handle the "Activate/Deactivate" row action. */ public function handle_toggle_status() { $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; check_admin_referer( 'developersd_ls_toggle_' . $post_id ); $this->guard_capability(); $slider = new DeveloperSD_LS_Slider( $post_id ); $slider->set_status( $slider->is_active() ? 'inactive' : 'active' ); wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url( 'edit.php?post_type=' . $this->post_type ) ); exit; } /** * Handle the "Preview" row action: outputs a minimal, unthemed HTML * document embedding just the slider, regardless of active status. */ public function handle_preview() { $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; check_admin_referer( 'developersd_ls_preview_' . $post_id ); $this->guard_capability(); $html = DeveloperSD_LS_Frontend::render_forced( $post_id ); nocache_headers(); ?> > <?php esc_html_e( 'Slider Preview', 'developersd-logo-slider' ); ?>

'developersd-logo-slider', 'format' => 'developersd-logo-slider-export', 'schema_version' => 2, 'plugin_version' => DEVELOPERSD_LS_VERSION, 'export_type' => ( 'all' === $export_type ) ? 'all' : 'single', 'exported_at' => gmdate( 'c' ), 'source_site' => esc_url_raw( home_url( '/' ) ), 'sliders' => array_values( $sliders ), ); } /** * Handle single-slider JSON export/download. */ public function handle_export() { $post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; check_admin_referer( 'developersd_ls_export_' . $post_id ); $this->guard_capability(); $slider = new DeveloperSD_LS_Slider( $post_id ); if ( ! $slider->exists() ) { wp_die( esc_html__( 'Slider not found.', 'developersd-logo-slider' ) ); } $slug = sanitize_title( $slider->get_title() ); if ( '' === $slug ) { $slug = 'slider-' . $post_id; } $this->send_json_export( $this->build_export_payload( array( $slider->export() ), 'single' ), 'developersd-logo-slider-' . $slug . '.json' ); } /** * Handle exporting every slider as one portable JSON file. */ public function handle_export_all() { check_admin_referer( 'developersd_ls_export_all' ); $this->guard_capability(); $sliders = DeveloperSD_LS_Slider::get_all(); $export = array(); foreach ( $sliders as $post ) { $export[] = ( new DeveloperSD_LS_Slider( $post->ID ) )->export(); } $this->send_json_export( $this->build_export_payload( $export, 'all' ), 'developersd-logo-slider-all.json' ); } /** * Read an uploaded JSON file using WordPress' direct filesystem class. * * PHP upload temp files always exist on the local web server. Using the * direct filesystem avoids import failures on sites where the generic * WP_Filesystem transport is configured for FTP/SSH and therefore cannot * see PHP's local temporary upload directory. * * @param string $tmp_path Temporary upload path supplied by PHP. * @return string|false */ protected function read_uploaded_json( $tmp_path ) { if ( ! class_exists( 'WP_Filesystem_Direct' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; } $filesystem = new WP_Filesystem_Direct( null ); if ( ! $filesystem->exists( $tmp_path ) || ! $filesystem->is_readable( $tmp_path ) ) { return false; } return $filesystem->get_contents( $tmp_path ); } /** * Normalize every supported export format into a list of slider arrays. * * @param array $data Decoded JSON data. * @return array[] */ protected function normalize_import_payload( $data ) { if ( ! is_array( $data ) ) { return array(); } // Current versioned envelope (v1.0.9+). if ( isset( $data['sliders'] ) && is_array( $data['sliders'] ) ) { if ( isset( $data['plugin'] ) && 'developersd-logo-slider' !== $data['plugin'] ) { return array(); } if ( isset( $data['format'] ) && 'developersd-logo-slider-export' !== $data['format'] ) { return array(); } return array_values( $data['sliders'] ); } // Legacy single-slider export. if ( isset( $data['title'] ) ) { return array( $data ); } // Legacy Export All format: a plain numeric array of slider exports. if ( function_exists( 'array_is_list' ) ? array_is_list( $data ) : array_keys( $data ) === range( 0, count( $data ) - 1 ) ) { return $data; } return array(); } /** * Handle importing either a single-slider or Export All JSON file. */ public function handle_import() { check_admin_referer( 'developersd_ls_import' ); $this->guard_capability(); if ( ! isset( $_FILES['developersd_ls_import_file'] ) || ! is_array( $_FILES['developersd_ls_import_file'] ) || ! isset( $_FILES['developersd_ls_import_file']['tmp_name'], $_FILES['developersd_ls_import_file']['error'], $_FILES['developersd_ls_import_file']['name'], $_FILES['developersd_ls_import_file']['size'] ) || UPLOAD_ERR_OK !== absint( $_FILES['developersd_ls_import_file']['error'] ) ) { $this->redirect_to_settings( 'import_error' ); } // tmp_name is generated by PHP, not user-entered text. Do not unslash it: // doing so can corrupt Windows temporary paths such as C:\Windows\Temp. $tmp_path = sanitize_text_field( $_FILES['developersd_ls_import_file']['tmp_name'] ); $original_name = sanitize_file_name( wp_unslash( $_FILES['developersd_ls_import_file']['name'] ) ); $file_size = absint( $_FILES['developersd_ls_import_file']['size'] ); if ( '' === $tmp_path || 'json' !== strtolower( pathinfo( $original_name, PATHINFO_EXTENSION ) ) || 0 === $file_size || $file_size > 10 * MB_IN_BYTES ) { $this->redirect_to_settings( 'import_error' ); } $contents = $this->read_uploaded_json( $tmp_path ); if ( false === $contents || '' === trim( $contents ) ) { $this->redirect_to_settings( 'import_error' ); } $data = json_decode( $contents, true ); if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $data ) ) { $this->redirect_to_settings( 'import_error' ); } $imports = $this->normalize_import_payload( $data ); if ( empty( $imports ) ) { $this->redirect_to_settings( 'import_error' ); } $imported = 0; $failed = 0; $media_failed = 0; foreach ( $imports as $slider_data ) { if ( ! is_array( $slider_data ) ) { ++$failed; continue; } $result = DeveloperSD_LS_Slider::import( $slider_data ); if ( is_wp_error( $result ) ) { ++$failed; } else { ++$imported; $media_report = DeveloperSD_LS_Slider::get_last_import_media_report(); $media_failed += isset( $media_report['failed'] ) ? absint( $media_report['failed'] ) : 0; } } if ( 0 === $imported ) { $this->redirect_to_settings( 'import_error' ); } $this->redirect_to_settings( ( 0 === $failed && 0 === $media_failed ) ? 'import_success' : 'import_partial' ); } /** * Handle resetting the global settings to defaults. */ public function handle_reset_settings() { check_admin_referer( 'developersd_ls_reset_settings' ); $this->guard_capability(); update_option( 'developersd_logo_slider_settings', DeveloperSD_LS_Plugin::default_settings() ); $this->redirect_to_settings( 'reset_success' ); } /** * Redirect back to the Settings screen with a status flag, then exit. * * @param string $flag Query arg value describing the result. */ protected function redirect_to_settings( $flag ) { wp_safe_redirect( add_query_arg( 'developersd_ls_notice', $flag, admin_url( 'admin.php?page=' . self::SETTINGS_SLUG ) ) ); exit; } /** * Die with a 403 if the current user lacks the management capability. */ protected function guard_capability() { if ( ! DeveloperSD_LS_Security::current_user_can_manage() ) { wp_die( esc_html__( 'You are not allowed to do this.', 'developersd-logo-slider' ), '', array( 'response' => 403 ) ); } } /* ------------------------------------------------------------------ * * Settings page * ------------------------------------------------------------------ */ /** * Register the global settings option. */ public function register_settings() { register_setting( 'developersd_logo_slider_settings_group', 'developersd_logo_slider_settings', array( 'type' => 'array', 'sanitize_callback' => array( $this, 'sanitize_global_settings' ), 'default' => DeveloperSD_LS_Plugin::default_settings(), ) ); } /** * Sanitize the global settings array on save. * * @param array $input Raw input. * @return array */ public function sanitize_global_settings( $input ) { if ( ! DeveloperSD_LS_Security::current_user_can_manage() ) { return get_option( 'developersd_logo_slider_settings', DeveloperSD_LS_Plugin::default_settings() ); } return DeveloperSD_LS_Security::sanitize_settings( is_array( $input ) ? $input : array(), DeveloperSD_LS_Plugin::default_settings() ); } /** * Render the Settings screen. */ public function render_settings_page() { $this->guard_capability(); $settings = wp_parse_args( get_option( 'developersd_logo_slider_settings', array() ), DeveloperSD_LS_Plugin::default_settings() ); $sliders = DeveloperSD_LS_Slider::get_all(); include DEVELOPERSD_LS_PATH . 'admin/views/settings-page.php'; } /** * Render the Documentation / Help screen. */ public function render_help_page() { $this->guard_capability(); include DEVELOPERSD_LS_PATH . 'admin/views/help-page.php'; } /* ------------------------------------------------------------------ * * Notices * ------------------------------------------------------------------ */ /** * Render contextual admin notices for duplicate/import/reset results, * plus a diagnostic notice explaining why the plugin's menu is hidden * when the current user lacks the required capability. */ public function render_notices() { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || ( $this->post_type !== $screen->post_type && false === strpos( (string) $screen->id, self::SETTINGS_SLUG ) && false === strpos( (string) $screen->id, self::HELP_SLUG ) ) ) { return; } // These query arguments only select which informational notice to display; // they do not perform or authorize any state-changing action. Using // filter_input() also keeps notice rendering separate from raw superglobals. $duplicated = sanitize_key( (string) filter_input( INPUT_GET, 'developersd_ls_duplicated', FILTER_UNSAFE_RAW ) ); $bulk_updated = sanitize_key( (string) filter_input( INPUT_GET, 'developersd_ls_bulk_updated', FILTER_UNSAFE_RAW ) ); $flag = sanitize_key( (string) filter_input( INPUT_GET, 'developersd_ls_notice', FILTER_UNSAFE_RAW ) ); if ( '' !== $duplicated ) { echo '

' . esc_html__( 'Slider duplicated successfully. It has been created as Inactive — activate it when ready.', 'developersd-logo-slider' ) . '

'; } if ( '' !== $bulk_updated ) { echo '

' . esc_html__( 'Slider status updated.', 'developersd-logo-slider' ) . '

'; } if ( '' !== $flag ) { $messages = array( 'import_success' => array( 'success', __( 'Import completed successfully, including available logo images. Imported sliders were created as Inactive — review them before activating.', 'developersd-logo-slider' ) ), 'import_partial' => array( 'warning', __( 'Import completed, but one or more slider records or logo images could not be imported. Successfully imported sliders were created as Inactive. Check that source images are publicly accessible if this was a cross-site migration.', 'developersd-logo-slider' ) ), 'import_error' => array( 'error', __( 'The import failed. Please upload a valid DeveloperSD Logo Slider export file.', 'developersd-logo-slider' ) ), 'reset_success' => array( 'success', __( 'Settings have been reset to their defaults.', 'developersd-logo-slider' ) ), ); if ( isset( $messages[ $flag ] ) ) { printf( '

%2$s

', esc_attr( $messages[ $flag ][0] ), esc_html( $messages[ $flag ][1] ) ); } } } }