Introducing Site Icon, favicon management for WordPress.

This v1 marries Jetpack's Site Icon module with the Media Modal, reusing code
from the Custom Header admin. For now, the core-provided icons will be limited
to a favicon, an iOS app icon, and a Windows tile icon, leaving `.ico` support
and additional icons to plugins to add.

Props obenland, tyxla, flixos90, jancbeck, markjaquith, scruffian.
See #16434.


Built from https://develop.svn.wordpress.org/trunk@32994


git-svn-id: http://core.svn.wordpress.org/trunk@32965 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Konstantin Obenland 2015-06-29 12:58:25 +00:00
parent 2a35e075d7
commit c56a8ae0f7
22 changed files with 1043 additions and 6 deletions

View File

@ -0,0 +1,57 @@
/*------------------------------------------------------------------------------
28.0 - Site Icon
------------------------------------------------------------------------------*/
.site-icon-image {
float: right;
margin: 0 0 0 20px;
}
.site-icon-content {
overflow: hidden;
padding: 10px;
position: relative;
}
.site-icon-crop-shell {
max-width: 720px;
}
.site-icon-crop-preview-shell {
float: left;
overflow: hidden;
}
.site-icon-crop-preview-shell h3 {
margin-top: 0;
}
.site-icon-crop-favicon-preview-shell {
margin-bottom: 20px;
position: relative;
}
.site-icon-crop-preview-favicon,
.site-icon-browser-title {
height: 16px;
right: 88px;
overflow: hidden;
position: absolute;
top: 16px;
}
.site-icon-crop-preview-favicon {
width: 16px;
}
.site-icon-browser-title {
right: 109px;
}
.site-icon-crop-preview-homeicon {
-webkit-border-radius: 16px;
border-radius: 16px;
height: 64px;
overflow: hidden;
width: 64px;
}

View File

@ -0,0 +1,57 @@
/*------------------------------------------------------------------------------
28.0 - Site Icon
------------------------------------------------------------------------------*/
.site-icon-image {
float: left;
margin: 0 20px 0 0;
}
.site-icon-content {
overflow: hidden;
padding: 10px;
position: relative;
}
.site-icon-crop-shell {
max-width: 720px;
}
.site-icon-crop-preview-shell {
float: right;
overflow: hidden;
}
.site-icon-crop-preview-shell h3 {
margin-top: 0;
}
.site-icon-crop-favicon-preview-shell {
margin-bottom: 20px;
position: relative;
}
.site-icon-crop-preview-favicon,
.site-icon-browser-title {
height: 16px;
left: 88px;
overflow: hidden;
position: absolute;
top: 16px;
}
.site-icon-crop-preview-favicon {
width: 16px;
}
.site-icon-browser-title {
left: 109px;
}
.site-icon-crop-preview-homeicon {
-webkit-border-radius: 16px;
border-radius: 16px;
height: 64px;
overflow: hidden;
width: 64px;
}

View File

@ -10,4 +10,5 @@
@import url(about-rtl.css);
@import url(nav-menus-rtl.css);
@import url(widgets-rtl.css);
@import url(site-icon-rtl.css);
@import url(l10n-rtl.css);

File diff suppressed because one or more lines are too long

View File

@ -10,4 +10,5 @@
@import url(about.css);
@import url(nav-menus.css);
@import url(widgets.css);
@import url(site-icon.css);
@import url(l10n.css);

File diff suppressed because one or more lines are too long

BIN
wp-admin/images/browser.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -64,6 +64,9 @@ require_once(ABSPATH . 'wp-admin/includes/theme.php');
/** WordPress User Administration API */
require_once(ABSPATH . 'wp-admin/includes/user.php');
/** WordPress Site Icon API */
require_once(ABSPATH . 'wp-admin/includes/site-icon.php');
/** WordPress Update Administration API */
require_once(ABSPATH . 'wp-admin/includes/update.php');

View File

@ -0,0 +1,636 @@
<?php
/**
* Class WP_Site_Icon.
*
* @since 4.3.0
*/
class WP_Site_Icon {
/**
* The minimum size of the site icon.
*
* @since 4.3.0
*
* @var int
*/
public $min_size = 512;
/**
* The size to which to crop the image so that we can display it in the UI nicely.
*
* @since 4.3.0
*
* @var int
*/
public $page_crop = 512;
/**
*
* @since 4.3.0
*
* @var array
*/
public $site_icon_sizes = array(
/**
* Square, medium sized tiles for IE11+.
*
* @link https://msdn.microsoft.com/library/dn455106(v=vs.85).aspx
*/
270,
/**
* App icons up to iPhone 6 Plus.
*
* @link https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html
*/
180,
// Our regular Favicon.
32,
);
/**
* Register our actions and filters.
*
* @since 4.3.0
*/
public function __construct() {
// Add the favicon to the backend.
add_action( 'admin_head', 'wp_site_icon' );
add_action( 'admin_menu', array( $this, 'admin_menu_upload_site_icon' ) );
add_action( 'admin_action_set_site_icon', array( $this, 'set_site_icon' ) );
add_action( 'admin_action_remove_site_icon', array( $this, 'remove_site_icon' ) );
add_action( 'delete_attachment', array( $this, 'delete_attachment_data' ), 10, 1 );
add_filter( 'get_post_metadata', array( $this, 'get_post_metadata' ), 10, 4 );
}
/**
* Add a hidden upload page.
*
* There is no need to access it directly.
*
* @since 4.3.0
*/
public function admin_menu_upload_site_icon() {
$hook = add_submenu_page( null, __( 'Site Icon' ), __( 'Site Icon' ), 'manage_options', 'site-icon', array( $this, 'upload_site_icon_page' ) );
add_action( "load-$hook", array( $this, 'add_upload_settings' ) );
add_action( "admin_print_scripts-$hook", array( $this, 'enqueue_scripts' ) );
}
/**
* Add scripts to admin settings pages.
*
* @since 4.3.0
*/
public function enqueue_scripts() {
wp_enqueue_style( 'jcrop' );
wp_enqueue_script( 'site-icon-crop' );
}
/**
* Load on when the admin is initialized.
*
* @since 4.3.0
*/
public function add_upload_settings() {
add_settings_section( 'site-icon-upload', false, false, 'site-icon-upload' );
add_settings_field( 'site-icon-upload', __( 'Upload Image' ), array( $this, 'upload_field' ), 'site-icon-upload', 'site-icon-upload', array(
'label_for' => 'site-icon-upload',
) );
}
/**
* Removes site icon.
*
* @since 4.3.0
*/
public function remove_site_icon() {
check_admin_referer( 'remove-site-icon' );
$this->delete_site_icon();
add_settings_error( 'site-icon', 'icon-removed', __( 'Site Icon removed.' ), 'updated' );
}
/**
* Uploading a site_icon is a 3 step process
*
* 1. Select the file to upload
* 2. Crop the file
* 3. Confirmation
*
* @since 4.3.0
*/
public function upload_site_icon_page() {
$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'select_site_icon';
switch ( $action ) {
case 'select_site_icon':
$this->select_page();
break;
case 'crop_site_icon':
$this->crop_page();
break;
default:
wp_safe_redirect( admin_url( 'options-general.php#site-icon' ) );
exit;
}
}
/**
* Displays the site_icon form to upload the image.
*
* @since 4.3.0
*/
public function select_page() {
?>
<div class="wrap">
<h2><?php _e( 'Add Site Icon' ); ?></h2>
<?php settings_errors( 'site-icon' ); ?>
<?php do_settings_sections( 'site-icon-upload' ); ?>
</div>
<?php
}
/**
* Settings field for file upload.
*
* @since 4.3.0
*/
public function upload_field() {
wp_enqueue_media();
wp_enqueue_script( 'site-icon' );
wp_dequeue_script( 'site-icon-crop' );
$update_url = esc_url( add_query_arg( array(
'page' => 'site-icon',
'action' => 'crop_site_icon',
), wp_nonce_url( admin_url( 'options-general.php' ), 'crop-site-icon' ) ) );
?>
<p class="hide-if-no-js">
<label class="screen-reader-text" for="choose-from-library-link"><?php _e( 'Choose an image from your media library:' ); ?></label>
<button type="button" id="choose-from-library-link" class="button" data-update-link="<?php echo esc_attr( $update_url ); ?>" data-choose="<?php esc_attr_e( 'Choose a Site Icon' ); ?>" data-update="<?php esc_attr_e( 'Set as Site Icon' ); ?>"><?php _e( 'Choose Image' ); ?></button>
</p>
<form class="hide-if-js" action="<?php echo esc_url( admin_url( 'options-general.php?page=site-icon' ) ); ?>" method="post" enctype="multipart/form-data">
<input name="action" value="crop_site_icon" type="hidden" />
<input name="site-icon" type="file" />
<input name="submit" value="<?php esc_attr_e( 'Upload Image' ); ?>" type="submit" class="button button-primary" />
<p class="description">
<?php printf( __( 'The image is recommended to be a square image of at least %spx in both width and height.' ), "<strong>$this->min_size</strong>" ); ?>
</p>
<?php wp_nonce_field( 'crop-site-icon' ); ?>
</form>
<?php
}
/**
* Crop a the image admin view.
*
* @since 4.3.0
*/
public function crop_page() {
check_admin_referer( 'crop-site-icon' );
if ( isset( $_GET['file'] ) ) {
$attachment_id = absint( $_GET['file'] );
$file = get_attached_file( $attachment_id, true );
$url = wp_get_attachment_image_src( $attachment_id, 'full' );
$url = $url[0];
} else {
$upload = $this->handle_upload();
$attachment_id = $upload['attachment_id'];
$file = $upload['file'];
$url = $upload['url'];
}
$image_size = getimagesize( $file );
if ( $image_size[0] < $this->min_size ) {
add_settings_error( 'site-icon', 'too-small', sprintf( __( 'The selected image is smaller than %upx in width.' ), $this->min_size ) );
// back to step one
$this->select_page();
return;
}
if ( $image_size[1] < $this->min_size ) {
add_settings_error( 'site-icon', 'too-small', sprintf( __( 'The selected image is smaller than %upx in height.' ), $this->min_size ) );
// Back to step one.
$this->select_page();
return;
}
// Let's resize the image so that the user can easier crop a image that in the admin view.
$crop_height = absint( $this->page_crop * $image_size[1] / $image_size[0] );
$cropped = wp_crop_image( $attachment_id, 0, 0, 0, 0, $this->page_crop, $crop_height );
if ( ! $cropped || is_wp_error( $cropped ) ) {
wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
}
$cropped_size = getimagesize( $cropped );
// set default values (in case of no JS)
$crop_ratio = $image_size[0] / $cropped_size[0];
if ( $cropped_size[0] < $cropped_size[1] ) {
$crop_x = 0;
$crop_y = absint( ( $cropped_size[1] - $cropped_size[0] ) / 2 );
$crop_size = $cropped_size[0];
} elseif ( $cropped_size[0] > $cropped_size[1] ) {
$crop_x = absint( ( $cropped_size[0] - $cropped_size[1] ) / 2 );
$crop_y = 0;
$crop_size = $cropped_size[1];
} else {
$crop_x = 0;
$crop_y = 0;
$crop_size = $cropped_size[0];
}
wp_delete_file( $cropped );
wp_localize_script( 'site-icon-crop', 'wpSiteIconCropData', $this->initial_crop_data( $crop_ratio, $cropped_size ) );
?>
<div class="wrap">
<h2 class="site-icon-title"><?php _e( 'Site Icon' ); ?></h2>
<?php settings_errors( 'site-icon' ); ?>
<div class="site-icon-crop-shell">
<form action="options-general.php" method="post" enctype="multipart/form-data">
<p class="hide-if-no-js description"><?php _e('Choose the part of the image you want to use as your site icon.'); ?></p>
<p class="hide-if-js description"><strong><?php _e( 'You need Javascript to choose a part of the image.'); ?></strong></p>
<div class="site-icon-crop-preview-shell hide-if-no-js">
<h3><?php _e( 'Preview' ); ?></h3>
<strong><?php _e( 'As your favicon' ); ?></strong>
<div class="site-icon-crop-favicon-preview-shell">
<img src="images/browser.png" class="site-icon-browser-preview" width="182" height="" alt="<?php esc_attr_e( 'Browser Chrome' ); ?>"/>
<div class="site-icon-crop-preview-favicon">
<img src="<?php echo esc_url( $url ); ?>" id="preview-favicon" alt="<?php esc_attr_e( 'Preview Favicon' ); ?>"/>
</div>
<span class="site-icon-browser-title"><?php bloginfo( 'name' ); ?></span>
</div>
<strong><?php _e( 'As a mobile icon' ); ?></strong>
<div class="site-icon-crop-preview-homeicon">
<img src="<?php echo esc_url( $url ); ?>" id="preview-homeicon" alt="<?php esc_attr_e( 'Preview Home Icon' ); ?>"/>
</div>
</div>
<img src="<?php echo esc_url( $url ); ?>" id="crop-image" class="site-icon-crop-image" width="<?php echo esc_attr( $cropped_size[0] ); ?>" height="<?php echo esc_attr( $cropped_size[1] ); ?>" alt="<?php esc_attr_e( 'Image to be cropped' ); ?>"/>
<input type="hidden" id="crop-x" name="crop-x" value="<?php echo esc_attr( $crop_x ); ?>" />
<input type="hidden" id="crop-y" name="crop-y" value="<?php echo esc_attr( $crop_y ); ?>" />
<input type="hidden" id="crop-width" name="crop-w" value="<?php echo esc_attr( $crop_size ); ?>" />
<input type="hidden" id="crop-height" name="crop-h" value="<?php echo esc_attr( $crop_size ); ?>" />
<input type="hidden" name="action" value="set_site_icon" />
<input type="hidden" name="attachment_id" value="<?php echo esc_attr( $attachment_id ); ?>" />
<input type="hidden" name="crop_ratio" value="<?php echo esc_attr( $crop_ratio ); ?>" />
<?php if ( empty( $_POST ) && isset( $_GET['file'] ) ) : ?>
<input type="hidden" name="create-new-attachment" value="true" />
<?php endif; ?>
<?php wp_nonce_field( 'set-site-icon' ); ?>
<p class="submit">
<?php submit_button( __( 'Crop and Publish' ), 'primary hide-if-no-js', 'submit', false ); ?>
<?php submit_button( __( 'Publish' ), 'primary hide-if-js', 'submit', false ); ?>
<a class="button secondary" href="options-general.php"><?php _e( 'Cancel' ); ?></a>
</p>
</form>
</div>
</div>
<?php
}
/**
* Saves a new Site Icon.
*
* @since 4.3.0
*/
public function set_site_icon() {
check_admin_referer( 'set-site-icon' );
// Delete any existing site icon images.
$this->delete_site_icon();
$attachment_id = absint( $_POST['attachment_id'] );
// TODO
if ( empty( $_POST['skip-cropping'] ) ) {
$crop_ratio = (float) $_POST['crop_ratio'];
$crop_data = $this->convert_coordinates_from_resized_to_full( $_POST['crop-x'], $_POST['crop-y'], $_POST['crop-w'], $_POST['crop-h'], $crop_ratio );
$cropped = wp_crop_image( $attachment_id, $crop_data['crop_x'], $crop_data['crop_y'], $crop_data['crop_width'], $crop_data['crop_height'], $this->min_size, $this->min_size );
} elseif ( ! empty( $_POST['create-new-attachment'] ) ) {
$cropped = _copy_image_file( $attachment_id );
} else {
$cropped = get_attached_file( $attachment_id );
}
if ( ! $cropped || is_wp_error( $cropped ) ) {
wp_die( __( 'Image could not be processed. Please go back and try again.' ), __( 'Image Processing Error' ) );
}
$object = $this->create_attachment_object( $cropped, $attachment_id );
if ( ! empty( $_POST['create-new-attachment'] ) ) {
unset( $object['ID'] );
}
// Update the attachment
add_filter( 'intermediate_image_sizes_advanced', array( $this, 'additional_sizes' ) );
$attachment_id = $this->insert_attachment( $object, $cropped );
remove_filter( 'intermediate_image_sizes_advanced', array( $this, 'additional_sizes' ) );
// Save the site_icon data into option
update_option( 'site_icon', $attachment_id );
add_settings_error( 'site-icon', 'icon-updated', __( 'Site Icon updated.' ), 'updated' );
}
/**
* This function is used to pass data to the localize script
* so that we can center the cropper and also set the minimum
* cropper.
*
* @since 4.3.0
*
* @param float $ratio
* @param array $cropped_size
* @return array
*/
public function initial_crop_data( $ratio, $cropped_size ) {
$init_x = $init_y = $init_size = 0;
$min_crop_size = ( $this->min_size / $ratio );
$resized_width = $cropped_size[0];
$resized_height = $cropped_size[1];
// Landscape format ( width > height )
if ( $resized_width > $resized_height ) {
$init_x = ( $this->page_crop - $resized_height ) / 2;
$init_size = $resized_height;
}
// Portrait format ( height > width )
if ( $resized_width < $resized_height ) {
$init_y = ( $this->page_crop - $resized_width ) / 2;
$init_size = $resized_height;
}
// Square height == width
if ( $resized_width == $resized_height ) {
$init_size = $resized_height;
}
return array(
'init_x' => $init_x,
'init_y' => $init_y,
'init_size' => $init_size,
'min_size' => $min_crop_size,
);
}
/**
* Converts the coordinates from the downsized image to the original image for accurate cropping.
*
* @since 4.3.0
*
* @param int $crop_x
* @param int $crop_y
* @param int $crop_width
* @param int $crop_height
* @param float $ratio
* @return array
*/
public function convert_coordinates_from_resized_to_full( $crop_x, $crop_y, $crop_width, $crop_height, $ratio ) {
return array(
'crop_x' => floor( $crop_x * $ratio ),
'crop_y' => floor( $crop_y * $ratio ),
'crop_width' => floor( $crop_width * $ratio ),
'crop_height' => floor( $crop_height * $ratio ),
);
}
/**
* Upload the file to be cropped in the second step.
*
* @since 4.3.0
*/
public function handle_upload() {
$uploaded_file = $_FILES['site-icon'];
$file_type = wp_check_filetype_and_ext( $uploaded_file['tmp_name'], $uploaded_file['name'] );
if ( ! wp_match_mime_types( 'image', $file_type['type'] ) ) {
wp_die( __( 'The uploaded file is not a valid image. Please try again.' ) );
}
$file = wp_handle_upload( $uploaded_file, array( 'test_form' => false ) );
if ( isset( $file['error'] ) ) {
wp_die( $file['error'], __( 'Image Upload Error' ) );
}
$url = $file['url'];
$type = $file['type'];
$file = $file['file'];
$filename = basename( $file );
// Construct the object array
$object = array(
'post_title' => $filename,
'post_content' => $url,
'post_mime_type' => $type,
'guid' => $url,
'context' => 'site-icon',
);
// Save the data
$attachment_id = wp_insert_attachment( $object, $file );
return compact( 'attachment_id', 'file', 'filename', 'url', 'type' );
}
/**
* Create an attachment 'object'.
*
* @since 4.3.0
*
* @param string $cropped Cropped image URL.
* @param int $parent_attachment_id Attachment ID of parent image.
* @return array Attachment object.
*/
public function create_attachment_object( $cropped, $parent_attachment_id ) {
$parent = get_post( $parent_attachment_id );
$parent_url = $parent->guid;
$url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
$size = @getimagesize( $cropped );
$image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
$object = array(
'ID' => $parent_attachment_id,
'post_title' => basename( $cropped ),
'post_content' => $url,
'post_mime_type' => $image_type,
'guid' => $url,
'context' => 'site-icon'
);
return $object;
}
/**
* Insert an attachment and its metadata.
*
* @since 4.3.0
*
* @param array $object Attachment object.
* @param string $cropped Cropped image URL.
* @return int Attachment ID.
*/
public function insert_attachment( $object, $cropped ) {
$attachment_id = wp_insert_attachment( $object, $cropped );
$metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );
/**
* Filter the site icon attachment metadata.
*
* @since 4.3.0
*
* @see wp_generate_attachment_metadata()
*
* @param array $metadata Attachment metadata.
*/
$metadata = apply_filters( 'site_icon_attachment_metadata', $metadata );
wp_update_attachment_metadata( $attachment_id, $metadata );
return $attachment_id;
}
/**
* Add additional sizes to be made when creating the site_icon images.
*
* @since 4.3.0
*
* @param array $sizes
* @return array
*/
public function additional_sizes( $sizes = array() ) {
$only_crop_sizes = array();
/**
* Filter the different dimensions that a site icon is saved in.
*
* @since 4.3.0
*
* @param array $site_icon_sizes Sizes available for the Site Icon.
*/
$this->site_icon_sizes = apply_filters( 'site_icon_image_sizes', $this->site_icon_sizes );
// Use a natural sort of numbers.
natsort( $this->site_icon_sizes );
$this->site_icon_sizes = array_reverse( $this->site_icon_sizes );
// ensure that we only resize the image into
foreach ( $sizes as $name => $size_array ) {
if ( $size_array['crop'] ) {
$only_crop_sizes[ $name ] = $size_array;
}
}
foreach ( $this->site_icon_sizes as $size ) {
if ( $size < $this->min_size ) {
$only_crop_sizes[ 'site_icon-' . $size ] = array(
'width ' => $size,
'height' => $size,
'crop' => true,
);
}
}
return $only_crop_sizes;
}
/**
* Add Site Icon sizes to the array of image sizes on demand.
*
* @since 4.3.0
*
* @param array $sizes
* @return array
*/
public function intermediate_image_sizes( $sizes = array() ) {
/** This filter is documented in wp-admin/includes/site-icon.php */
$this->site_icon_sizes = apply_filters( 'site_icon_image_sizes', $this->site_icon_sizes );
foreach ( $this->site_icon_sizes as $size ) {
$sizes[] = 'site_icon-' . $size;
}
return $sizes;
}
/**
* Deletes all additional image sizes, used for site icons.
*
* @since 4.3.0
*
* @return bool
*/
public function delete_site_icon() {
// We add the filter to make sure that we also delete all the added images.
add_filter( 'intermediate_image_sizes', array( $this, 'intermediate_image_sizes' ) );
wp_delete_attachment( get_option( 'site_icon' ), true );
remove_filter( 'intermediate_image_sizes', array( $this, 'intermediate_image_sizes' ) );
return delete_option( 'site_icon' );
}
/**
* Deletes the Site Icon when the image file is deleted.
*
* @since 4.3.0
*
* @param int $post_id Attachment ID.
*/
public function delete_attachment_data( $post_id ) {
$site_icon_id = get_option( 'site_icon' );
if ( $site_icon_id && $post_id == $site_icon_id ) {
delete_option( 'site_icon' );
}
}
/**
* Adds custom image sizes when meta data for an image is requested, that happens to be used as Site Icon.
*
* @since 4.3.0
*
* @param null|array|string $value The value get_metadata() should
* return - a single metadata value,
* or an array of values.
* @param int $post_id Post ID.
* @param string $meta_key Meta key.
* @param string|array $single Meta value, or an array of values.
* @return array|null|string
*/
public function get_post_metadata( $value, $post_id, $meta_key, $single ) {
$site_icon_id = get_option( 'site_icon' );
if ( $post_id == $site_icon_id && '_wp_attachment_backup_sizes' == $meta_key && $single ) {
add_filter( 'intermediate_image_sizes', array( $this, 'intermediate_image_sizes' ) );
}
return $value;
}
}
$GLOBALS['wp_site_icon'] = new WP_Site_Icon;

View File

@ -1778,13 +1778,17 @@ function _media_states( $post ) {
$media_states[] = __( 'Background Image' );
}
if ( $post->ID == get_option( 'site_icon' ) ) {
$media_states[] = __( 'Site Icon' );
}
/**
* Filter the default media display states for items in the Media list table.
*
* @since 3.2.0
*
* @param array $media_states An array of media states. Default 'Header Image',
* 'Background Image'.
* 'Background Image', 'Site Icon'.
*/
$media_states = apply_filters( 'display_media_states', $media_states );

View File

@ -0,0 +1,54 @@
/* global wpSiteIconCropData, jQuery */
(function($) {
var jcrop_api = {},
siteIconCrop = {
updateCoords : function ( coords ) {
$( '#crop-x' ).val( coords.x );
$( '#crop-y' ).val( coords.y );
$( '#crop-width' ).val( coords.w );
$( '#crop-height' ).val( coords.h );
siteIconCrop.showPreview( coords );
},
showPreview : function( coords ){
var rx = 64 / coords.w,
ry = 64 / coords.h,
preview_rx = 16 / coords.w,
preview_ry = 16 / coords.h,
$cropImage = $( '#crop-image' ),
$homeIcon = $( '#preview-homeicon' ),
$favicon = $( '#preview-favicon' );
$homeIcon.css({
width: Math.round(rx * $cropImage.attr( 'width' ) ) + 'px',
height: Math.round(ry * $cropImage.attr( 'height' ) ) + 'px',
marginLeft: '-' + Math.round(rx * coords.x) + 'px',
marginTop: '-' + Math.round(ry * coords.y) + 'px'
});
$favicon.css({
width: Math.round( preview_rx * $cropImage.attr( 'width' ) ) + 'px',
height: Math.round( preview_ry * $cropImage.attr( 'height' ) ) + 'px',
marginLeft: '-' + Math.round( preview_rx * coords.x ) + 'px',
marginTop: '-' + Math.floor( preview_ry* coords.y ) + 'px'
});
},
ready: function() {
jcrop_api = $.Jcrop( '#crop-image' );
jcrop_api.setOptions({
aspectRatio: 1,
onSelect: siteIconCrop.updateCoords,
onChange: siteIconCrop.updateCoords,
minSize: [ wpSiteIconCropData.min_size, wpSiteIconCropData.min_size ]
});
jcrop_api.animateTo([wpSiteIconCropData.init_x, wpSiteIconCropData.init_y, wpSiteIconCropData.init_size, wpSiteIconCropData.init_size]);
}
};
siteIconCrop.ready();
})(jQuery);

1
wp-admin/js/site-icon-crop.min.js vendored Normal file
View File

@ -0,0 +1 @@
!function(a){var b={},c={updateCoords:function(b){a("#crop-x").val(b.x),a("#crop-y").val(b.y),a("#crop-width").val(b.w),a("#crop-height").val(b.h),c.showPreview(b)},showPreview:function(b){var c=64/b.w,d=64/b.h,e=16/b.w,f=16/b.h,g=a("#crop-image"),h=a("#preview-homeicon"),i=a("#preview-favicon");h.css({width:Math.round(c*g.attr("width"))+"px",height:Math.round(d*g.attr("height"))+"px",marginLeft:"-"+Math.round(c*b.x)+"px",marginTop:"-"+Math.round(d*b.y)+"px"}),i.css({width:Math.round(e*g.attr("width"))+"px",height:Math.round(f*g.attr("height"))+"px",marginLeft:"-"+Math.round(e*b.x)+"px",marginTop:"-"+Math.floor(f*b.y)+"px"})},ready:function(){b=a.Jcrop("#crop-image"),b.setOptions({aspectRatio:1,onSelect:c.updateCoords,onChange:c.updateCoords,minSize:[wpSiteIconCropData.min_size,wpSiteIconCropData.min_size]}),b.animateTo([wpSiteIconCropData.init_x,wpSiteIconCropData.init_y,wpSiteIconCropData.init_size,wpSiteIconCropData.init_size])}};c.ready()}(jQuery);

49
wp-admin/js/site-icon.js Normal file
View File

@ -0,0 +1,49 @@
(function($) {
var frame;
$( function() {
// Build the choose from library frame.
$( '#choose-from-library-link' ).click( function( event ) {
var $el = $(this);
event.preventDefault();
// If the media frame already exists, reopen it.
if ( frame ) {
frame.open();
return;
}
// Create the media frame.
frame = wp.media.frames.customHeader = wp.media({
// Set the title of the modal.
title: $el.data('choose'),
// Tell the modal to show only images.
library: {
type: 'image'
},
// Customize the submit button.
button: {
// Set the text of the button.
text: $el.data('update'),
// Tell the button not to close the modal, since we're
// going to refresh the page when the image is selected.
close: false
}
});
// When an image is selected, run a callback.
frame.on( 'select', function() {
// Grab the selected attachment.
var attachment = frame.state().get('selection').first(),
link = $el.data('updateLink');
// Tell the browser to navigate to the crop step.
window.location = link + '&file=' + attachment.id;
});
frame.open();
});
});
}(jQuery));

1
wp-admin/js/site-icon.min.js vendored Normal file
View File

@ -0,0 +1 @@
!function(a){var b;a(function(){a("#choose-from-library-link").click(function(c){var d=a(this);return c.preventDefault(),b?void b.open():(b=wp.media.frames.customHeader=wp.media({title:d.data("choose"),library:{type:"image"},button:{text:d.data("update"),close:!1}}),b.on("select",function(){var a=b.state().get("selection").first(),c=d.data("updateLink");window.location=c+"&file="+a.id}),void b.open())})})}(jQuery);

View File

@ -124,6 +124,48 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
<td><input name="blogdescription" type="text" id="blogdescription" aria-describedby="tagline-description" value="<?php form_option('blogdescription'); ?>" class="regular-text" />
<p class="description" id="tagline-description"><?php _e( 'In a few words, explain what this site is about.' ) ?></p></td>
</tr>
<tr>
<th scope="row"><label for="site_icon"><?php _e( 'Site Icon' ); ?></label></th>
<td>
<?php
$upload_url = admin_url( 'options-general.php?page=site-icon' );
$update_url = esc_url( add_query_arg( array(
'page' => 'site-icon',
'action' => 'crop_site_icon',
), wp_nonce_url( admin_url( 'options-general.php' ), 'crop-site-icon' ) ) );
wp_enqueue_media();
wp_enqueue_script( 'site-icon' );
if ( has_site_icon() ) :
$remove_url = add_query_arg( array(
'action' => 'remove_site_icon',
), wp_nonce_url( admin_url( 'options-general.php' ), 'remove-site-icon' ) );
?>
<img class="avatar avatar-150" src="<?php site_icon_url( null, 150 ); ?>" height="150" width="150" />
<p class="hide-if-no-js">
<label class="screen-reader-text" for="choose-from-library-link"><?php _e( 'Choose an image from your media library:' ); ?></label>
<button type="button" id="choose-from-library-link" class="button" data-update-link="<?php echo esc_attr( $update_url ); ?>" data-choose="<?php esc_attr_e( 'Choose a Site Icon' ); ?>" data-update="<?php esc_attr_e( 'Set as Site Icon' ); ?>"><?php _e( 'Update Site Icon' ); ?></button>
<a href="<?php echo esc_url( $remove_url ); ?>"><?php _e( 'Remove Site Icon' ); ?></a>
</p>
<p class="hide-if-js">
<a href="<?php echo esc_url( $upload_url ); ?>" class="button"><?php _e( 'Update Site Icon' ); ?></a>
<a href="<?php echo esc_url( $remove_url ); ?>"><?php _e( 'Remove Site Icon' ); ?></a>
</p>
<?php else : ?>
<p class="hide-if-no-js">
<label class="screen-reader-text" for="choose-from-library-link"><?php _e( 'Choose an image from your media library:' ); ?></label>
<button type="button" id="choose-from-library-link" class="button" data-update-link="<?php echo esc_attr( $update_url ); ?>" data-choose="<?php esc_attr_e( 'Choose a Site Icon' ); ?>" data-update="<?php esc_attr_e( 'Set as Site Icon' ); ?>"><?php _e( 'Choose Image' ); ?></button>
</p>
<a class="button hide-if-js" href="<?php echo esc_url( $upload_url ); ?>"><?php _e( 'Add a Site Icon' ); ?></a>
<?php endif; ?>
<p class="description"><?php _e( 'Site Icon creates a favicon and app icons for your site.' ) ?></p>
</td>
</tr>
<?php if ( !is_multisite() ) { ?>
<tr>
<th scope="row"><label for="siteurl"><?php _e('WordPress Address (URL)') ?></label></th>

View File

@ -72,7 +72,7 @@ if ( is_multisite() && !is_super_admin() && 'update' != $action )
wp_die( __( 'Cheatin&#8217; uh?' ), 403 );
$whitelist_options = array(
'general' => array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG' ),
'general' => array( 'blogname', 'blogdescription', 'site_icon', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG' ),
'discussion' => array( 'default_pingback_flag', 'default_ping_status', 'default_comment_status', 'comments_notify', 'moderation_notify', 'comment_moderation', 'require_name_email', 'comment_whitelist', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_rating', 'avatar_default', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comment_registration' ),
'media' => array( 'thumbnail_size_w', 'thumbnail_size_h', 'thumbnail_crop', 'medium_size_w', 'medium_size_h', 'large_size_w', 'large_size_h', 'image_default_size', 'image_default_align', 'image_default_link_type' ),
'reading' => array( 'posts_per_page', 'posts_per_rss', 'rss_use_excerpt', 'show_on_front', 'page_on_front', 'page_for_posts', 'blog_public' ),

View File

@ -222,8 +222,9 @@ add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
add_action( 'wp_head', 'wp_generator' );
add_action( 'wp_head', 'rel_canonical' );
add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
add_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 );
add_action( 'wp_head', 'wp_site_icon', 99 );
add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
add_action( 'template_redirect', 'wp_shortlink_header', 11, 0 );
add_action( 'wp_print_footer_scripts', '_wp_footer_scripts' );
add_action( 'init', 'check_theme_switched', 99 );
@ -243,6 +244,11 @@ foreach ( array( 'rss2_head', 'commentsrss2_head', 'rss_head', 'rdf_header', 'at
add_action( $action, 'the_generator' );
}
// Feed Site Icon
add_action( 'atom_head', 'atom_site_icon' );
add_action( 'rss2_head', 'rss2_site_icon' );
// WP Cron
if ( !defined( 'DOING_CRON' ) )
add_action( 'init', 'wp_cron' );

View File

@ -587,6 +587,42 @@ function prep_atom_text_construct($data) {
}
}
/**
* Display Site Icon in atom feeds.
*
* @since 4.3.0
*/
function atom_site_icon() {
$url = get_site_icon_url( null, 32 );
if ( $url ) {
echo "<icon>$url</icon>\n";
}
}
/**
* Display Site Icon in RSS2.
*
* @since 4.3.0
*/
function rss2_site_icon() {
$rss_title = get_wp_title_rss();
if ( empty( $rss_title ) ) {
$rss_title = get_bloginfo_rss( 'name' );
}
$url = get_site_icon_url( null, 32 );
if ( $url ) {
echo '
<image>
<url>' . convert_chars( $url ) . '</url>
<title>' . $rss_title . '</title>
<link>' . get_bloginfo_rss( 'url' ) . '</link>
<width>32</width>
<height>32</height>
</image> ' . "\n";
}
}
/**
* Display the link for the currently displayed feed in a XSS safe way.
*

View File

@ -3421,6 +3421,7 @@ function sanitize_option( $option, $value ) {
case 'thread_comments_depth':
case 'users_can_register':
case 'start_of_week':
case 'site_icon':
$value = absint( $value );
break;

View File

@ -721,6 +721,58 @@ function get_bloginfo( $show = '', $filter = 'raw' ) {
return $output;
}
/**
* Returns the Site Icon URL.
*
* @param null|int $blog_id Id of the blog to get the site icon for.
* @param int $size Size of the site icon.
* @param string $url Fallback url if no site icon is found.
* @return string Site Icon URL.
*/
function get_site_icon_url( $blog_id = null, $size = 512, $url = '' ) {
if ( function_exists( 'get_blog_option' ) ) {
if ( ! is_int( $blog_id ) ) {
$blog_id = get_current_blog_id();
}
$site_icon_id = get_blog_option( $blog_id, 'site_icon' );
} else {
$site_icon_id = get_option( 'site_icon' );
}
if ( $site_icon_id ) {
if ( $size >= 512 ) {
$size_data = 'full';
} else {
$size_data = array( $size, $size );
}
$url_data = wp_get_attachment_image_src( $site_icon_id, $size_data );
$url = $url_data[0];
}
return $url;
}
/**
* Displays the Site Icon URL.
*
* @param null|int $blog_id Id of the blog to get the site icon for.
* @param int $size Size of the site icon.
* @param string $url Fallback url if no site icon is found.
*/
function site_icon_url( $blog_id = null, $size = 512, $url = '' ) {
echo esc_url( get_site_icon_url( $blog_id, $size, $url ) );
}
/**
* Whether the site has a Site Icon.
*
* @param int|null $blog_id Optional. Blog ID. Default: Current blog.
* @return bool
*/
function has_site_icon( $blog_id = null ) {
return !! get_site_icon_url( $blog_id, 512 );
}
/**
* Display title tag with contents.
*
@ -2385,6 +2437,39 @@ function wp_no_robots() {
echo "<meta name='robots' content='noindex,follow' />\n";
}
/**
* Display site icon meta tags.
*
* @since 4.3.0
*
* @link http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon HTML5 specification link icon.
*/
function wp_site_icon() {
if ( ! has_site_icon() ) {
return;
}
$meta_tags = array(
sprintf( '<link rel="icon" href="%s" sizes="32x32" />', esc_url( get_site_icon_url( null, 32 ) ) ),
sprintf( '<link rel="apple-touch-icon-precomposed" href="%s">', esc_url( get_site_icon_url( null, 180 ) ) ),
sprintf( '<meta name="msapplication-TileImage" content="%s">', esc_url( get_site_icon_url( null, 270 ) ) ),
);
/**
* Filters the site icon meta tags, so Plugins can add their own.
*
* @since 4.3.0
*
* @param array $meta_tags Site Icon meta elements.
*/
$meta_tags = apply_filters( 'site_icon_meta_tags', $meta_tags );
$meta_tags = array_filter( $meta_tags );
foreach ( $meta_tags as $meta_tag ) {
echo "$meta_tag\n";
}
}
/**
* Whether the user should have a WYSIWIG editor.
*

View File

@ -612,6 +612,9 @@ function wp_default_scripts( &$scripts ) {
'untitled' => _x( '(no label)', 'missing menu item navigation label' )
) );
$scripts->add( 'site-icon', '/wp-admin/js/site-icon.js', array( 'jquery' ), false, 1 );
$scripts->add( 'site-icon-crop', '/wp-admin/js/site-icon-crop.js', array( 'jcrop' ), false, 1 );
$scripts->add( 'custom-header', "/wp-admin/js/custom-header.js", array( 'jquery-masonry' ), false, 1 );
$scripts->add( 'custom-background', "/wp-admin/js/custom-background$suffix.js", array( 'wp-color-picker', 'media-views' ), false, 1 );
$scripts->add( 'media-gallery', "/wp-admin/js/media-gallery$suffix.js", array('jquery'), false, 1 );

View File

@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
$wp_version = '4.3-alpha-32993';
$wp_version = '4.3-alpha-32994';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.