*/ public static function tabs() { return array( 'general' => __( 'General', 'mm-post-grid' ), 'content' => __( 'Content', 'mm-post-grid' ), 'templates' => __( 'Templates', 'mm-post-grid' ), 'styling' => __( 'Styling', 'mm-post-grid' ), 'advanced' => __( 'Advanced', 'mm-post-grid' ), ); } /** * Returns the requested tab, falling back to the first one. * * @since 0.1 * @return string */ public static function current_tab() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Navigation only, no state is changed. $requested = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; $tabs = self::tabs(); return isset( $tabs[ $requested ] ) ? $requested : 'general'; } /** * Adds the top level menu. * * @since 0.1 * @return void */ public function add_menu() { $this->hook_suffix = (string) add_menu_page( __( 'MM Post Grid', 'mm-post-grid' ), __( 'MM Post Grid', 'mm-post-grid' ), self::CAPABILITY, self::MENU_SLUG, array( $this, 'render_page' ), 'dashicons-grid-view', 58 ); } /** * Adds a settings link on the plugins screen. * * @since 0.1 * * @param string[] $links Existing action links. * @return string[] */ public function action_links( $links ) { if ( ! current_user_can( self::CAPABILITY ) ) { return $links; } $link = sprintf( '%2$s', esc_url( admin_url( 'admin.php?page=' . self::MENU_SLUG ) ), esc_html__( 'Settings', 'mm-post-grid' ) ); return array_merge( array( 'settings' => $link ), (array) $links ); } /** * Loads the admin assets on the settings screen only. * * @since 0.1 * * @param string $hook_suffix Current admin page. * @return void */ public function enqueue( $hook_suffix ) { if ( '' === $this->hook_suffix || $hook_suffix !== $this->hook_suffix ) { return; } wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_style( 'mm-post-grid-admin', MM_POST_GRID_URL . 'assets/css/mmpg-admin.css', array( 'wp-color-picker' ), MM_POST_GRID_VERSION ); wp_enqueue_script( 'mm-post-grid-admin', MM_POST_GRID_URL . 'assets/js/mmpg-admin.js', array( 'jquery', 'wp-color-picker', 'wp-a11y' ), MM_POST_GRID_VERSION, true ); wp_localize_script( 'mm-post-grid-admin', 'mmpgAdmin', array( 'copied' => __( 'Copied to the clipboard.', 'mm-post-grid' ), 'copyError' => __( 'Copying failed, please select the text and copy it.', 'mm-post-grid' ), 'unsaved' => __( 'You have unsaved changes.', 'mm-post-grid' ), ) ); } /** * Restores the default settings. * * @since 0.1 * @return void */ public function handle_reset() { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to change these settings.', 'mm-post-grid' ), esc_html__( 'Permission denied', 'mm-post-grid' ), array( 'response' => 403 ) ); } check_admin_referer( 'mmpg_reset_settings' ); update_option( Settings::OPTION, Settings::defaults() ); Settings::flush_cache(); $tab = isset( $_POST['tab'] ) ? sanitize_key( wp_unslash( $_POST['tab'] ) ) : 'general'; $tabs = self::tabs(); wp_safe_redirect( add_query_arg( array( 'page' => self::MENU_SLUG, 'tab' => isset( $tabs[ $tab ] ) ? $tab : 'general', 'mmpg-reset' => '1', ), admin_url( 'admin.php' ) ) ); exit; } /** * Applies a styling template. * * @since 0.1 * @return void */ public function handle_apply_template() { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to change these settings.', 'mm-post-grid' ), esc_html__( 'Permission denied', 'mm-post-grid' ), array( 'response' => 403 ) ); } check_admin_referer( 'mmpg_apply_template' ); $slug = isset( $_POST['template'] ) ? sanitize_key( wp_unslash( $_POST['template'] ) ) : ''; $applied = Templates::apply( $slug ); wp_safe_redirect( add_query_arg( array( 'page' => self::MENU_SLUG, 'tab' => 'templates', 'mmpg-template' => $applied ? $slug : 'mmpg-unknown', ), admin_url( 'admin.php' ) ) ); exit; } /** * Renders the settings screen. * * @since 0.1 * @return void */ public function render_page() { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to view this page.', 'mm-post-grid' ) ); } $tabs = self::tabs(); $tab = self::current_tab(); $this->queue_notices(); ?>

[mm_post_grid]

render_templates(); ?>
render_sections( 'mmpg_settings_' . $tab ); ?>

render_help( $tab ); ?>

'', 'content' => '', 'templates' => '', 'styling' => '', 'advanced' => '', ); if ( ! isset( $paths[ $slug ] ) ) { return ''; } return ''; } /** * Prints the styling templates. * * @since 0.1 * @return void */ private function render_templates() { $templates = Templates::all(); $active = Templates::active(); ?>

    $template ) : ?>
$values Template values. * @return string */ private static function preview_classes( array $values ) { $classes = array( 'mmpg-tpl-preview' ); $map = array( 'card_layout' => 'layout', 'button_style' => 'more', 'term_style' => 'terms', 'text_align' => 'align', ); foreach ( $map as $key => $prefix ) { if ( isset( $values[ $key ] ) ) { $classes[] = 'mmpg-tpl-preview--' . $prefix . '-' . sanitize_html_class( (string) $values[ $key ] ); } } return implode( ' ', $classes ); } /** * Returns the inline custom properties of a template preview. * * Every value was sanitized against the schema by Templates::values(), * and still passes the CSS value guard before it reaches the attribute. * * @since 0.1 * * @param array $values Template values. * @return string */ private static function preview_style( array $values ) { $shadow = Helpers::shadow( isset( $values['card_shadow'] ) ? (string) $values['card_shadow'] : 'soft' ); $accent = isset( $values['accent_color'] ) ? (string) $values['accent_color'] : '#4f46e5'; $props = array( '--p-bg' => ! empty( $values['card_bg'] ) ? (string) $values['card_bg'] : 'transparent', '--p-border' => isset( $values['card_border_color'] ) ? (string) $values['card_border_color'] : '#e6e6ef', '--p-bw' => ( isset( $values['card_border_width'] ) ? (int) $values['card_border_width'] : 1 ) . 'px', '--p-radius' => ( isset( $values['card_radius'] ) ? min( 18, (int) $values['card_radius'] ) : 14 ) . 'px', '--p-img-radius' => ( isset( $values['image_radius'] ) ? min( 10, (int) $values['image_radius'] ) : 0 ) . 'px', '--p-shadow' => $shadow['shadow'], '--p-title' => isset( $values['title_color'] ) ? (string) $values['title_color'] : '#17172b', '--p-meta' => isset( $values['meta_color'] ) ? (string) $values['meta_color'] : '#6b6b80', '--p-accent' => $accent, '--p-term' => ! empty( $values['term_color'] ) ? (string) $values['term_color'] : $accent, '--p-btn' => isset( $values['button_bg'] ) ? (string) $values['button_bg'] : '#4f46e5', '--p-btn-radius' => ( isset( $values['button_radius'] ) ? min( 10, (int) $values['button_radius'] ) : 8 ) . 'px', ); $declarations = array(); foreach ( $props as $property => $value ) { $declarations[] = $property . ':' . Helpers::css_value( $value ); } return implode( ';', $declarations ); } /** * Renders the sidebar: the current look and shortcode examples. * * @since 0.1 * * @param string $tab Current tab. * @return void */ private function render_help( $tab ) { $examples = array( array( 'code' => '[mm_post_grid columns="4" posts_per_page="8"]', 'note' => __( 'Four columns, eight posts.', 'mm-post-grid' ), ), array( 'code' => '[mm_post_grid category="news" pagination="numbers"]', 'note' => __( 'One category, numbered pages.', 'mm-post-grid' ), ), array( 'code' => '[mm_post_grid post_type="page" show_excerpt="no"]', 'note' => __( 'Any post type, no excerpt.', 'mm-post-grid' ), ), array( 'code' => '[mm_post_grid card_layout="overlay" columns="2"]', 'note' => __( 'A different card layout for this grid only.', 'mm-post-grid' ), ), ); $settings = Settings::all(); $active = Templates::active( $settings ); $templates = Templates::all(); $look = isset( $templates[ $active ] ) ? $templates[ $active ]['label'] : __( 'Custom styling', 'mm-post-grid' ); ?>