WordPress.org

Make WordPress Core

Ticket #16434: trac-16434-01.patch

File trac-16434-01.patch, 16.8 KB (added by TomAuger, 6 years ago)

Early patch with basic add image functionality

  • .

  • wp-admin/favicon-upload.php

    Property changes on: .
    ___________________________________________________________________
    Added: svn:ignore
    ## -0,0 +1,2 ##
    +wp-config.php
    +_extras
     
     1<?php
     2/**
     3 * Handles the uploading, cropping and scaling of favicons.
     4 *
     5 * @package WordPress
     6 * @subpackage Administration
     7 * @since 3.4.1
     8 */
     9
     10// Bootstrap admin and all its goodies
     11require_once('admin.php');
     12
     13define( 'FAVICON_SIZE', 32 ); // Width (and height) of the favicon (in pixels)
     14
     15/*      Upload is a 2-step process:     
     16 *      1. Process the uploaded file and show the crop UI
     17 *      2. Manipulate the pixel data, save to PNG and ICO and write to options.
     18 */
     19
     20if ( isset( $_POST['CROP_AND_SAVE'] ) ) {
     21        if ( isset( $_POST['attachment_id'] ) && is_numeric( $_POST['attachment_id'] ) ){
     22                $image_basename = process_crop_thumbnail( $_POST['attachment_id'] );
     23
     24                if ( is_wp_error( $image_basename ) ) {
     25                        include_once('./admin-header.php');
     26                        echo '<div class="wrap">';
     27                                echo '<h2>' . __( 'Image upload error' ) . '</h2>';
     28                                echo '<p>' . $image_basename->get_error_message() . '</p>';
     29                                echo '<p><a href="' . admin_url( 'options-general.php' ) . '">&laquo;' . __( 'Back to Settings &gt; General' ) . '</a></p>';
     30                        echo '</div><!-- .wrap -->';
     31                        include_once('./admin-footer.php');
     32                } else {
     33                        foreach ( array( $image_basename . '.png', $image_basename . '.ico' ) as $image_filename )
     34                                save_thumbnail_attachment( $image_filename, $attachment_id );
     35
     36                        // And save the basename out to options.
     37                        update_option( 'sitefavicon', basename( $image_basename ) );
     38                       
     39                        /** @TODO need to find a way to notify the user that the process has completed successfully - admin_notices? */
     40                        wp_redirect( admin_url( 'options-general.php' ) );
     41                }
     42        } else {
     43                return new WP_Error( 'attachment_id_missing', 'Form submission error.' );
     44        }
     45} elseif ( isset( $_POST['REMOVE_FAVICON'] ) ) {
     46        remove_thumbnail();
     47} else {
     48        /** @TODO make sure that we trap for someone just pressing "Upload image" but with no image attached */
     49        // Enqueue the JS for the cropper...
     50        add_action( 'admin_enqueue_scripts', 'enqueue_cropper' );
     51        // ...and our own script for populating the crop form
     52        add_action( 'admin_footer', 'cropping_js', 10,  1);
     53       
     54        // Process the upload and create the attachment file in the media center
     55        $image_results = process_thumbnail_upload();
     56       
     57        include_once('./admin-header.php');
     58       
     59        // hack because image replication isn't fast enough. See https://wpcom.automattic.com/ticket/1294
     60        sleep( 2 );
     61
     62        echo '<div class="wrap">';
     63       
     64        if ( is_wp_error( $image_results ) )  {
     65                echo '<h2>' . __( 'Image upload error' ) . '</h2>';
     66                echo '<p>' . $image_results->get_error_message() . '</p>';
     67                echo '<p><a href="' . admin_url( 'options-general.php' ) . '">&laquo;' . __( 'Back to Settings &gt; General' ) . '</a></p>';
     68        } else {
     69                // Image upload successful.
     70                // Now we can hook in our javascript and provide the width/height of our image as the default crop size
     71                $crop_size = min( $image_results['width'], $image_results['height'] );
     72                echo '<script type="text/javascript">var jcrop_starting_size = ' . $crop_size . '; // Initialize jcrop crop area starting size</script>';
     73       
     74                echo '<h2>' . __( 'Crop uploaded image' ) . '</h2>';
     75                echo '<p>' . __( 'Choose the part of the image you want to use for your favicon.' ) . '</p>';
     76               
     77                echo '<form id="favicon-crop-form" method="post" action="' . $_SERVER['REQUEST_URI'] . '">'; // Point the form action back to this script
     78               
     79                        echo <<<CROP_FORM
     80                        <input type="hidden" name="x1" id="x1" />
     81                        <input type="hidden" name="y1" id="y1" />
     82                        <input type="hidden" name="x2" id="x2" />
     83                        <input type="hidden" name="y2" id="y2" />
     84                        <input type="hidden" name="width" id="width" />
     85                        <input type="hidden" name="height" id="height" />
     86                        <input type="hidden" name="attachment_id" id="attachment_id" value="{$image_results['attachment_id']}" />
     87                        <input type="hidden" name="scaling_factor" id="scaling_factor" value="{$image_results['scaling_factor']}" />
     88CROP_FORM;
     89               
     90                        echo '<img src="' . $image_results['src'] . '" id="upload" width="' . $image_results['width'] . '" height="' . $image_results['height'] . '" />';
     91               
     92                        echo '<p class="submit"><input type="submit" name="CROP_AND_SAVE" value="' . __( 'Crop image' ) . ' &raquo;" /></p>';
     93                echo '</form>';
     94        }
     95       
     96        echo '</div><!-- .wrap -->';
     97       
     98        include_once('./admin-footer.php');
     99}
     100
     101
     102/**
     103 * Process the image file upload and return a WP_Error or details about the attachment image file.
     104 *
     105 * @return mixed WP_Error | $image_results array
     106 */
     107function process_thumbnail_upload(){
     108        $file = wp_handle_upload( $_FILES['avatarfile'], array( 'action' => 'wp_upload_favicon') );
     109        if ( isset($file['error']) ) die( $file['error'] );
     110       
     111        $url = $file['url'];
     112        $file = $file['file'];
     113        $filename = basename($file);
     114       
     115        // Construct the object array
     116        $object = array(
     117                'post_title' => $filename,
     118                'post_content' => $url,
     119                'post_mime_type' => 'import',
     120                'guid' => $url
     121        );
     122
     123        // Save the data.  Also makes replication work
     124        $id = wp_insert_attachment($object, $file);
     125
     126        // Retrieve the image dimensions
     127        list( $orig_width, $orig_height, $type, $attr ) = getimagesize( $file );
     128       
     129        // Do we need to scale down the image so we can display it nicely in the interactive Crop tool?
     130        if ( $orig_width > 600 || $orig_height > 600 ) {
     131                $image = wp_create_thumbnail( $file, 600 );
     132                list( $width, $height, $type, $attr ) = getimagesize( $image );
     133               
     134                 // Update the attachment record to reflect the newly-scaled thumbnail image
     135                $thumb = basename( $image );
     136                $metadata = array( 'thumb' => $thumb );
     137                wp_update_attachment_metadata( $id, $metadata );
     138
     139                $url = str_replace( basename( $url ), $thumb, $url );
     140
     141                $scaling = $orig_width / $width;
     142        } else {
     143                // No scaling required; just copy original values.
     144                $width = $orig_width;
     145                $height = $orig_height;
     146                $scaling = 1;
     147        }
     148
     149        // Check image file format
     150        $image_type = exif_imagetype( get_attached_file( $id ) );
     151        if (! in_array( $image_type, array( IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP ) ) )
     152                $error = new WP_Error( 'bad_file_format', __( 'Please only use PNG (.png), JPEG (.jpg) or BMP (.bmp) image files for favicons. ' ) );
     153       
     154        // return WP_Error or the $image_results array
     155        if ( $error ){
     156                return $error;
     157        } else {
     158                return array(
     159                        'attachment_id' => $id,
     160                        'src' => $url,
     161                        'width' => $width,
     162                        'height' => $height,
     163                        'scaling_factor' => $scaling
     164                );
     165        }
     166}
     167
     168/**
     169 * Create PNG and BMP image resources based on the form submission of the cropped thumbnail.
     170 *
     171 * @param int $attachment_id The ID of the original attachment's post record.
     172 * @return mixed WP_Error | Favicon file base name (ie: fully qualified file name without any TLA file extension)
     173 */
     174function process_crop_thumbnail( $attachment_id ){
     175        $src_file = get_attached_file( $attachment_id );
     176
     177        // Highly unlikely, but let's check
     178        if (! file_exists( $src_file ) )
     179                return new WP_Error( 'file_missing', __( 'Attachment image file missing (possible save error: check space on web server).' ) );
     180
     181        // Make sure we're still within accepted image types
     182        $image_type = exif_imagetype( $src_file );
     183        if (! $image_type || ! in_array( $image_type, array( IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP ) ) )
     184                return new WP_Error( 'bad_file_format', __( 'Please only use PNG (.png), JPEG (.jpg) or BMP (.bmp) image files for favicons. ' ) );
     185
     186        // Parse image file bytes
     187        $src_image = wp_load_image( $src_file );
     188        if ( !is_resource($src_image) )
     189                return new WP_Error( 'is_not_resource', __( 'Error loading image. You got me: I\'m stumped.' ) );
     190
     191        // We crop from the original, not the medium sized, display-only thumbnail
     192        $src_x = $_POST['x1'] * $_POST['scaling_factor'];
     193        $src_y = $_POST['y1'] * $_POST['scaling_factor'];
     194        $src_width = $_POST['width'] * $_POST['scaling_factor'];
     195        $src_height = $_POST['height'] * $_POST['scaling_factor'];
     196
     197        $dst_width = $dst_height = FAVICON_SIZE;
     198        // Avoid upscaling
     199        if ( $src_width < $dst_width || $src_height < $dst_height ) {
     200                $dst_width = $src_width;
     201                $dst_height = $src_height;
     202        }
     203
     204        $dst_image = wp_imagecreatetruecolor( $dst_width, $dst_height );
     205        if (function_exists('imageantialias')) imageantialias($dst_image, true);
     206        imagealphablending($dst_image, false);
     207        imagesavealpha($dst_image, true);
     208        imagecopyresampled($dst_image, $src_image, 0, 0, $src_x, $src_y, $dst_width, $dst_height, $src_width, $src_height);
     209        imagedestroy( $src_image );
     210
     211
     212        // Save the image in PNG and BMP formats
     213        $file_info = pathinfo( $src_file );
     214        $src_basename = basename( $src_file, '.' . $file_info['extension'] );
     215        $dst_filename = str_replace( $src_basename, $src_basename . '_' . FAVICON_SIZE . 'x' . FAVICON_SIZE, $src_file );
     216        // Strip the TLA from the filename
     217        $dst_filename = preg_replace( '/\\.[^\\.]+$/', '', $dst_filename );
     218
     219        $png_filename = $dst_filename . '.png';
     220        if (! imagepng( $dst_image, $png_filename, 0 ) )
     221                return new WP_Error( 'png_write_error', 'Error writing PNG favicon file.' );
     222        $ico_filename = $dst_filename . '.ico';
     223        if (!image2wbmp( $dst_image, $ico_filename, 1 ) )
     224                return new WP_Error( 'ico_write_error', 'Error writing ICO favicon file.' );
     225
     226        imagedestroy($dst_image);
     227
     228        return $dst_filename;
     229}
     230
     231/**
     232 * Creates an attachment post record for a newly created thumbnail
     233 *
     234 * @param string $file_name Fully qualified file name for the image asset file.
     235 * @param int $parent_attachment_id The ID of the original thumbnail's attachment post record
     236 *
     237 * @return int The ID of the newly-created thumbnail attachment post record
     238 */
     239function save_thumbnail_attachment( $file_name, $parent_attachment_id ){
     240        $file_info = pathinfo( $file_name ); // So we can get the TLA later on
     241       
     242        $file_name = apply_filters( 'wp_create_file_in_uploads', $file_name, $parent_attachment_id ); // For replication
     243
     244        $parent = get_post( $parent_attachment_id );
     245        $parent_url = $parent->guid;
     246       
     247        // Update the attachment
     248        $mimes = get_allowed_mime_types();
     249        $attachment_id = wp_insert_attachment( array(
     250                'ID' => $attachment_id,
     251                'post_title' => basename( $file_name ),
     252                'post_content' => $url,
     253                'post_mime_type' => $mimes[ $file_info['extension'] ],
     254                'guid' => $url,
     255                'context' => 'favicon'
     256        ), $file_name );
     257        wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file_name ) );
     258       
     259        return $attachment_id;
     260}
     261
     262function remove_thumbnail(){
     263       
     264}
     265
     266
     267/**
     268 * Called in admin_enqueue_scripts to add the cropper.js script and styles
     269 */
     270function enqueue_cropper(){
     271        wp_enqueue_script( 'jcrop', 'jquery' );
     272        wp_enqueue_style('jcrop'); // We can enqueue styles within the admin_enqueue_script action hook {@link http://wpdevel.wordpress.com/2011/12/12/use-wp_enqueue_scripts-not-wp_print_styles-to-enqueue-scripts-and-styles-for-the-frontend/}
     273}
     274
     275function cropping_js(){
     276        // Purely for coding convenience and legibility
     277        $favicon_size = FAVICON_SIZE;
     278       
     279        echo <<<CROP_JS
     280        <!-- Favicon cropping -->
     281        <script type="text/javascript">
     282                // Update the crop form
     283                function onEndCrop( coords ) {
     284                        jQuery( '#x1' ).val(coords.x);
     285                        jQuery( '#y1' ).val(coords.y);
     286                        jQuery( '#x2' ).val(coords.x2);
     287                        jQuery( '#y2' ).val(coords.y2);
     288                        jQuery( '#width' ).val(coords.w);
     289                        jQuery( '#height' ).val(coords.h);
     290                }
     291
     292                // with a supplied ratio
     293                jQuery(function($) {
     294                        if (! jcrop_starting_size) jcrop_starting_size = {$favicon_size}; // jcrop_starting_size should be set in the body once the image has been processed
     295
     296                        // Set up default values on the crop form
     297                        jQuery( '#x1' ).val(0);
     298                        jQuery( '#y1' ).val(0);
     299                        jQuery( '#x2' ).val(jcrop_starting_size);
     300                        jQuery( '#y2' ).val(jcrop_starting_size);
     301                        jQuery( '#width' ).val(jcrop_starting_size);
     302                        jQuery( '#height' ).val(jcrop_starting_size);
     303
     304                        // Initialize Jcrop
     305                        $('#upload').Jcrop({
     306                                aspectRatio: 1,
     307                                setSelect: [0, 0, jcrop_starting_size, jcrop_starting_size],
     308                                onSelect: onEndCrop
     309                        });
     310                });
     311        </script>
     312CROP_JS;
     313
     314}
     315 No newline at end of file
  • wp-admin/options-general.php

     
    8888<?php screen_icon(); ?>
    8989<h2><?php echo esc_html( $title ); ?></h2>
    9090
     91<form action="<?php echo admin_url('favicon-upload.php')?>" method="post" enctype="multipart/form-data">
     92        <input type="hidden" name="action" value="wp_upload_favicon" />
     93       
     94        <table class="form-table">
     95                <tr valign="top">
     96                        <th scope="row"><label for="sitefavicon"><?php _e('Favicon') ?></label></th>
     97                        <td>
     98                                <?php //echo generate_media_thumbnail_link( 'options-general-favicon', 0,  __( 'Upload favicon image' ) ); ?>
     99                                <input class="button" name="avatarfile" type="file" id="avatarfile" size="20" />
     100                                <p class="submit no-js"><input type="submit" name="Submit" value="Upload Image &raquo;" /></p>
     101                                <span class="description"><?php _e('Click to upload your own custom icon ("favicon") for your blog. You\'ll be able to crop and scale it once it\'s uploaded.') ?></span>
     102
     103                        </td>
     104                </tr>
     105        </table>
     106</form>
     107
     108
    91109<form method="post" action="options.php">
    92110<?php settings_fields('general'); ?>
    93111
  • wp-includes/functions.php

     
    18451845                'png' => 'image/png',
    18461846                'bmp' => 'image/bmp',
    18471847                'tif|tiff' => 'image/tiff',
    1848                 'ico' => 'image/x-icon',
     1848                'ico' => 'image/vnd.microsoft.icon',
    18491849                'asf|asx|wax|wmv|wmx' => 'video/asf',
    18501850                'avi' => 'video/avi',
    18511851                'divx' => 'video/divx',
  • wp-includes/general-template.php

     
    15871587}
    15881588
    15891589/**
     1590 * Convenience function that echoes the HTML for the site's favicon icon.
     1591 * By default, automatically included in the header via the 'wp_head' action, which can be removed by themes if a custom favicon is desired.
     1592 *
     1593 * @uses generate_site_favicon_html() to do the actual heavy lifting
     1594 */
     1595function site_favicon(){
     1596        echo generate_site_favicon_html();
     1597}
     1598add_action( 'wp_head', 'site_favicon' );
     1599add_action( 'admin_head', 'site_favicon' );
     1600
     1601/**
     1602 * Return the HTML for the site's favicon icon, if such has been defined.
     1603 *
     1604 * @uses get_site_favicon_uri();
     1605 *
     1606 * Includes the conditional tag wrapper for an IE (.ico) version.
     1607 */
     1608function generate_site_favicon_html() {
     1609        $ie_favicon_uri = get_site_favicon_uri( 'ico' );
     1610        $favicon_uri = get_site_favicon_uri();
     1611
     1612        $content = "";
     1613        if (! is_wp_error( $ie_favicon_uri ) && ! is_wp_error( $favicon_uri ) ){
     1614
     1615                $content .= <<<FAVICON_HTML
     1616<!--Favicon (via 'wp_head' action) -->
     1617<!--[if IE]>
     1618<link rel="shortcut icon" href="{$ie_favicon_uri}" />
     1619<![endif]-->
     1620<!--[if !IE]>-->
     1621<link href="{$favicon_uri}" rel="icon" type="image/png" />
     1622<!--<![endif]-->
     1623FAVICON_HTML;
     1624    }
     1625        return $content;
     1626}
     1627
     1628/**
     1629 * Returns the URI for the site's favicon based on the option set in  Admin > Settings > General.
     1630 *
     1631 * @param string $format png|ico default 'png'. Use 'ico' for serving up an IE-compatible ICO file
     1632 * @return mixed WP_Error | fully qualified URI
     1633 */
     1634function get_site_favicon_uri( $format = 'png' ){
     1635        /** @TODO provide error checking for validity of $format and $size */
     1636        //return site_url( '/wp-content/uploads/2012/02/wp-favicon_' . $size . 'x' . $size . '.' . $format );
     1637        $error = null;
     1638
     1639        $favicon_basename = get_option ( 'sitefavicon' );
     1640        if ( ! empty( $favicon_basename ) ) {
     1641                $favicon_fullname = $favicon_basename . '-' . $format;
     1642               
     1643                $posts = get_posts( array( 'name' => $favicon_fullname, 'post_type' => 'attachment' ) );
     1644                if ( $posts[0] ){
     1645                        $favicon_file = wp_get_attachment_url( $posts[0]->ID );
     1646                                return $favicon_file ;
     1647                } else {
     1648                        $error = new WP_Error( 'attachment_missing', __( "Favicon file '$favicon_fullname' not found." ) );
     1649                }
     1650        } else {
     1651                $error = new WP_Error( 'no_icon', __( "No favicon file provided." ) );
     1652        }
     1653
     1654        // We get here because of an error condition
     1655        /** @TODO default to the theme's favicon **/
     1656        return $error;
     1657}
     1658
     1659/**
    15901660 * Display the links to the general feeds.
    15911661 *
    15921662 * @since 2.8.0