* @copyright Copyright (c) 2010 - 2014, Justin Tadlock * @link http://justintadlock.com * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ /* Register metadata with WordPress. */ add_action( 'init', 'theme_layouts_register_meta' ); /* Add post type support for theme layouts. */ add_action( 'init', 'theme_layouts_add_post_type_support', 5 ); add_action( 'init', 'theme_layouts_remove_post_type_support', 5 ); /* Set up the custom post layouts. */ add_action( 'admin_init', 'theme_layouts_admin_setup' ); /* Add layout option in Customize. */ add_action( 'customize_register', 'theme_layouts_customize_register' ); /* Filters the theme layout mod. */ add_filter( 'theme_mod_theme_layout', 'theme_layouts_filter_layout', 5 ); /* Filters the body_class hook to add a custom class. */ add_filter( 'body_class', 'theme_layouts_body_class' ); /** * Registers the theme layouts meta key ('Layout') for specific object types and provides a function to * sanitize the metadata on update. * * @since 0.4.0 * @access public * @return void */ function theme_layouts_register_meta() { register_meta( 'post', theme_layouts_get_meta_key(), 'theme_layouts_sanitize_meta' ); register_meta( 'user', theme_layouts_get_meta_key(), 'theme_layouts_sanitize_meta' ); } /** * Callback function for sanitizing meta when add_metadata() or update_metadata() is called by WordPress. * If a developer wants to set up a custom method for sanitizing the data, they should use the * "sanitize_{$meta_type}_meta_{$meta_key}" filter hook to do so. * * @since 0.4.0 * @access public * @param mixed $meta_value The value of the data to sanitize. * @param string $meta_key The meta key name. * @param string $meta_type The type of metadata (post, comment, user, etc.) * @return mixed $meta_value */ function theme_layouts_sanitize_meta( $meta_value, $meta_key, $meta_type ) { return sanitize_html_class( $meta_value ); } /** * Adds post type support to all 'public' post types. This allows themes to remove support for the * 'theme-layouts' feature with remove_post_type_support(). * * @since 0.4.0 * @access public * @return void */ function theme_layouts_add_post_type_support() { /* Gets available public post types. */ $post_types = get_post_types( array( 'public' => true ) ); /* For each available post type, create a meta box on its edit page if it supports '$prefix-post-settings'. */ foreach ( $post_types as $type ) add_post_type_support( $type, 'theme-layouts' ); } /** * Removes theme layouts support from specific post types created by plugins. * * @since 0.4.0 * @access public * @return void */ function theme_layouts_remove_post_type_support() { /* Removes theme layouts support of the bbPress 'reply' post type. */ if ( function_exists( 'bbp_get_reply_post_type' ) ) remove_post_type_support( bbp_get_reply_post_type(), 'theme-layouts' ); } /** * Gets all the available layouts for the theme. * * @since 0.5.0 * @access public * @return array Either theme-supported layouts or the default layouts. */ function theme_layouts_get_layouts() { $layouts = get_theme_support( 'theme-layouts' ); return isset( $layouts[0] ) ? array_keys( $layouts[0] ) : array_keys( theme_layouts_strings() ); } /** * Returns an array of arguments for setting up the theme layouts script. The defaults are merged * with the theme-supported arguments. * * @since 0.5.0 * @access public * @return array Arguments for the theme layouts script. */ function theme_layouts_get_args() { $defaults = array( 'customize' => true, 'post_meta' => true, 'default' => 'default' ); $layouts = get_theme_support( 'theme-layouts' ); $args = isset( $layouts[1] ) ? $layouts[1] : array(); return apply_filters( 'theme_layouts_args', wp_parse_args( $args, $defaults ) ); } /** * Filters the 'theme_mods_theme_layout' hook to alter the layout based on post and user metadata. * Theme authors should also use this hook to filter the layout if need be. * * @since 0.5.0 * @access public * @param string $theme_layout * @return string */ function theme_layouts_filter_layout( $theme_layout ) { /* If viewing a singular post, get the post layout. */ if ( is_singular() ) $layout = get_post_layout( get_queried_object_id() ); /* If viewing an author archive, get the user layout. */ elseif ( is_author() ) $layout = get_user_layout( get_queried_object_id() ); /* If a layout was found, set it. */ if ( !empty( $layout ) && 'default' !== $layout ) { $theme_layout = $layout; } /* Else, if no layout option has yet been saved, return the theme default. */ elseif ( empty( $theme_layout ) ) { $args = theme_layouts_get_args(); $theme_layout = $args['default']; } return $theme_layout; } /** * Gets the layout for the current post based off the 'Layout' custom field key if viewing a singular post * entry. All other pages are given a default layout of 'layout-default'. * * @since 0.2.0 * @access public * @return string The layout for the given page. */ function theme_layouts_get_layout() { /* Get the available theme layouts. */ $layouts = theme_layouts_get_layouts(); /* Get the theme layout arguments. */ $args = theme_layouts_get_args(); /* Set the layout to an empty string. */ $layout = get_theme_mod( 'theme_layout', $args['default'] ); /* Make sure the given layout is in the array of available post layouts for the theme. */ if ( empty( $layout ) || !in_array( $layout, $layouts ) || 'default' == $layout ) $layout = $args['default']; /* @deprecated 0.2.0. Use the 'get_theme_layout' hook. */ $layout = apply_filters( 'get_post_layout', "layout-{$layout}" ); /* @deprecated 0.5.0. Use the 'theme_mods_theme_layout' hook. */ return esc_attr( apply_filters( 'get_theme_layout', $layout ) ); } /** * Get the post layout based on the given post ID. * * @since 0.2.0 * @param int $post_id The ID of the post to get the layout for. * @return string $layout The name of the post's layout. */ function get_post_layout( $post_id ) { /* Get the post layout. */ $layout = get_post_meta( $post_id, theme_layouts_get_meta_key(), true ); /* Return the layout if one is found. Otherwise, return 'default'. */ return ( !empty( $layout ) ? $layout : 'default' ); } /** * Update/set the post layout based on the given post ID and layout. * * @since 0.2.0 * @access public * @param int $post_id The ID of the post to set the layout for. * @param string $layout The name of the layout to set. * @return bool True on successful update, false on failure. */ function set_post_layout( $post_id, $layout ) { return update_post_meta( $post_id, theme_layouts_get_meta_key(), $layout ); } /** * Deletes a post layout. * * @since 0.4.0 * @access public * @param int $post_id The ID of the post to delete the layout for. * @return bool True on successful delete, false on failure. */ function delete_post_layout( $post_id ) { return delete_post_meta( $post_id, theme_layouts_get_meta_key() ); } /** * Checks if a specific post's layout matches that of the given layout. * * @since 0.3.0 * @access public * @param string $layout The name of the layout to check if the post has. * @param int $post_id The ID of the post to check the layout for. * @return bool Whether the given layout matches the post's layout. */ function has_post_layout( $layout, $post_id = '' ) { /* If no post ID is given, use WP's get_the_ID() to get it and assume we're in the post loop. */ if ( empty( $post_id ) ) $post_id = get_the_ID(); /* Return true/false based on whether the layout matches. */ return ( $layout == get_post_layout( $post_id ) ? true : false ); } /** * Get the layout for a user/author archive page based on a specific user ID. * * @since 0.3.0 * @access public * @param int $user_id The ID of the user to get the layout for. * @return string The layout if one exists, 'default' if one doesn't. */ function get_user_layout( $user_id ) { /* Get the user layout. */ $layout = get_user_meta( $user_id, theme_layouts_get_meta_key(), true ); /* Return the layout if one is found. Otherwise, return 'default'. */ return ( !empty( $layout ) ? $layout : 'default' ); } /** * Update/set the layout for a user/author archive paged based on the user ID. * * @since 0.3.0 * @access public * @param int $user_id The ID of the user to set the layout for. * @param string $layout The name of the layout to set. * @return bool True on successful update, false on failure. */ function set_user_layout( $user_id, $layout ) { return update_user_meta( $user_id, theme_layouts_get_meta_key(), $layout ); } /** * Deletes a user layout. * * @since 0.4.0 * @access public * @param int $user_id The ID of the user to delete the layout for. * @return bool True on successful delete, false on failure. */ function delete_user_layout( $user_id ) { return delete_user_meta( $user_id, theme_layouts_get_meta_key() ); } /** * Checks if a specific user's layout matches that of the given layout. * * @since 0.3.0 * @access public * @param string $layout The name of the layout to check if the user has. * @param int $user_id The ID of the user to check the layout for. * @return bool Whether the given layout matches the user's layout. */ function has_user_layout( $layout, $user_id = '' ) { /* If no user ID is given, assume we're viewing an author archive page and get the user ID. */ if ( empty( $user_id ) ) $user_id = get_query_var( 'author' ); /* Return true/false based on whether the layout matches. */ return ( $layout == get_user_layout( $user_id ) ? true : false ); } /** * Adds the post layout class to the WordPress body class in the form of "layout-$layout". This allows * theme developers to design their theme layouts based on the layout class. If designing a theme with * this extension, the theme should make sure to handle all possible layout classes. * * @since 0.2.0 * @access public * @param array $classes * @return array $classes */ function theme_layouts_body_class( $classes ) { /* Adds the layout to array of body classes. */ $classes[] = sanitize_html_class( theme_layouts_get_layout() ); /* Return the $classes array. */ return $classes; } /** * Creates default text strings based on the default post layouts. Theme developers that add custom * layouts should filter 'post_layouts_strings' to add strings to match the custom layouts, but it's not * required. The layout name will be used if no text string is found. * * @since 0.2.0 * @access public * @return array $strings */ function theme_layouts_strings() { /* Set up the default layout strings. */ $strings = array( /* Translators: Default theme layout option. */ 'default' => _x( 'Default', 'theme layout', 'theme-layouts' ) ); /* Get theme-supported layouts. */ $layouts = get_theme_support( 'theme-layouts' ); /* Assign the strings passed in by the theme author. */ if ( isset( $layouts[0] ) ) $strings = array_merge( $layouts[0], $strings ); /* Allow devs to filter the strings for custom layouts. */ return apply_filters( 'theme_layouts_strings', $strings ); } /** * Get a specific layout's text string. * * @since 0.2.0 * @param string $layout * @return string */ function theme_layouts_get_string( $layout ) { /* Get an array of post layout strings. */ $strings = theme_layouts_strings(); /* Return the layout's string if it exists. Else, return the layout slug. */ return ( ( isset( $strings[ $layout ] ) ) ? $strings[ $layout ] : $layout ); } /** * Post layouts admin setup. Registers the post layouts meta box for the post editing screen. Adds the * metadata save function to the 'save_post' hook. * * @since 0.2.0 * @access public * @return void */ function theme_layouts_admin_setup() { /* Get the extension arguments. */ $args = theme_layouts_get_args(); /* Return if the theme doesn't support the post meta box. */ if ( false === $args['post_meta'] ) return; /* Load the post meta boxes on the new post and edit post screens. */ add_action( 'load-post.php', 'theme_layouts_load_meta_boxes' ); add_action( 'load-post-new.php', 'theme_layouts_load_meta_boxes' ); /* If the attachment post type supports 'theme-layouts', add form fields for it. */ if ( post_type_supports( 'attachment', 'theme-layouts' ) ) { /* Adds a theme layout />
  • />
  • ID ); /* Set the default post layout. */ $select = ''; /* Loop through each theme-supported layout, adding it to the select element. */ foreach ( $post_layouts as $layout ) $select .= ''; /* Set the HTML for the post layout select drop-down. */ $select = ''; /* Add the attachment layout field to the $fields array. */ $fields['theme-layouts-post-layout'] = array( 'label' => __( 'Layout', 'theme-layouts' ), 'input' => 'html', 'html' => $select, 'show_in_edit' => false, 'show_in_modal' => true ); /* Return the $fields array back to WordPress. */ return $fields; } /** * Saves the attachment layout for the attachment edit form. * * @since 0.3.0 * @access public * @param array $post The attachment post array (not the post object!). * @param array $fields Array of fields for the edit attachment form. * @return array $post */ function theme_layouts_attachment_fields_to_save( $post, $fields ) { /* If the theme layouts field was submitted. */ if ( isset( $fields['theme-layouts-post-layout'] ) ) { /* Get the meta key. */ $meta_key = theme_layouts_get_meta_key(); /* Get the previous post layout. */ $meta_value = get_post_layout( $post['ID'] ); /* Get the submitted post layout. */ $new_meta_value = $fields['theme-layouts-post-layout']; /* If there is no new meta value but an old value exists, delete it. */ if ( current_user_can( 'delete_post_meta', $post['ID'], $meta_key ) && '' == $new_meta_value && $meta_value ) delete_post_layout( $post['ID'] ); /* If a new meta value was added and there was no previous value, add it. */ elseif ( current_user_can( 'add_post_meta', $post['ID'], $meta_key ) && $new_meta_value && '' == $meta_value ) set_post_layout( $post['ID'], $new_meta_value ); /* If the old layout doesn't match the new layout, update the post layout meta. */ elseif ( current_user_can( 'edit_post_meta', $post['ID'], $meta_key ) && $meta_value !== $new_meta_value ) set_post_layout( $post['ID'], $new_meta_value ); } /* Return the attachment post array. */ return $post; } /** * Wrapper function for returning the metadata key used for objects that can use layouts. * * @since 0.3.0 * @access public * @return string The meta key used for theme layouts. */ function theme_layouts_get_meta_key() { return apply_filters( 'theme_layouts_meta_key', 'Layout' ); } /** * Registers custom sections, settings, and controls for the $wp_customize instance. * * @since 0.1.0 * @author Justin Tadlock * @author Sami Keijonen * @copyright Copyright (c) 2012 * @link http://themehybrid.com/support/topic/add-theme-layout-in-theme-customize * @access public * @param object $wp_customize */ function theme_layouts_customize_register( $wp_customize ) { /* Get supported theme layouts. */ $layouts = theme_layouts_get_layouts(); $args = theme_layouts_get_args(); if ( true === $args['customize'] ) { /* Add the layout section. */ $wp_customize->add_section( 'layout', array( 'title' => esc_html__( 'Layout', 'theme-layouts' ), 'priority' => 30, 'capability' => 'edit_theme_options' ) ); /* Add the 'layout' setting. */ $wp_customize->add_setting( 'theme_layout', array( 'default' => get_theme_mod( 'theme_layout', $args['default'] ), 'type' => 'theme_mod', 'capability' => 'edit_theme_options', 'sanitize_callback' => 'sanitize_html_class', 'transport' => 'postMessage' ) ); /* Set up an array for the layout choices and add in the 'default' layout. */ $layout_choices = array(); /* Only add 'default' if it's the actual default layout. */ if ( 'default' == $args['default'] ) $layout_choices['default'] = theme_layouts_get_string( 'default' ); /* Loop through each of the layouts and add it to the choices array with proper key/value pairs. */ foreach ( $layouts as $layout ) $layout_choices[$layout] = theme_layouts_get_string( $layout ); /* Add the layout control. */ $wp_customize->add_control( 'theme-layout-control', array( 'label' => esc_html__( 'Global Layout', 'theme-layouts' ), 'section' => 'layout', 'settings' => 'theme_layout', 'type' => 'radio', 'choices' => $layout_choices ) ); /* If viewing the customize preview screen, add a script to show a live preview. */ if ( $wp_customize->is_preview() && !is_admin() ) add_action( 'wp_footer', 'theme_layouts_customize_preview_script', 21 ); } } /** * JavaScript for handling the live preview editing of the theme layout in the theme customizer. The * script uses regex to remove all potential "layout-xyz" classes and replaces it with the user-selected * layout. * * @since 0.1.0 * @access public * @author Justin Tadlock * @author Sami Keijonen * @copyright Copyright (c) 2012 * @link http://themehybrid.com/support/topic/add-theme-layout-in-theme-customize * @return void */ function theme_layouts_customize_preview_script() { ?>