<?php /** * Site Logo class main class file. * * @package automattic/jetpack */ /** * Site Logo class for managing a theme-agnostic logo through the Customizer. */ class Site_Logo { /** * Stores our single instance. * * @var Site_Logo */ private static $instance; /** * Stores the attachment ID of the site logo. * * @var int */ public $logo; /** * Return our instance, creating a new one if necessary. * * @uses Site_Logo::$instance * @return object Site_Logo */ public static function instance() { if ( ! isset( self::$instance ) ) { self::$instance = new Site_Logo(); self::$instance->register_hooks(); } return self::$instance; } /** * Get our current logo settings stored in options. * * @uses get_option() */ private function __construct() { $this->logo = (int) get_option( 'site_logo', null ); } /** * Register our actions and filters. * * @uses Site_Logo::head_text_styles() * @uses Site_Logo::customize_register() * @uses Site_Logo::preview_enqueue() * @uses Site_Logo::body_classes() * @uses Site_Logo::media_manager_image_sizes() * @uses add_action * @uses add_filter */ public function register_hooks() { // This would only happen if a theme supports BOTH site-logo and custom-logo for some reason if ( current_theme_supports( 'custom-logo' ) ) { return; } add_action( 'wp_head', array( $this, 'head_text_styles' ) ); add_action( 'customize_register', array( $this, 'customize_register' ) ); add_action( 'customize_preview_init', array( $this, 'preview_enqueue' ) ); add_action( 'delete_attachment', array( $this, 'reset_on_attachment_delete' ) ); add_filter( 'body_class', array( $this, 'body_classes' ) ); add_filter( 'image_size_names_choose', array( $this, 'media_manager_image_sizes' ) ); add_filter( 'display_media_states', array( $this, 'add_media_state' ) ); } /** * Add our logo uploader to the Customizer. * * @param object $wp_customize Customizer object. * @uses current_theme_supports() * @uses current_theme_supports() * @uses WP_Customize_Manager::add_setting() * @uses WP_Customize_Manager::add_control() * @uses Site_Logo::sanitize_checkbox() */ public function customize_register( $wp_customize ) { // Add a setting to hide header text if the theme isn't supporting the feature itself if ( ! current_theme_supports( 'custom-header' ) ) { $wp_customize->add_setting( 'site_logo_header_text', array( 'default' => 1, 'sanitize_callback' => array( $this, 'sanitize_checkbox' ), 'transport' => 'postMessage', ) ); $wp_customize->add_control( new WP_Customize_Control( $wp_customize, 'site_logo_header_text', array( 'label' => __( 'Display Header Text', 'jetpack' ), 'section' => 'title_tagline', 'settings' => 'site_logo_header_text', 'type' => 'checkbox', ) ) ); } // Add the setting for our logo value. $wp_customize->add_setting( 'site_logo', array( 'capability' => 'manage_options', 'default' => 0, 'sanitize_callback' => array( $this, 'sanitize_logo_setting' ), 'transport' => 'postMessage', 'type' => 'option', ) ); // By default, not setting width and height will suggest a square crop. $width = null; $height = null; $logo_size = jetpack_get_site_logo_dimensions(); // Only suggested a different crop if the theme declares both dimensions. if ( false !== $logo_size && $logo_size['width'] && $logo_size['height'] ) { $width = $logo_size['width']; $height = $logo_size['height']; } // Add our image uploader. $wp_customize->add_control( new WP_Customize_Cropped_Image_Control( $wp_customize, 'site_logo', array( 'label' => __( 'Logo', 'jetpack' ), 'section' => 'title_tagline', 'settings' => 'site_logo', 'width' => $width, 'height' => $height, 'flex_width' => true, 'flex_height' => true, 'button_labels' => array( 'select' => __( 'Add logo', 'jetpack' ), 'change' => __( 'Change logo', 'jetpack' ), 'remove' => __( 'Remove logo', 'jetpack' ), 'placeholder' => __( 'No logo set', 'jetpack' ), 'frame_title' => __( 'Set as logo', 'jetpack' ), 'frame_button' => __( 'Choose logo', 'jetpack' ), ), ) ) ); $wp_customize->selective_refresh->add_partial( 'site_logo', array( 'settings' => 'site_logo', 'selector' => '.site-logo-link', 'render_callback' => array( $this, 'customizer_preview' ), 'container_inclusive' => true, ) ); } /** * Enqueue scripts for the Customizer live preview. * * @uses wp_enqueue_script() * @uses plugins_url() * @uses current_theme_supports() * @uses Site_Logo::header_text_classes() * @uses wp_localize_script() */ public function preview_enqueue() { // Don't bother passing in header text classes if the theme supports custom headers. if ( ! current_theme_supports( 'custom-header' ) ) { $classes = jetpack_sanitize_header_text_classes( $this->header_text_classes() ); wp_enqueue_script( 'site-logo-header-text', plugins_url( '../js/site-logo-header-text.js', __FILE__ ), array( 'media-views' ), JETPACK__VERSION, true ); wp_localize_script( 'site-logo-header-text', 'site_logo_header_classes', array( 'classes' => $classes ) ); } } /** * Get header text classes. If not defined in add_theme_support(), defaults from Underscores will be used. * * @uses get_theme_support * @return string String of classes to hide */ public function header_text_classes() { $args = get_theme_support( 'site-logo' ); if ( isset( $args[0]['header-text'] ) ) { // Use any classes defined in add_theme_support(). $classes = $args[0]['header-text']; } else { // Otherwise, use these defaults, which will work with any Underscores-based theme. $classes = array( 'site-title', 'site-description', ); } // If we've got an array, reduce them to a string for output if ( is_array( $classes ) ) { $classes = (string) '.' . implode( ', .', $classes ); } else { $classes = (string) '.' . $classes; } return $classes; } /** * Hide header text on front-end if necessary. * * @uses current_theme_supports() * @uses get_theme_mod() * @uses Site_Logo::header_text_classes() * @uses esc_html() */ public function head_text_styles() { // Bail if our theme supports custom headers. if ( current_theme_supports( 'custom-header' ) ) { return; } // Is Display Header Text unchecked? If so, we need to hide our header text. if ( ! get_theme_mod( 'site_logo_header_text', 1 ) ) { $classes = $this->header_text_classes(); ?> <!-- Site Logo: hide header text --> <style type="text/css"> <?php echo jetpack_sanitize_header_text_classes( $classes ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> { position: absolute; clip: rect(1px, 1px, 1px, 1px); } </style> <?php } } /** * Determine image size to use for the logo. * * @uses get_theme_support() * @return string Size specified in add_theme_support declaration, or 'thumbnail' default */ public function theme_size() { $args = get_theme_support( 'site-logo' ); $valid_sizes = get_intermediate_image_sizes(); // Add 'full' to the list of accepted values. $valid_sizes[] = 'full'; // If the size declared in add_theme_support is valid, use it; otherwise, just go with 'thumbnail'. $size = ( isset( $args[0]['size'] ) && in_array( $args[0]['size'], $valid_sizes, true ) ) ? $args[0]['size'] : 'thumbnail'; return $size; } /** * Make custom image sizes available to the media manager. * * @param array $sizes List of image sizes. * @uses get_intermediate_image_sizes() * @return array All default and registered custom image sizes. */ public function media_manager_image_sizes( $sizes ) { // Get an array of all registered image sizes. $intermediate = get_intermediate_image_sizes(); // Have we got anything fun to work with? if ( is_array( $intermediate ) && ! empty( $intermediate ) ) { foreach ( $intermediate as $size ) { // If the size isn't already in the $sizes array, add it. if ( ! array_key_exists( $size, $sizes ) ) { $sizes[ $size ] = $size; } } } return $sizes; } /** * Add site logos to media states in the Media Manager. * * @param array $media_states An array of media states. * * @return array The current attachment's media states. */ public function add_media_state( $media_states ) { // Only bother testing if we have a site logo set. if ( $this->has_site_logo() ) { global $post; // If our attachment ID and the site logo ID match, this image is the site logo. if ( $post && $post->ID === $this->logo ) { $media_states[] = __( 'Site Logo', 'jetpack' ); } } return $media_states; } /** * Reset the site logo if the current logo is deleted in the media manager. * * @param int $post_id Attachment ID. * @uses Site_Logo::remove_site_logo() */ public function reset_on_attachment_delete( $post_id ) { if ( $this->logo === $post_id ) { $this->remove_site_logo(); } } /** * Determine if a site logo is assigned or not. * * @uses Site_Logo::$logo * @return boolean True if there is an active logo, false otherwise */ public function has_site_logo() { return (bool) $this->logo; } /** * Reset the site logo option to zero (empty). * * @uses update_option() */ public function remove_site_logo() { update_option( 'site_logo', null ); } /** * Adds custom classes to the array of body classes. * * @uses Site_Logo::has_site_logo() * @param array $classes Classes for the body element. * @return array Array of <body> classes */ public function body_classes( $classes ) { // Add a class if a Site Logo is active if ( $this->has_site_logo() ) { $classes[] = 'has-site-logo'; } return $classes; } /** * Sanitize our header text Customizer setting. * * @param any $input The input value to sanitize. * @return bool|string 1 if checked, empty string if not checked. */ public function sanitize_checkbox( $input ) { return ( 1 === (int) $input ) ? 1 : ''; } /** * Validate and sanitize a new site logo setting. * * @param any $input Logo setting value to sanitize. * @return int Attachment post ID, or 0 if invalid. */ public function sanitize_logo_setting( $input ) { $input = absint( $input ); // If the new setting doesn't point to a valid attachment, just reset the whole thing. if ( false === wp_get_attachment_image_src( $input ) ) { $input = 0; } return $input; } /** * This function returns the updated HTML in the Customizer preview when the logo is added, updated, or removed. * * @return string */ public function customizer_preview() { ob_start(); jetpack_the_site_logo(); return ob_get_clean(); } } /** * Allow themes and plugins to access Site_Logo methods and properties. * * @uses Site_Logo::instance() * @return object Site_Logo */ function site_logo() { return Site_Logo::instance(); } /** * One site logo, please. */ site_logo();