class-site-logo.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. /**
  3. * Our Site Logo class for managing a theme-agnostic logo through the Customizer.
  4. *
  5. * @package Jetpack
  6. */
  7. class Site_Logo {
  8. /**
  9. * Stores our single instance.
  10. */
  11. private static $instance;
  12. /**
  13. * Stores our current logo settings.
  14. */
  15. public $logo;
  16. /**
  17. * Return our instance, creating a new one if necessary.
  18. *
  19. * @uses Site_Logo::$instance
  20. * @return object Site_Logo
  21. */
  22. public static function instance() {
  23. if ( ! isset( self::$instance ) ) {
  24. self::$instance = new Site_Logo;
  25. self::$instance->register_hooks();
  26. }
  27. return self::$instance;
  28. }
  29. /**
  30. * Get our current logo settings stored in options.
  31. *
  32. * @uses get_option()
  33. */
  34. private function __construct() {
  35. $this->logo = get_option( 'site_logo', null );
  36. }
  37. /**
  38. * Register our actions and filters.
  39. *
  40. * @uses Site_Logo::head_text_styles()
  41. * @uses Site_Logo::customize_register()
  42. * @uses Site_Logo::preview_enqueue()
  43. * @uses Site_Logo::body_classes()
  44. * @uses Site_Logo::media_manager_image_sizes()
  45. * @uses add_action
  46. * @uses add_filter
  47. */
  48. public function register_hooks() {
  49. // This would only happen if a theme supports BOTH site-logo and custom-logo for some reason
  50. if ( current_theme_supports( 'custom-logo' ) ) {
  51. return;
  52. }
  53. add_action( 'wp_head', array( $this, 'head_text_styles' ) );
  54. add_action( 'customize_register', array( $this, 'customize_register' ) );
  55. add_action( 'customize_preview_init', array( $this, 'preview_enqueue' ) );
  56. add_action( 'delete_attachment', array( $this, 'reset_on_attachment_delete' ) );
  57. add_filter( 'body_class', array( $this, 'body_classes' ) );
  58. add_filter( 'image_size_names_choose', array( $this, 'media_manager_image_sizes' ) );
  59. add_filter( 'display_media_states', array( $this, 'add_media_state' ) );
  60. }
  61. /**
  62. * Add our logo uploader to the Customizer.
  63. *
  64. * @param object $wp_customize Customizer object.
  65. * @uses current_theme_supports()
  66. * @uses current_theme_supports()
  67. * @uses WP_Customize_Manager::add_setting()
  68. * @uses WP_Customize_Manager::add_control()
  69. * @uses Site_Logo::sanitize_checkbox()
  70. */
  71. public function customize_register( $wp_customize ) {
  72. // Include our custom control.
  73. require( dirname( __FILE__ ) . '/class-site-logo-control.php' );
  74. // Add a setting to hide header text if the theme isn't supporting the feature itself
  75. if ( ! current_theme_supports( 'custom-header' ) ) {
  76. $wp_customize->add_setting( 'site_logo_header_text', array(
  77. 'default' => 1,
  78. 'sanitize_callback' => array( $this, 'sanitize_checkbox' ),
  79. 'transport' => 'postMessage',
  80. ) );
  81. $wp_customize->add_control( new WP_Customize_Control( $wp_customize, 'site_logo_header_text', array(
  82. 'label' => __( 'Display Header Text', 'jetpack' ),
  83. 'section' => 'title_tagline',
  84. 'settings' => 'site_logo_header_text',
  85. 'type' => 'checkbox',
  86. ) ) );
  87. }
  88. // Add the setting for our logo value.
  89. $wp_customize->add_setting( 'site_logo', array(
  90. 'capability' => 'manage_options',
  91. 'default' => array(
  92. 'id' => 0,
  93. 'sizes' => array(),
  94. 'url' => false,
  95. ),
  96. 'sanitize_callback' => array( $this, 'sanitize_logo_setting' ),
  97. 'transport' => 'postMessage',
  98. 'type' => 'option',
  99. ) );
  100. // Add our image uploader.
  101. $wp_customize->add_control( new Site_Logo_Image_Control( $wp_customize, 'site_logo', array(
  102. 'label' => __( 'Logo', 'jetpack' ),
  103. 'section' => 'title_tagline',
  104. 'settings' => 'site_logo',
  105. ) ) );
  106. }
  107. /**
  108. * Enqueue scripts for the Customizer live preview.
  109. *
  110. * @uses wp_enqueue_script()
  111. * @uses plugins_url()
  112. * @uses current_theme_supports()
  113. * @uses Site_Logo::header_text_classes()
  114. * @uses wp_localize_script()
  115. */
  116. public function preview_enqueue() {
  117. wp_enqueue_script( 'site-logo-preview', plugins_url( '../js/site-logo.js', __FILE__ ), array( 'media-views' ), '', true );
  118. // Don't bother passing in header text classes if the theme supports custom headers.
  119. if ( ! current_theme_supports( 'custom-header' ) ) {
  120. $classes = jetpack_sanitize_header_text_classes( $this->header_text_classes() );
  121. wp_enqueue_script( 'site-logo-header-text', plugins_url( '../js/site-logo-header-text.js', __FILE__ ), array( 'media-views' ), '', true );
  122. wp_localize_script( 'site-logo-header-text', 'site_logo_header_classes', $classes );
  123. }
  124. }
  125. /**
  126. * Get header text classes. If not defined in add_theme_support(), defaults from Underscores will be used.
  127. *
  128. * @uses get_theme_support
  129. * @return string String of classes to hide
  130. */
  131. public function header_text_classes() {
  132. $args = get_theme_support( 'site-logo' );
  133. if ( isset( $args[0][ 'header-text' ] ) ) {
  134. // Use any classes defined in add_theme_support().
  135. $classes = $args[0][ 'header-text' ];
  136. } else {
  137. // Otherwise, use these defaults, which will work with any Underscores-based theme.
  138. $classes = array(
  139. 'site-title',
  140. 'site-description',
  141. );
  142. }
  143. // If we've got an array, reduce them to a string for output
  144. if ( is_array( $classes ) ) {
  145. $classes = (string) '.' . implode( ', .', $classes );
  146. } else {
  147. $classes = (string) '.' . $classes;
  148. }
  149. return $classes;
  150. }
  151. /**
  152. * Hide header text on front-end if necessary.
  153. *
  154. * @uses current_theme_supports()
  155. * @uses get_theme_mod()
  156. * @uses Site_Logo::header_text_classes()
  157. * @uses esc_html()
  158. */
  159. public function head_text_styles() {
  160. // Bail if our theme supports custom headers.
  161. if ( current_theme_supports( 'custom-header' ) ) {
  162. return;
  163. }
  164. // Is Display Header Text unchecked? If so, we need to hide our header text.
  165. if ( ! get_theme_mod( 'site_logo_header_text', 1 ) ) {
  166. $classes = $this->header_text_classes();
  167. ?>
  168. <!-- Site Logo: hide header text -->
  169. <style type="text/css">
  170. <?php echo jetpack_sanitize_header_text_classes( $classes ); ?> {
  171. position: absolute;
  172. clip: rect(1px, 1px, 1px, 1px);
  173. }
  174. </style>
  175. <?php
  176. }
  177. }
  178. /**
  179. * Determine image size to use for the logo.
  180. *
  181. * @uses get_theme_support()
  182. * @return string Size specified in add_theme_support declaration, or 'thumbnail' default
  183. */
  184. public function theme_size() {
  185. $args = get_theme_support( 'site-logo' );
  186. $valid_sizes = get_intermediate_image_sizes();
  187. // Add 'full' to the list of accepted values.
  188. $valid_sizes[] = 'full';
  189. // If the size declared in add_theme_support is valid, use it; otherwise, just go with 'thumbnail'.
  190. $size = ( isset( $args[0]['size'] ) && in_array( $args[0]['size'], $valid_sizes ) ) ? $args[0]['size'] : 'thumbnail';
  191. return $size;
  192. }
  193. /**
  194. * Make custom image sizes available to the media manager.
  195. *
  196. * @param array $sizes
  197. * @uses get_intermediate_image_sizes()
  198. * @return array All default and registered custom image sizes.
  199. */
  200. public function media_manager_image_sizes( $sizes ) {
  201. // Get an array of all registered image sizes.
  202. $intermediate = get_intermediate_image_sizes();
  203. // Have we got anything fun to work with?
  204. if ( is_array( $intermediate ) && ! empty( $intermediate ) ) {
  205. foreach ( $intermediate as $key => $size ) {
  206. // If the size isn't already in the $sizes array, add it.
  207. if ( ! array_key_exists( $size, $sizes ) ) {
  208. $sizes[ $size ] = $size;
  209. }
  210. }
  211. }
  212. return $sizes;
  213. }
  214. /**
  215. * Add site logos to media states in the Media Manager.
  216. *
  217. * @return array The current attachment's media states.
  218. */
  219. public function add_media_state( $media_states ) {
  220. // Only bother testing if we have a site logo set.
  221. if ( $this->has_site_logo() ) {
  222. global $post;
  223. // If our attachment ID and the site logo ID match, this image is the site logo.
  224. if ( $post->ID == $this->logo['id'] ) {
  225. $media_states[] = __( 'Site Logo', 'jetpack' );
  226. }
  227. }
  228. return $media_states;
  229. }
  230. /**
  231. * Reset the site logo if the current logo is deleted in the media manager.
  232. *
  233. * @param int $site_id
  234. * @uses Site_Logo::remove_site_logo()
  235. */
  236. public function reset_on_attachment_delete( $post_id ) {
  237. if ( $this->logo['id'] == $post_id ) {
  238. $this->remove_site_logo();
  239. }
  240. }
  241. /**
  242. * Determine if a site logo is assigned or not.
  243. *
  244. * @uses Site_Logo::$logo
  245. * @return boolean True if there is an active logo, false otherwise
  246. */
  247. public function has_site_logo() {
  248. return ( isset( $this->logo['id'] ) && 0 !== $this->logo['id'] ) ? true : false;
  249. }
  250. /**
  251. * Reset the site logo option to zero (empty).
  252. *
  253. * @uses update_option()
  254. */
  255. public function remove_site_logo() {
  256. update_option( 'site_logo', array(
  257. 'id' => (int) 0,
  258. 'sizes' => array(),
  259. 'url' => '',
  260. ) );
  261. }
  262. /**
  263. * Adds custom classes to the array of body classes.
  264. *
  265. * @uses Site_Logo::has_site_logo()
  266. * @return array Array of <body> classes
  267. */
  268. public function body_classes( $classes ) {
  269. // Add a class if a Site Logo is active
  270. if ( $this->has_site_logo() ) {
  271. $classes[] = 'has-site-logo';
  272. }
  273. return $classes;
  274. }
  275. /**
  276. * Sanitize our header text Customizer setting.
  277. *
  278. * @param $input
  279. * @return mixed 1 if checked, empty string if not checked.
  280. */
  281. public function sanitize_checkbox( $input ) {
  282. return ( 1 == $input ) ? 1 : '';
  283. }
  284. /**
  285. * Validate and sanitize a new site logo setting.
  286. *
  287. * @param $input
  288. * @return mixed 1 if checked, empty string if not checked.
  289. */
  290. public function sanitize_logo_setting( $input ) {
  291. $input['id'] = absint( $input['id'] );
  292. $input['url'] = esc_url_raw( $input['url'] );
  293. // If the new setting doesn't point to a valid attachment, just reset the whole thing.
  294. if ( false == wp_get_attachment_image_src( $input['id'] ) ) {
  295. $input = array(
  296. 'id' => (int) 0,
  297. 'sizes' => array(),
  298. 'url' => '',
  299. );
  300. }
  301. return $input;
  302. }
  303. }
  304. /**
  305. * Allow themes and plugins to access Site_Logo methods and properties.
  306. *
  307. * @uses Site_Logo::instance()
  308. * @return object Site_Logo
  309. */
  310. function site_logo() {
  311. return Site_Logo::instance();
  312. }
  313. /**
  314. * One site logo, please.
  315. */
  316. site_logo();