WordPress.org

Make WordPress Core

Ticket #16434: 16434.customize.diff

File 16434.customize.diff, 24.1 KB (added by celloexpressions, 21 months ago)

First-pass at site icons and a cropped-image control in the Customizer.

  • src/wp-admin/css/customize-controls.css

     
    758758.customize-control-upload .current, 
    759759.customize-control-image .current, 
    760760.customize-control-background .current, 
     761.customize-control-cropped_image .current, 
     762.customize-control-site_icon .current, 
    761763.customize-control-header .current { 
    762764        margin-bottom: 8px; 
    763765} 
     
    793795.customize-control-background .remove-button, 
    794796.customize-control-background .default-button, 
    795797.customize-control-background .upload-button, 
     798.customize-control-cropped_image .remove-button, 
     799.customize-control-cropped_image .default-button, 
     800.customize-control-cropped_image .upload-button, 
     801.customize-control-site_icon .remove-button, 
     802.customize-control-site_icon .default-button, 
     803.customize-control-site_icon .upload-button, 
    796804.customize-control-header button.new, 
    797805.customize-control-header button.remove { 
    798806        white-space: normal; 
     
    804812.customize-control-upload .current .container, 
    805813.customize-control-image .current .container, 
    806814.customize-control-background .current .container, 
     815.customize-control-cropped_image .current .container, 
     816.customize-control-site_icon .current .container, 
    807817.customize-control-header .current .container { 
    808818        overflow: hidden; 
    809819        -webkit-border-radius: 2px; 
     
    815825.customize-control-media .current .container, 
    816826.customize-control-upload .current .container, 
    817827.customize-control-background .current .container, 
     828.customize-control-cropped_image .current .container, 
     829.customize-control-site_icon .current .container, 
    818830.customize-control-image .current .container { 
    819831        min-height: 40px; 
    820832} 
     
    823835.customize-control-upload .placeholder, 
    824836.customize-control-image .placeholder, 
    825837.customize-control-background .placeholder, 
     838.customize-control-cropped_image .placeholder, 
     839.customize-control-site_icon .placeholder, 
    826840.customize-control-header .placeholder { 
    827841        width: 100%; 
    828842        position: relative; 
     
    834848.customize-control-upload .inner, 
    835849.customize-control-image .inner, 
    836850.customize-control-background .inner, 
     851.customize-control-cropped_image .inner, 
     852.customize-control-site_icon .inner, 
    837853.customize-control-header .inner { 
    838854        display: none; 
    839855        position: absolute; 
     
    847863.customize-control-media .inner, 
    848864.customize-control-upload .inner, 
    849865.customize-control-background .inner, 
     866.customize-control-cropped_image .inner, 
     867.customize-control-site_icon .inner, 
    850868.customize-control-image .inner { 
    851869        display: block; 
    852870        min-height: 40px; 
     
    856874.customize-control-upload .inner, 
    857875.customize-control-image .inner, 
    858876.customize-control-background .inner, 
     877.customize-control-cropped_image .inner, 
     878.customize-control-site_icon .inner, 
    859879.customize-control-header .inner, 
    860880.customize-control-header .inner .dashicons { 
    861881        line-height: 20px; 
     
    959979.customize-control-upload .actions, 
    960980.customize-control-image .actions, 
    961981.customize-control-background .actions, 
     982.customize-control-cropped_image .actions, 
     983.customize-control-site_icon .actions, 
    962984.customize-control-header .actions { 
    963985        margin-bottom: 32px; 
    964986} 
     
    977999.customize-control-upload img, 
    9781000.customize-control-image img, 
    9791001.customize-control-background img, 
     1002.customize-control-cropped_image img, 
     1003.customize-control-site_icon img, 
    9801004.customize-control-header img { 
    9811005        width: 100%; 
    9821006        -webkit-border-radius: 2px; 
     
    9911015.customize-control-image .default-button, 
    9921016.customize-control-background .remove-button, 
    9931017.customize-control-background .default-button, 
     1018.customize-control-cropped_image .remove-button, 
     1019.customize-control-cropped_image .default-button, 
     1020.customize-control-site_icon .remove-button, 
     1021.customize-control-site_icon .default-button, 
    9941022.customize-control-header .remove { 
    9951023        float: left; 
    9961024        margin-right: 3px; 
     
    10001028.customize-control-upload .upload-button, 
    10011029.customize-control-image .upload-button, 
    10021030.customize-control-background .upload-button, 
     1031.customize-control-cropped_image .upload-button, 
     1032.customize-control-site_icon .upload-button, 
    10031033.customize-control-header .new { 
    10041034        float: right; 
    10051035} 
  • src/wp-admin/includes/class-wp-site-icon.php

     
    6565 
    6666                add_action( 'delete_attachment', array( $this, 'delete_attachment_data' ), 10, 1 ); 
    6767                add_filter( 'get_post_metadata', array( $this, 'get_post_metadata' ), 10, 4 ); 
     68 
     69                // Integrate with Ajax-cropped site icons from the Customizer. 
     70                add_action( 'customize_pre_insert_attachment_site_icon', array( $this, 'customize_pre_insert_attachment_filters' ) ); 
     71                add_action( 'customize_post_insert_attachment_site_icon', array( $this, 'customize_post_insert_attachment_filters' ) );  
    6872        } 
    6973 
    7074        /** 
     
    619623 
    620624                return array( $attachment_id, $url, $image_size ); 
    621625        } 
     626 
     627        /** 
     628         * Add intermediate image sizes when inserting the attachment in the Customizer. 
     629         * 
     630         * @since 4.3.0 
     631         */ 
     632        private function customize_pre_insert_attachment_filters() { 
     633                add_filter( 'intermediate_image_sizes', array( $this, 'intermediate_image_sizes' ) ); 
     634        } 
     635 
     636        /** 
     637         * Cleanup intermediate sizes. 
     638         * 
     639         * @since 4.3.0 
     640         */ 
     641        private function customize_post_insert_attachment_filters() { 
     642                remove_filter( 'intermediate_image_sizes', array( $this, 'intermediate_image_sizes' ) ); 
     643        } 
    622644} 
    623645 
    624646/** 
  • src/wp-admin/js/customize-controls.js

     
    18361836        }); 
    18371837 
    18381838        /** 
     1839         * A control for selecting and cropping an image. 
     1840         * 
    18391841         * @class 
     1842         * @augments wp.customize.MediaControl 
    18401843         * @augments wp.customize.Control 
    18411844         * @augments wp.customize.Class 
    18421845         */ 
     1846        api.CroppedImageControl = api.MediaControl.extend({ 
     1847                /** 
     1848                 * Open the media modal to the library state. 
     1849                 */ 
     1850                openFrame: function( event ) { 
     1851                        if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 
     1852                                return; 
     1853                        } 
     1854 
     1855                        if ( this.frame ) { 
     1856                                this.frame.setState( 'library' ); 
     1857                        } 
     1858 
     1859                        api.MediaControl.prototype.openFrame.call( this, event ); 
     1860                }, 
     1861 
     1862                /** 
     1863                 * Create a media modal select frame, and store it so the instance can be reused when needed. 
     1864                 */ 
     1865                initFrame: function() { 
     1866                        var l10n = _wpMediaViewsL10n; 
     1867 
     1868                        this.frame = wp.media({ 
     1869                                button: { 
     1870                                        text: l10n.selectAndCrop, 
     1871                                        close: false 
     1872                                }, 
     1873                                states: [ 
     1874                                        new wp.media.controller.Library({ 
     1875                                                title:     this.params.button_labels.frame_title, 
     1876                                                library:   wp.media.query({ type: 'image' }), 
     1877                                                multiple:  false, 
     1878                                                date:      false, 
     1879                                                priority:  20, 
     1880                                                suggestedWidth: this.params.width, 
     1881                                                suggestedHeight: this.params.height 
     1882                                        }), 
     1883                                        new wp.media.controller.customizeImageCropper({ 
     1884                                                imgSelectOptions: this.calculateImageSelectOptions, 
     1885                                                control: this 
     1886                                        }) 
     1887                                ] 
     1888                        }); 
     1889 
     1890                        this.frame.on( 'select', this.onSelect, this ); 
     1891                        this.frame.on( 'cropped', this.onCropped, this ); 
     1892                        this.frame.on( 'skippedcrop', this.onSkippedCrop, this ); 
     1893                }, 
     1894 
     1895                /** 
     1896                 * After an image is selected in the media modal, 
     1897                 * switch to the cropper state. 
     1898                 */ 
     1899                onSelect: function() { 
     1900                        this.frame.setState( 'cropper' ); 
     1901                }, 
     1902 
     1903                /** 
     1904                 * After the image has been cropped, apply the cropped image data to the setting. 
     1905                 * 
     1906                 * @param {object} croppedImage Cropped attachment data. 
     1907                 */ 
     1908                onCropped: function( croppedImage ) { 
     1909                        this.setImageFromAttachment( croppedImage ); 
     1910                }, 
     1911 
     1912                /** 
     1913                 * Returns a set of options, computed from the attached image data and 
     1914                 * control-specific data, to be fed to the imgAreaSelect plugin in 
     1915                 * wp.media.view.Cropper. 
     1916                 * 
     1917                 * @param {wp.media.model.Attachment} attachment 
     1918                 * @param {wp.media.controller.Cropper} controller 
     1919                 * @returns {Object} Options 
     1920                 */ 
     1921                calculateImageSelectOptions: function( attachment, controller ) { 
     1922                        var control = controller.get( 'control' ), 
     1923                            xInit = parseInt( control.params.width, 10 ), 
     1924                            yInit = parseInt( control.params.height, 10 ), 
     1925                            flexWidth = !! parseInt( control.params.flex_width, 10 ), 
     1926                            flexHeight = !! parseInt( control.params.flex_height, 10 ), 
     1927                            ratio, xImg, yImg, realHeight, realWidth, 
     1928                            imgSelectOptions; 
     1929 
     1930                        realWidth = attachment.get( 'width' ); 
     1931                        realHeight = attachment.get( 'height' ); 
     1932 
     1933                        controller.set( 'canSkipCrop', ! control.mustBeCropped( flexWidth, flexHeight, xInit, yInit, realWidth, realHeight ) ); 
     1934 
     1935                        ratio = xInit / yInit; 
     1936                        xImg = realWidth; 
     1937                        yImg = realHeight; 
     1938 
     1939                        if ( xImg / yImg > ratio ) { 
     1940                                yInit = yImg; 
     1941                                xInit = yInit * ratio; 
     1942                        } else { 
     1943                                xInit = xImg; 
     1944                                yInit = xInit / ratio; 
     1945                        } 
     1946 
     1947                        imgSelectOptions = { 
     1948                                handles: true, 
     1949                                keys: true, 
     1950                                instance: true, 
     1951                                persistent: true, 
     1952                                imageWidth: realWidth, 
     1953                                imageHeight: realHeight, 
     1954                                x1: 0, 
     1955                                y1: 0, 
     1956                                x2: xInit, 
     1957                                y2: yInit 
     1958                        }; 
     1959 
     1960                        if ( flexHeight === false && flexWidth === false ) { 
     1961                                imgSelectOptions.aspectRatio = xInit + ':' + yInit; 
     1962                        } 
     1963                        if ( flexHeight === false ) { 
     1964                                imgSelectOptions.maxHeight = yInit; 
     1965                        } 
     1966                        if ( flexWidth === false ) { 
     1967                                imgSelectOptions.maxWidth = xInit; 
     1968                        } 
     1969 
     1970                        return imgSelectOptions; 
     1971                }, 
     1972 
     1973                /** 
     1974                 * Return whether the image must be cropped, based on required dimensions. 
     1975                 */ 
     1976                mustBeCropped: function( flexW, flexH, dstW, dstH, imgW, imgH ) { 
     1977                        if ( flexW === true && flexH === true ) { 
     1978                                return false; 
     1979                        } 
     1980 
     1981                        if ( flexW === true && dstH === imgH ) { 
     1982                                return false; 
     1983                        } 
     1984 
     1985                        if ( flexH === true && dstW === imgW ) { 
     1986                                return false; 
     1987                        } 
     1988 
     1989                        if ( dstW === imgW && dstH === imgH ) { 
     1990                                return false; 
     1991                        } 
     1992 
     1993                        if ( imgW <= dstW ) { 
     1994                                return false; 
     1995                        } 
     1996 
     1997                        return true; 
     1998                }, 
     1999 
     2000                /** 
     2001                 * If cropping was skipped, apply the image data directly to the setting. 
     2002                 */ 
     2003                onSkippedCrop: function() { 
     2004                        var attachment = this.frame.state().get( 'selection' ).first().toJSON(); 
     2005                        this.setImageFromAttachment( attachment ); 
     2006                }, 
     2007 
     2008                /** 
     2009                 * Updates the setting and re-renders the control UI. 
     2010                 * 
     2011                 * @param {object} attachment 
     2012                 */ 
     2013                setImageFromAttachment: function( attachment ) { 
     2014                        this.params.attachment = attachment; 
     2015 
     2016                        // Set the Customizer setting; the callback takes care of rendering. 
     2017                        this.setting( attachment.id ); 
     2018                }, 
     2019 
     2020        }); 
     2021 
     2022        /** 
     2023         * A control for selecting and cropping an image. 
     2024         * 
     2025         * @class 
     2026         * @augments wp.customize.CroppedImageControl 
     2027         * @augments wp.customize.MediaControl 
     2028         * @augments wp.customize.Control 
     2029         * @augments wp.customize.Class 
     2030         */ 
     2031        api.SiteIconControl = api.CroppedImageControl.extend({ 
     2032                /** 
     2033                 * Updates the setting and re-renders the control UI. 
     2034                 * 
     2035                 * @param {object} attachment 
     2036                 */ 
     2037                setImageFromAttachment: function( attachment ) { 
     2038                        this.params.attachment = attachment; 
     2039 
     2040                        // Set the Customizer setting; the callback takes care of rendering. 
     2041                        this.setting( attachment.id ); 
     2042 
     2043                        // Update the icon in-browser. 
     2044                        $( '#wp-site-icon-favicon' ).attr( 'href', attachment.sizes.thumbnail.url ); 
     2045                }, 
     2046 
     2047                /** 
     2048                 * Called when the "Remove" link is clicked. Empties the setting. 
     2049                 * 
     2050                 * @param {object} event jQuery Event object 
     2051                 */ 
     2052                removeFile: function( event ) { 
     2053                        if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 
     2054                                return; 
     2055                        } 
     2056                        event.preventDefault(); 
     2057 
     2058                        this.params.attachment = {}; 
     2059                        this.setting( '' ); 
     2060                        this.renderContent(); // Not bound to setting change when emptying. 
     2061                        $( '#wp-site-icon-favicon' ).attr( 'href', '' ); 
     2062                } 
     2063        }); 
     2064 
     2065        /** 
     2066         * @class 
     2067         * @augments wp.customize.Control 
     2068         * @augments wp.customize.Class 
     2069         */ 
    18432070        api.HeaderControl = api.Control.extend({ 
    18442071                ready: function() { 
    18452072                        this.btnRemove = $('#customize-control-header_image .actions .remove'); 
     
    26952922        }); 
    26962923 
    26972924        api.controlConstructor = { 
    2698                 color:      api.ColorControl, 
    2699                 media:      api.MediaControl, 
    2700                 upload:     api.UploadControl, 
    2701                 image:      api.ImageControl, 
    2702                 header:     api.HeaderControl, 
    2703                 background: api.BackgroundControl, 
    2704                 theme:      api.ThemeControl 
     2925                color:         api.ColorControl, 
     2926                media:         api.MediaControl, 
     2927                upload:        api.UploadControl, 
     2928                image:         api.ImageControl, 
     2929                cropped_image: api.CroppedImageControl, 
     2930                site_icon:     api.SiteIconControl, 
     2931                header:        api.HeaderControl, 
     2932                background:    api.BackgroundControl, 
     2933                theme:         api.ThemeControl 
    27052934        }; 
    27062935        api.panelConstructor = {}; 
    27072936        api.sectionConstructor = { 
  • src/wp-includes/class-wp-customize-ajax-actions.php

     
     1<?php 
     2/** 
     3 * Ajax Actions for the Customizer. 
     4 * 
     5 * @package WordPress 
     6 * @subpackage Customize 
     7 */ 
     8 
     9/** 
     10 * Ajax actions for Customizer controls. 
     11 * 
     12 * @since 4.3.0 
     13 * @package WordPress 
     14 * @subpackage Customize 
     15 */ 
     16class WP_Customize_Ajax_Actions { 
     17        public function __construct() { 
     18                add_action( 'wp_ajax_customize-image-crop', array( $this, 'ajax_image_crop' ) ); 
     19        } 
     20 
     21        /** 
     22         * Gets attachment uploaded by Media Manager, crops it, then saves it as a 
     23         * new object. Returns JSON-encoded object details. 
     24         */ 
     25        public function ajax_image_crop() { 
     26                check_ajax_referer( 'image_editor-' . absint( $_POST['id'] ), 'nonce' ); 
     27                if ( ! current_user_can( 'customize' ) ) { 
     28                        wp_send_json_error(); 
     29                } 
     30                $crop_details = $_POST['cropDetails']; 
     31                $context = $_POST['context']; 
     32                $attachment_id = absint( $_POST['id'] ); 
     33                $cropped = wp_crop_image( 
     34                        $attachment_id, 
     35                        (int) $crop_details['x1'], 
     36                        (int) $crop_details['y1'], 
     37                        (int) $crop_details['width'], 
     38                        (int) $crop_details['height'], 
     39                        (int) $crop_details['dst_width'], 
     40                        (int) $crop_details['dst_height'] 
     41                ); 
     42 
     43                if ( ! $cropped || is_wp_error( $cropped ) ) { 
     44                        wp_send_json_error( array( 'message' => __( 'Image could not be processed. Please go back and try again.' ) ) ); 
     45                } 
     46 
     47                /** This filter is documented in wp-admin/custom-header.php */ 
     48                $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication 
     49                $object = $this->create_attachment_object( $cropped, $attachment_id, $context ); 
     50                unset( $object['ID'] ); 
     51 
     52                require_once( ABSPATH . '/wp-admin/includes/class-wp-site-icon.php' ); 
     53                $site_icon = new WP_Site_Icon(); 
     54 
     55                // Update the attachment. 
     56                /** 
     57                 * Action prior to inserting a cropped image from the Customizer. 
     58                 * 
     59                 * $context is the context argument of WP_Customize_Cropped_Image_Control. 
     60                 * 
     61                 * @since 4.3.0 
     62                 */ 
     63                do_action( 'customize_pre_insert_attachment_' . $context ); 
     64                $attachment_id = $this->insert_attachment( $object, $cropped, $context ); 
     65                /** 
     66                 * Action prior to inserting a cropped image from the Customizer. 
     67                 * 
     68                 * $context is the context argument of WP_Customize_Cropped_Image_Control. 
     69                 * 
     70                 * @since 4.3.0 
     71                 */ 
     72                do_action( 'customize_post_insert_attachment_' . $context ); 
     73 
     74                wp_send_json_success( wp_prepare_attachment_for_js( $attachment_id ) ); 
     75        } 
     76 
     77        /** 
     78         * Create an attachment 'object'. 
     79         * 
     80         * @param string $cropped              Cropped image URL. 
     81         * @param int    $parent_attachment_id Attachment ID of parent image. 
     82         * @param string $context              Context of the attachment object. 
     83         * 
     84         * @return array Attachment object. 
     85         */ 
     86        final public function create_attachment_object( $cropped, $parent_attachment_id, $context ) { 
     87                $parent = get_post( $parent_attachment_id ); 
     88                $parent_url = $parent->guid; 
     89                $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url ); 
     90                $size = @getimagesize( $cropped ); 
     91                $image_type = ( $size ) ? $size['mime'] : 'image/jpeg'; 
     92                $object = array( 
     93                        'ID' => $parent_attachment_id, 
     94                        'post_title' => basename($cropped), 
     95                        'post_content' => $url, 
     96                        'post_mime_type' => $image_type, 
     97                        'guid' => $url, 
     98                        'context' => $context, 
     99                ); 
     100                return $object; 
     101        } 
     102 
     103        /** 
     104         * Insert an attachment and its metadata. 
     105         * 
     106         * @param array  $object  Attachment object. 
     107         * @param string $cropped Cropped image URL. 
     108         * 
     109         * @return int Attachment ID. 
     110         */ 
     111        final public function insert_attachment( $object, $cropped, $context ) { 
     112                $attachment_id = wp_insert_attachment( $object, $cropped ); 
     113                $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped ); 
     114 
     115                /** 
     116                 * Filter the attachment metadata for an image cropped in the Customizer. 
     117                 * 
     118                 * @since 4.3.0 
     119                 * 
     120                 * @see wp_generate_attachment_metadata() 
     121                 * 
     122                 * @param array $metadata Attachment metadata. 
     123                 */ 
     124                $metadata = apply_filters( $context . '_customize_attachment_metadata', $metadata ); 
     125                wp_update_attachment_metadata( $attachment_id, $metadata ); 
     126 
     127                return $attachment_id; 
     128        } 
     129} 
  • src/wp-includes/class-wp-customize-control.php

     
    10011001} 
    10021002 
    10031003/** 
     1004 * Customize Cropped Image Control class. 
     1005 * 
     1006 * @since 4.3.0 
     1007 * 
     1008 * @see WP_Customize_Image_Control 
     1009 */ 
     1010class WP_Customize_Cropped_Image_Control extends WP_Customize_Image_Control { 
     1011        public $type = 'cropped_image'; 
     1012 
     1013        public $width  = 150; 
     1014        public $height = 150; 
     1015        public $flex_width  = false; 
     1016        public $flex_height = false; 
     1017        public $context = 'customize_crop_image'; 
     1018 
     1019        /** 
     1020         * Constructor. 
     1021         * 
     1022         * @since 4.3.0 
     1023         * @uses WP_Customize_Image_Control::__construct() 
     1024         * 
     1025         * @param WP_Customize_Manager $manager 
     1026         */ 
     1027        public function __construct( $manager, $id, $args ) { 
     1028                parent::__construct( $manager, $id, $args ); 
     1029        } 
     1030 
     1031        /** 
     1032         * @access public 
     1033         */ 
     1034        public function enqueue() { 
     1035                wp_enqueue_script( 'customize-views' ); 
     1036 
     1037                parent::enqueue(); 
     1038        } 
     1039 
     1040        /** 
     1041         * Refresh the parameters passed to the JavaScript via JSON. 
     1042         * 
     1043         * @since 4.3.0 
     1044         * @access public 
     1045         * @uses WP_Customize_Image_Control::to_json() 
     1046         * 
     1047         * @see WP_Customize_Control::to_json() 
     1048         */ 
     1049        public function to_json() { 
     1050                parent::to_json(); 
     1051 
     1052                $this->json['width']       = absint( $this->width ); 
     1053                $this->json['height']      = absint( $this->height ); 
     1054                $this->json['flex_width']  = absint( $this->flex_width ); 
     1055                $this->json['flex_height'] = absint( $this->flex_height ); 
     1056                $this->json['context']     = $this->context; 
     1057        } 
     1058 
     1059} 
     1060 
     1061/** 
     1062 * Customize Site Icon control class. 
     1063 * 
     1064 * Used only for custom functionality in JavaScript. 
     1065 * 
     1066 * @since 4.3.0 
     1067 * 
     1068 * @see WP_Customize_Cropped_Image_Control 
     1069 */ 
     1070class WP_Customize_Site_Icon_Control extends WP_Customize_Cropped_Image_Control { 
     1071        public $type = 'site_icon'; 
     1072} 
     1073 
     1074/** 
    10041075 * Customize Header Image Control class. 
    10051076 * 
    10061077 * @since 3.4.0 
  • src/wp-includes/class-wp-customize-manager.php

     
    112112                require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' ); 
    113113                require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); 
    114114                require_once( ABSPATH . WPINC . '/class-wp-customize-nav-menus.php' ); 
     115                require_once( ABSPATH . WPINC . '/class-wp-customize-ajax-actions.php' ); 
    115116 
    116117                $this->widgets = new WP_Customize_Widgets( $this ); 
    117118                $this->nav_menus = new WP_Customize_Nav_Menus( $this ); 
     119                $this->ajax_actions = new WP_Customize_Ajax_Actions(); 
    118120 
    119121                add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) ); 
    120122 
     
    12781280                $this->register_control_type( 'WP_Customize_Upload_Control' ); 
    12791281                $this->register_control_type( 'WP_Customize_Image_Control' ); 
    12801282                $this->register_control_type( 'WP_Customize_Background_Image_Control' ); 
     1283                $this->register_control_type( 'WP_Customize_Cropped_Image_Control' ); 
     1284                $this->register_control_type( 'WP_Customize_Site_Icon_Control' ); 
    12811285                $this->register_control_type( 'WP_Customize_Theme_Control' ); 
    12821286 
    12831287                /* Themes */ 
     
    13241328                        ) ) ); 
    13251329                } 
    13261330 
    1327                 /* Site Title & Tagline */ 
     1331                /* Site Identity */ 
    13281332 
    13291333                $this->add_section( 'title_tagline', array( 
    1330                         'title'    => __( 'Site Title & Tagline' ), 
     1334                        'title'    => __( 'Site Identity' ), 
    13311335                        'priority' => 20, 
    13321336                ) ); 
    13331337 
     
    13531357                        'section'    => 'title_tagline', 
    13541358                ) ); 
    13551359 
     1360                $icon = wp_get_attachment_image_src( absint( get_option( 'site_icon', 0 ) ), 'full' )[0]; 
     1361                $this->add_setting( 'site_icon', array( 
     1362                        'default'    => ( $icon ) ? $icon[0] : '', 
     1363                        'type'       => 'option', 
     1364                        'capability' => 'manage_options', 
     1365                        'transport'  => 'postMessage', // Previewed with JS in the Customizer controls window. 
     1366                ) ); 
     1367 
     1368                $this->add_control( new WP_Customize_Site_Icon_Control( $this, 'site_icon', array( 
     1369                        'label'       => __( 'Site Icon' ), 
     1370                        'description' => __( 'The site icon is used as the browser and device icon for your site. Your theme may also display the site icon. Icons must be square, and at least 512px wide and tall.' ), 
     1371                        'section'     => 'title_tagline', 
     1372                        'priority'    => 60, 
     1373                        'context'     => 'site-icon', 
     1374                        'height'      => 512, 
     1375                        'width'       => 512, 
     1376                ) ) ); 
     1377 
    13561378                /* Colors */ 
    13571379 
    13581380                $this->add_section( 'colors', array( 
     
    13751397                        'label'    => __( 'Display Header Text' ), 
    13761398                        'section'  => 'title_tagline', 
    13771399                        'type'     => 'checkbox', 
     1400                        'priority' => 40, 
    13781401                ) ); 
    13791402 
    13801403                $this->add_control( new WP_Customize_Color_Control( $this, 'header_textcolor', array( 
  • src/wp-includes/default-filters.php

     
    224224add_action( 'wp_head',             'rel_canonical'                          ); 
    225225add_action( 'wp_head',             'wp_shortlink_wp_head',            10, 0 ); 
    226226add_action( 'wp_head',             'wp_site_icon',                    99    ); 
     227add_action( 'customize_controls_print_styles', 'wp_site_icon',        99    ); 
    227228add_action( 'wp_footer',           'wp_print_footer_scripts',         20    ); 
    228229add_action( 'template_redirect',   'wp_shortlink_header',             11, 0 ); 
    229230add_action( 'wp_print_footer_scripts', '_wp_footer_scripts'                 ); 
  • src/wp-includes/general-template.php

     
    24502450        } 
    24512451 
    24522452        $meta_tags = array( 
    2453                 sprintf( '<link rel="icon" href="%s" sizes="32x32" />', esc_url( get_site_icon_url( null, 32 ) ) ), 
     2453                sprintf( '<link rel="icon" href="%s" sizes="32x32" id="wp-site-icon-favicon" />', esc_url( get_site_icon_url( null, 32 ) ) ), 
    24542454                sprintf( '<link rel="apple-touch-icon-precomposed" href="%s">', esc_url( get_site_icon_url( null, 180 ) ) ), 
    24552455                sprintf( '<meta name="msapplication-TileImage" content="%s">', esc_url( get_site_icon_url( null, 270 ) ) ), 
    24562456        ); 
  • src/wp-includes/js/customize-views.js

     
    33        if ( ! wp || ! wp.customize ) { return; } 
    44        var api = wp.customize; 
    55 
     6        /** 
     7         * Use a custom ajax action for cropped image controls 
     8         */ 
     9        wp.media.controller.customizeImageCropper = wp.media.controller.Cropper.extend( { 
     10                doCrop: function( attachment ) { 
     11                        var cropDetails = attachment.get( 'cropDetails' ), 
     12                            control = this.get( 'control' ); 
     13                        cropDetails.dst_height = control.params.height; 
     14                        cropDetails.dst_width = control.params.width; 
     15                        return wp.ajax.post( 'customize-image-crop', { 
     16                                wp_customize: 'on', 
     17                                nonce: attachment.get( 'nonces' ).edit, 
     18                                id: attachment.get( 'id' ), 
     19                                context: control.params.context, 
     20                                cropDetails: cropDetails 
     21                        } ); 
     22                } 
     23        } ); 
    624 
    725        /** 
    826         * wp.customize.HeaderTool.CurrentView