>>
*/
public static function sections() {
return array(
'general' => array(
'query' => array(
'title' => __( 'Posts', 'mm-post-grid' ),
'description' => __( 'What a grid queries when the shortcode has no attributes.', 'mm-post-grid' ),
),
'layout' => array(
'title' => __( 'Layout', 'mm-post-grid' ),
'description' => __( 'Column counts apply per screen size. Below 1024px the tablet value is used, below 640px the mobile value.', 'mm-post-grid' ),
),
),
'content' => array(
'elements' => array(
'title' => __( 'Card elements', 'mm-post-grid' ),
'description' => __( 'Turn off anything the cards should not show.', 'mm-post-grid' ),
),
'text' => array(
'title' => __( 'Text', 'mm-post-grid' ),
'description' => '',
),
),
'styling' => array(
'card' => array(
'title' => __( 'Card', 'mm-post-grid' ),
'description' => __( 'These values become CSS custom properties, so the grid keeps the fonts of your theme and only the values below change. Start from a template on the Templates tab, then fine-tune here.', 'mm-post-grid' ),
),
'image' => array(
'title' => __( 'Image', 'mm-post-grid' ),
'description' => '',
),
'type' => array(
'title' => __( 'Typography and colours', 'mm-post-grid' ),
'description' => __( 'In the overlay layout the text is always light, so it stays readable on the image.', 'mm-post-grid' ),
),
'terms' => array(
'title' => __( 'Categories', 'mm-post-grid' ),
'description' => '',
),
'links' => array(
'title' => __( 'Links, buttons and pagination', 'mm-post-grid' ),
'description' => __( 'Pagination and load more sit on your page rather than on a card, so they use the button colours and your theme text colour.', 'mm-post-grid' ),
),
),
'advanced' => array(
'advanced' => array(
'title' => __( 'Advanced', 'mm-post-grid' ),
'description' => '',
),
),
);
}
/**
* Returns the field labels and help text in display order.
*
* @since 0.1
* @return array>
*/
public static function fields() {
return array(
// General, posts.
'post_type' => array(
'tab' => 'general',
'section' => 'query',
'label' => __( 'Post type', 'mm-post-grid' ),
'help' => __( 'Only publicly viewable post types are listed.', 'mm-post-grid' ),
),
'posts_per_page' => array(
'tab' => 'general',
'section' => 'query',
'label' => __( 'Posts per grid', 'mm-post-grid' ),
'help' => __( 'Between 1 and 48. With pagination on, this is the number per page.', 'mm-post-grid' ),
),
'orderby' => array(
'tab' => 'general',
'section' => 'query',
'label' => __( 'Order by', 'mm-post-grid' ),
'help' => '',
),
'order' => array(
'tab' => 'general',
'section' => 'query',
'label' => __( 'Direction', 'mm-post-grid' ),
'help' => '',
),
'pagination' => array(
'tab' => 'general',
'section' => 'query',
'label' => __( 'Pagination', 'mm-post-grid' ),
'help' => __( 'Load more falls back to a normal next page link when JavaScript is unavailable.', 'mm-post-grid' ),
),
// General, layout.
'columns' => array(
'tab' => 'general',
'section' => 'layout',
'label' => __( 'Columns on desktop', 'mm-post-grid' ),
'help' => '',
),
'columns_tablet' => array(
'tab' => 'general',
'section' => 'layout',
'label' => __( 'Columns on tablet', 'mm-post-grid' ),
'help' => '',
),
'columns_mobile' => array(
'tab' => 'general',
'section' => 'layout',
'label' => __( 'Columns on mobile', 'mm-post-grid' ),
'help' => '',
),
'gap' => array(
'tab' => 'general',
'section' => 'layout',
'label' => __( 'Gap between cards', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'image_size' => array(
'tab' => 'general',
'section' => 'layout',
'label' => __( 'Image size', 'mm-post-grid' ),
'help' => __( 'Pick the smallest size that still looks sharp at your column width.', 'mm-post-grid' ),
),
'image_ratio' => array(
'tab' => 'general',
'section' => 'layout',
'label' => __( 'Image ratio', 'mm-post-grid' ),
'help' => __( 'A fixed ratio keeps every card the same height. Original keeps the uploaded proportions.', 'mm-post-grid' ),
),
// Content, elements.
'show_image' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Featured image', 'mm-post-grid' ),
'help' => '',
),
'image_fallback' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Image placeholder', 'mm-post-grid' ),
'help' => __( 'Shows a plain tinted block when a post has no featured image, so the rows stay even.', 'mm-post-grid' ),
),
'show_category' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Category', 'mm-post-grid' ),
'help' => __( 'Uses the category for posts, or the first public taxonomy of the post type.', 'mm-post-grid' ),
),
'show_title' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Title', 'mm-post-grid' ),
'help' => '',
),
'show_excerpt' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Excerpt', 'mm-post-grid' ),
'help' => '',
),
'show_date' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Date', 'mm-post-grid' ),
'help' => '',
),
'show_author' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Author', 'mm-post-grid' ),
'help' => '',
),
'show_read_more' => array(
'tab' => 'content',
'section' => 'elements',
'label' => __( 'Read more link', 'mm-post-grid' ),
'help' => '',
),
// Content, text.
'title_tag' => array(
'tab' => 'content',
'section' => 'text',
'label' => __( 'Title element', 'mm-post-grid' ),
'help' => __( 'Match the heading level to the page around the grid. Use h3 under an h2 section title.', 'mm-post-grid' ),
),
'excerpt_length' => array(
'tab' => 'content',
'section' => 'text',
'label' => __( 'Excerpt length', 'mm-post-grid' ),
'help' => __( 'Number of words. Set 0 to print the excerpt exactly as written.', 'mm-post-grid' ),
'unit' => __( 'words', 'mm-post-grid' ),
),
'read_more_text' => array(
'tab' => 'content',
'section' => 'text',
'label' => __( 'Read more label', 'mm-post-grid' ),
'help' => __( 'Screen readers also hear the post title after the label.', 'mm-post-grid' ),
),
// Styling, card.
'card_layout' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Card layout', 'mm-post-grid' ),
'help' => __( 'Horizontal puts the image beside the text once a card is wider than 460px, so it suits one or two columns. Overlay lays the text over the image.', 'mm-post-grid' ),
),
'card_bg' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Background', 'mm-post-grid' ),
'help' => __( 'Leave empty for a transparent card.', 'mm-post-grid' ),
),
'card_border_color' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Border colour', 'mm-post-grid' ),
'help' => '',
),
'card_border_width' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Border width', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'card_radius' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Corner radius', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'card_padding' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Inner spacing', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'content_gap' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Space between elements', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'card_shadow' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Shadow', 'mm-post-grid' ),
'help' => '',
),
'hover_effect' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Hover effect', 'mm-post-grid' ),
'help' => __( 'Border colours the card border, so it needs a border width of at least 1px.', 'mm-post-grid' ),
),
'card_border_hover' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Border colour on hover', 'mm-post-grid' ),
'help' => __( 'Leave empty to follow the accent colour.', 'mm-post-grid' ),
'depends' => array( 'hover_effect', array( 'border' ) ),
),
'text_align' => array(
'tab' => 'styling',
'section' => 'card',
'label' => __( 'Text alignment', 'mm-post-grid' ),
'help' => '',
),
// Styling, image.
'image_radius' => array(
'tab' => 'styling',
'section' => 'image',
'label' => __( 'Image corner radius', 'mm-post-grid' ),
'help' => __( 'Ignored in the overlay layout, where the card corners shape the image.', 'mm-post-grid' ),
'unit' => 'px',
),
'image_hover' => array(
'tab' => 'styling',
'section' => 'image',
'label' => __( 'Image hover effect', 'mm-post-grid' ),
'help' => '',
),
'media_width' => array(
'tab' => 'styling',
'section' => 'image',
'label' => __( 'Image width', 'mm-post-grid' ),
'help' => __( 'Share of the card width the image takes in the horizontal layout.', 'mm-post-grid' ),
'unit' => '%',
'depends' => array( 'card_layout', array( 'horizontal' ) ),
),
'overlay_opacity' => array(
'tab' => 'styling',
'section' => 'image',
'label' => __( 'Overlay strength', 'mm-post-grid' ),
'help' => __( 'How dark the gradient behind the text gets. The minimum of 50 keeps light text readable on bright photos.', 'mm-post-grid' ),
'unit' => '%',
'depends' => array( 'card_layout', array( 'overlay' ) ),
),
// Styling, typography.
'title_color' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Title colour', 'mm-post-grid' ),
'help' => '',
),
'title_hover_color' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Title colour on hover', 'mm-post-grid' ),
'help' => '',
),
'title_size' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Title size', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'title_weight' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Title weight', 'mm-post-grid' ),
'help' => '',
),
'title_line_height' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Title line height', 'mm-post-grid' ),
'help' => __( 'Relative to the title size: 135 means 1.35 times the size.', 'mm-post-grid' ),
'unit' => '%',
),
'title_lines' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Maximum title lines', 'mm-post-grid' ),
'help' => __( 'Longer titles end with an ellipsis. 0 always shows the full title.', 'mm-post-grid' ),
),
'meta_color' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Meta colour', 'mm-post-grid' ),
'help' => __( 'Used for the date and the author line.', 'mm-post-grid' ),
),
'meta_size' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Meta size', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'excerpt_color' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Excerpt colour', 'mm-post-grid' ),
'help' => '',
),
'excerpt_size' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Excerpt size', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'excerpt_line_height' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Excerpt line height', 'mm-post-grid' ),
'help' => __( 'Relative to the excerpt size.', 'mm-post-grid' ),
'unit' => '%',
),
'excerpt_lines' => array(
'tab' => 'styling',
'section' => 'type',
'label' => __( 'Maximum excerpt lines', 'mm-post-grid' ),
'help' => __( 'Clamping keeps the cards in a row the same height. 0 always shows the full excerpt.', 'mm-post-grid' ),
),
// Styling, categories.
'term_style' => array(
'tab' => 'styling',
'section' => 'terms',
'label' => __( 'Category style', 'mm-post-grid' ),
'help' => '',
),
'term_color' => array(
'tab' => 'styling',
'section' => 'terms',
'label' => __( 'Category colour', 'mm-post-grid' ),
'help' => __( 'Leave empty to follow the accent colour.', 'mm-post-grid' ),
),
// Styling, links.
'accent_color' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Accent colour', 'mm-post-grid' ),
'help' => __( 'Used for text links and focus outlines inside the cards.', 'mm-post-grid' ),
),
'button_style' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Read more style', 'mm-post-grid' ),
'help' => '',
),
'button_size' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Button size', 'mm-post-grid' ),
'help' => '',
'depends' => array( 'button_style', array( 'button', 'outline' ) ),
),
'button_bg' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Button colour', 'mm-post-grid' ),
'help' => __( 'Fills the button, draws the outline style, and colours load more and the current page number.', 'mm-post-grid' ),
),
'button_bg_hover' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Button colour on hover', 'mm-post-grid' ),
'help' => '',
),
'button_color' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Button text colour', 'mm-post-grid' ),
'help' => __( 'Keep enough contrast against the button colour.', 'mm-post-grid' ),
),
'button_radius' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Button corner radius', 'mm-post-grid' ),
'help' => '',
'unit' => 'px',
),
'pagination_align' => array(
'tab' => 'styling',
'section' => 'links',
'label' => __( 'Pagination alignment', 'mm-post-grid' ),
'help' => __( 'Also aligns the load more button.', 'mm-post-grid' ),
),
// Advanced.
'disable_css' => array(
'tab' => 'advanced',
'section' => 'advanced',
'label' => __( 'Skip the plugin stylesheet', 'mm-post-grid' ),
'help' => __( 'For developers who style the grid in their own theme. The markup and the class names stay the same.', 'mm-post-grid' ),
),
'delete_data' => array(
'tab' => 'advanced',
'section' => 'advanced',
'label' => __( 'Delete settings on uninstall', 'mm-post-grid' ),
'help' => __( 'Off by default, so deactivating and reinstalling keeps your setup. Posts are never deleted either way.', 'mm-post-grid' ),
),
);
}
/**
* Registers the option, sections and fields.
*
* @since 0.1
* @return void
*/
public function register_settings() {
register_setting(
Settings::GROUP,
Settings::OPTION,
array(
'type' => 'array',
'sanitize_callback' => array( Settings::class, 'sanitize' ),
'default' => Settings::defaults(),
'show_in_rest' => false,
)
);
foreach ( self::sections() as $tab => $sections ) {
foreach ( $sections as $section => $data ) {
add_settings_section(
'mmpg_' . $section,
$data['title'],
array( $this, 'render_section' ),
'mmpg_settings_' . $tab
);
}
}
$schema = Settings::schema();
foreach ( self::fields() as $key => $ui ) {
if ( ! isset( $schema[ $key ] ) ) {
continue;
}
add_settings_field(
'mmpg_' . $key,
$ui['label'],
array( $this, 'render_field' ),
'mmpg_settings_' . $ui['tab'],
'mmpg_' . $ui['section'],
array(
'label_for' => 'mmpg-' . $key,
'key' => $key,
'field' => $schema[ $key ],
'ui' => $ui,
'class' => self::row_class( $ui ),
)
);
}
}
/**
* Prints the description of a section.
*
* @since 0.1
*
* @param array $section Section data from the Settings API.
* @return void
*/
public function render_section( $section ) {
$id = isset( $section['id'] ) ? (string) $section['id'] : '';
foreach ( self::sections() as $sections ) {
foreach ( $sections as $slug => $data ) {
if ( 'mmpg_' . $slug === $id && '' !== $data['description'] ) {
echo '' . esc_html( $data['description'] ) . '
';
return;
}
}
}
}
/**
* Prints one field.
*
* @since 0.1
*
* @param array $args Field arguments from the Settings API.
* @return void
*/
public function render_field( $args ) {
$key = (string) $args['key'];
$field = (array) $args['field'];
$ui = (array) $args['ui'];
$type = isset( $field['type'] ) ? $field['type'] : 'text';
$value = Settings::get( $key );
$id = 'mmpg-' . $key;
$name = Settings::OPTION . '[' . $key . ']';
$help = isset( $ui['help'] ) ? (string) $ui['help'] : '';
$desc_id = '' !== $help ? $id . '-desc' : '';
switch ( $type ) {
case 'bool':
// The hidden input makes sure an unchecked box is still submitted.
?>
/>
/>
/>
/>
%2$s
',
esc_attr( $desc_id ),
esc_html( $help )
);
}
}
/**
* Returns the table row class of a field.
*
* A field that only matters for some values of another field gets a class
* such as mmpg-when-card_layout--horizontal. The admin script hides the row
* while it does not apply; without JavaScript every row stays visible and
* every value is still saved.
*
* @since 0.1
*
* @param array $ui Field UI definition.
* @return string
*/
private static function row_class( array $ui ) {
$classes = array( 'mmpg-row' );
if ( isset( $ui['depends'][0], $ui['depends'][1] ) && is_array( $ui['depends'][1] ) ) {
$values = array_map( 'sanitize_html_class', $ui['depends'][1] );
$classes[] = 'mmpg-when-' . sanitize_html_class( $ui['depends'][0] ) . '--' . implode( '--', $values );
}
return implode( ' ', $classes );
}
/**
* Returns the options of a select field.
*
* @since 0.1
*
* @param string $key Setting key.
* @param string $type Schema type.
* @param array $field Schema entry.
* @return array
*/
private static function choices( $key, $type, array $field ) {
if ( 'post_type' === $type ) {
$choices = array();
$post_types = get_post_types( array( 'public' => true ), 'objects' );
foreach ( $post_types as $post_type ) {
if ( ! is_post_type_viewable( $post_type ) ) {
continue;
}
$choices[ $post_type->name ] = $post_type->labels->singular_name . ' (' . $post_type->name . ')';
}
return $choices;
}
if ( 'image_size' === $type ) {
$choices = array();
$subsizes = wp_get_registered_image_subsizes();
foreach ( Settings::image_sizes() as $size ) {
if ( isset( $subsizes[ $size ]['width'], $subsizes[ $size ]['height'] ) ) {
$choices[ $size ] = sprintf(
/* translators: 1: Image size name, 2: Width in pixels, 3: Height in pixels. */
_x( '%1$s (%2$d × %3$d)', 'image size option', 'mm-post-grid' ),
$size,
(int) $subsizes[ $size ]['width'],
(int) $subsizes[ $size ]['height']
);
} else {
$choices[ $size ] = $size;
}
}
return $choices;
}
$labels = self::choice_labels();
$choices = array();
foreach ( (array) ( isset( $field['choices'] ) ? $field['choices'] : array() ) as $choice ) {
$choices[ $choice ] = isset( $labels[ $key ][ $choice ] ) ? $labels[ $key ][ $choice ] : (string) $choice;
}
return $choices;
}
/**
* Returns the human readable labels of the choice fields.
*
* @since 0.1
* @return array>
*/
private static function choice_labels() {
return array(
'orderby' => array(
'date' => __( 'Publish date', 'mm-post-grid' ),
'modified' => __( 'Last modified', 'mm-post-grid' ),
'title' => __( 'Title', 'mm-post-grid' ),
'menu_order' => __( 'Menu order', 'mm-post-grid' ),
'comment_count' => __( 'Comment count', 'mm-post-grid' ),
'rand' => __( 'Random', 'mm-post-grid' ),
'ID' => __( 'Post ID', 'mm-post-grid' ),
),
'order' => array(
'DESC' => __( 'Descending', 'mm-post-grid' ),
'ASC' => __( 'Ascending', 'mm-post-grid' ),
),
'pagination' => array(
'none' => __( 'None', 'mm-post-grid' ),
'numbers' => __( 'Page numbers', 'mm-post-grid' ),
'prev_next' => __( 'Previous and next', 'mm-post-grid' ),
'load_more' => __( 'Load more', 'mm-post-grid' ),
),
'image_ratio' => array(
'16-9' => __( 'Wide, 16:9', 'mm-post-grid' ),
'16-10' => __( 'Wide, 16:10', 'mm-post-grid' ),
'3-2' => __( 'Classic, 3:2', 'mm-post-grid' ),
'4-3' => __( 'Standard, 4:3', 'mm-post-grid' ),
'1-1' => __( 'Square', 'mm-post-grid' ),
'3-4' => __( 'Portrait, 3:4', 'mm-post-grid' ),
'auto' => __( 'Original proportions', 'mm-post-grid' ),
),
'card_shadow' => array(
'none' => __( 'None', 'mm-post-grid' ),
'soft' => __( 'Soft', 'mm-post-grid' ),
'medium' => __( 'Medium', 'mm-post-grid' ),
'strong' => __( 'Strong', 'mm-post-grid' ),
),
'title_tag' => array(
'h2' => 'h2',
'h3' => 'h3',
'h4' => 'h4',
'h5' => 'h5',
'h6' => 'h6',
'p' => __( 'Paragraph', 'mm-post-grid' ),
'div' => __( 'Plain block', 'mm-post-grid' ),
),
'button_style' => array(
'link' => __( 'Text link', 'mm-post-grid' ),
'button' => __( 'Button', 'mm-post-grid' ),
'outline' => __( 'Outline button', 'mm-post-grid' ),
),
'button_size' => array(
'small' => __( 'Small', 'mm-post-grid' ),
'medium' => __( 'Medium', 'mm-post-grid' ),
'large' => __( 'Large', 'mm-post-grid' ),
),
'card_layout' => array(
'stacked' => __( 'Image above the text', 'mm-post-grid' ),
'horizontal' => __( 'Image beside the text', 'mm-post-grid' ),
'overlay' => __( 'Text over the image', 'mm-post-grid' ),
),
'hover_effect' => array(
'lift' => __( 'Lift with shadow', 'mm-post-grid' ),
'shadow' => __( 'Shadow only', 'mm-post-grid' ),
'border' => __( 'Border colour', 'mm-post-grid' ),
'none' => __( 'None', 'mm-post-grid' ),
),
'text_align' => array(
'left' => __( 'Left', 'mm-post-grid' ),
'center' => __( 'Centred', 'mm-post-grid' ),
),
'image_hover' => array(
'zoom' => __( 'Zoom', 'mm-post-grid' ),
'none' => __( 'None', 'mm-post-grid' ),
'grayscale' => __( 'Greyscale until hover', 'mm-post-grid' ),
),
'title_weight' => array(
'400' => __( 'Regular', 'mm-post-grid' ),
'500' => __( 'Medium', 'mm-post-grid' ),
'600' => __( 'Semibold', 'mm-post-grid' ),
'700' => __( 'Bold', 'mm-post-grid' ),
'800' => __( 'Extra bold', 'mm-post-grid' ),
),
'term_style' => array(
'text' => __( 'Text', 'mm-post-grid' ),
'badge' => __( 'Badge', 'mm-post-grid' ),
),
'pagination_align' => array(
'left' => __( 'Left', 'mm-post-grid' ),
'center' => __( 'Centre', 'mm-post-grid' ),
'right' => __( 'Right', 'mm-post-grid' ),
),
);
}
}