WordPress.org

Make WordPress Core

Changeset 34855


Ignore:
Timestamp:
10/06/15 04:58:21 (2 years ago)
Author:
wonderboymusic
Message:

Merge the Responsive Images feature plugin into core, initial commit. See: https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/

New functions in media.php:

  • wp_get_attachment_image_srcset_array() - Returns an array of image candidate string data used to build a srcset value for an attachment given an $attachement_id and $size.
  • wp_get_attachment_image_srcset() - Returns the srcset value for an attachment given an $attachement_id and $size.
  • wp_get_attachment_image_sizes() - Returns the sizes value for an attachment given an $attachement_id and $size and optional arguments used to alter its output.
  • wp_make_content_images_responsive() - A display filter for adding srcset and sizes to images embedded in content.
  • wp_img_add_srcset_and_sizes() - A utility function used by wp_make_content_images_responsive() to add srcset and sizes to a single <img> element.

Modifies existing core functions:

  • Modify wp_get_attachment_image() so the HTML returned for an image includes srcset and sizes.
  • Modify get_media_embedded_in_content() (sup, 3.6 leftover) by adding <img> to the list of accepted tags that can be matched in content. This is used in wp_make_content_images_responsive() to find all of the images embedded in content before passing them off to wp_img_add_srcset_and_sizes().

Tests:

  • Add a new factory method to WP_UnitTest_Factory_For_Attachment named create_upload_object()
  • Adds unit tests
  • Updates unit tests

Props joemcgill, tevko, jaspermdegroot, mdmcginn, barryceelen, peterwilsoncc, fsylum, wonderboymusic, chriscoyier, benjaminpick, jrfnl, #12kingkool68, janhenckens, ryanmarkel, side777, ryelle, wturrell, micahmills, mattbagwell, coliff, DrewAPicture.
See #33641.

Location:
trunk
Files:
1 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/default-filters.php

    r34711 r34855  
    130130add_filter( 'the_title', 'trim'          ); 
    131131 
    132 add_filter( 'the_content', 'wptexturize'        ); 
    133 add_filter( 'the_content', 'convert_smilies'    ); 
    134 add_filter( 'the_content', 'convert_chars'      ); 
    135 add_filter( 'the_content', 'wpautop'            ); 
    136 add_filter( 'the_content', 'shortcode_unautop'  ); 
    137 add_filter( 'the_content', 'prepend_attachment' ); 
     132add_filter( 'the_content', 'wptexturize'                       ); 
     133add_filter( 'the_content', 'convert_smilies'                   ); 
     134add_filter( 'the_content', 'convert_chars'                     ); 
     135add_filter( 'the_content', 'wpautop'                           ); 
     136add_filter( 'the_content', 'shortcode_unautop'                 ); 
     137add_filter( 'the_content', 'prepend_attachment'                ); 
     138add_filter( 'the_content', 'wp_make_content_images_responsive' ); 
    138139 
    139140add_filter( 'the_excerpt',     'wptexturize'      ); 
  • trunk/src/wp-includes/media.php

    r34851 r34855  
    778778        $attr = wp_parse_args($attr, $default_attr); 
    779779 
     780        // Generate srcset and sizes if not already present. 
     781        if ( empty( $attr['srcset'] ) && $srcset = wp_get_attachment_image_srcset( $attachment_id, $size ) ) { 
     782            $attr['srcset'] = $srcset; 
     783            $sizes_args = array( 
     784                'height' => $height, 
     785                'width'  => $width, 
     786            ); 
     787            $attr['sizes'] = wp_get_attachment_image_sizes( $attachment_id, $size, $sizes_args ); 
     788        } 
     789 
    780790        /** 
    781791         * Filter the list of attachment image attributes. 
     
    813823    $image = wp_get_attachment_image_src( $attachment_id, $size, $icon ); 
    814824    return isset( $image['0'] ) ? $image['0'] : false; 
     825} 
     826 
     827/** 
     828 * Retrieves an array of URLs and pixel widths representing sizes of an image. 
     829 * 
     830 * The purpose is to populate a source set when creating responsive image markup. 
     831 * 
     832 * @since 4.4.0 
     833 * 
     834 * @param  int    $attachment_id Image attachment ID. 
     835 * @param  string $size          Optional. Name of image size. Default 'medium'. 
     836 * @return array|bool $images { 
     837 *     Array image candidate values containing a URL, descriptor type, and 
     838 *     descriptor value. False if none exist. 
     839 * 
     840 *     @type array $values { 
     841 *        @type string $url        An image URL. 
     842 *        @type string $descriptor A width or density descriptor used in a srcset. 
     843 *        @type int    $value      The descriptor value representing a width or 
     844 *                                 or pixel density. 
     845 *     } 
     846 * } 
     847 * 
     848 */ 
     849function wp_get_attachment_image_srcset_array( $attachment_id, $size = 'medium' ) { 
     850    // Get the intermediate size. 
     851    $image = image_get_intermediate_size( $attachment_id, $size ); 
     852 
     853    // Get the post meta. 
     854    $img_meta = wp_get_attachment_metadata( $attachment_id ); 
     855    if ( ! is_array( $img_meta ) ) { 
     856        return false; 
     857    } 
     858 
     859    // Extract the height and width from the intermediate or the full size. 
     860    $img_width  = ( $image ) ? $image['width']  : $img_meta['width']; 
     861    $img_height = ( $image ) ? $image['height'] : $img_meta['height']; 
     862 
     863    // Bail early if the width isn't greater that zero. 
     864    if ( ! $img_width > 0 ) { 
     865        return false; 
     866    } 
     867 
     868    // Use the URL from the intermediate size or build the url from the metadata. 
     869    if ( ! empty( $image['url'] ) ) { 
     870        $img_url = $image['url']; 
     871    } else { 
     872        $uploads_dir = wp_upload_dir(); 
     873        $img_file = ( $image ) ? path_join( dirname( $img_meta['file'] ) , $image['file'] ) : $img_meta['file']; 
     874        $img_url = $uploads_dir['baseurl'] . '/' . $img_file; 
     875    } 
     876 
     877    $img_sizes = $img_meta['sizes']; 
     878 
     879    // Add full size to the img_sizes array. 
     880    $img_sizes['full'] = array( 
     881        'width'  => $img_meta['width'], 
     882        'height' => $img_meta['height'], 
     883        'file'   => wp_basename( $img_meta['file'] ) 
     884    ); 
     885 
     886    // Calculate the image aspect ratio. 
     887    $img_ratio = $img_height / $img_width; 
     888 
     889    /* 
     890     * Images that have been edited in WordPress after being uploaded will 
     891     * contain a unique hash. Look for that hash and use it later to filter 
     892     * out images that are leftovers from previous versions. 
     893     */ 
     894    $img_edited = preg_match( '/-e[0-9]{13}/', $img_url, $img_edit_hash ); 
     895 
     896    /* 
     897     * Set up arrays to hold url candidates and matched image sources so 
     898     * we can avoid duplicates without looping through the full sources array 
     899     */ 
     900    $candidates = $sources = array(); 
     901 
     902    /* 
     903     * Loop through available images and only use images that are resized 
     904     * versions of the same rendition. 
     905     */ 
     906    foreach ( $img_sizes as $img ) { 
     907 
     908        // Filter out images that are leftovers from previous renditions. 
     909        if ( $img_edited && ! strpos( $img['file'], $img_edit_hash[0] ) ) { 
     910            continue; 
     911        } 
     912 
     913        $candidate_url = path_join( dirname( $img_url ), $img['file'] ); 
     914 
     915        // Calculate the new image ratio. 
     916        $img_ratio_compare = $img['height'] / $img['width']; 
     917 
     918        // If the new ratio differs by less than 0.01, use it. 
     919        if ( abs( $img_ratio - $img_ratio_compare ) < 0.01 && ! in_array( $candidate_url, $candidates ) ) { 
     920            // Add the URL to our list of candidates. 
     921            $candidates[] = $candidate_url; 
     922 
     923            // Add the url, descriptor, and value to the sources array to be returned. 
     924            $sources[] = array( 
     925                'url'        => $candidate_url, 
     926                'descriptor' => 'w', 
     927                'value'      => $img['width'], 
     928            ); 
     929        } 
     930    } 
     931 
     932    /** 
     933     * Filter the output of wp_get_attachment_image_srcset_array(). 
     934     * 
     935     * @since 4.4.0 
     936     * 
     937     * @param array        $sources       An array of image urls and widths. 
     938     * @param int          $attachment_id Attachment ID for image. 
     939     * @param array|string $size          Size of image, either array or string. 
     940     */ 
     941    return apply_filters( 'wp_get_attachment_image_srcset_array', $sources, $attachment_id, $size ); 
     942} 
     943 
     944/** 
     945 * Retrieves the value for an image attachment's 'srcset' attribute. 
     946 * 
     947 * @since 4.4.0 
     948 * 
     949 * @param int    $attachment_id Image attachment ID. 
     950 * @param string $size          Optional. Name of image size. Default 'medium'. 
     951 * @return string|bool A 'srcset' value string or false. 
     952 */ 
     953function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium' ) { 
     954    $srcset_array = wp_get_attachment_image_srcset_array( $attachment_id, $size ); 
     955 
     956    // Only return a srcset value if there is more than one source. 
     957    if ( count( $srcset_array ) <= 1 ) { 
     958        return false; 
     959    } 
     960 
     961    $srcset = ''; 
     962    foreach ( $srcset_array as $source ) { 
     963        $srcset .= $source['url'] . ' ' . $source['value'] . $source['descriptor'] . ', '; 
     964    } 
     965 
     966    /** 
     967     * Filter the output of wp_get_attachment_image_srcset(). 
     968     * 
     969     * @since 4.4.0 
     970     * 
     971     * @param string       $srcset        A source set formated for a `srcset` attribute. 
     972     * @param int          $attachment_id Attachment ID for image. 
     973     * @param array|string $size          Size of image, either array or string. 
     974     */ 
     975    return apply_filters( 'wp_get_attachment_image_srcset', rtrim( $srcset, ', ' ), $attachment_id, $size ); 
     976} 
     977 
     978/** 
     979 * Retrieves a source size attribute for an image from an array of values. 
     980 * 
     981 * @since 4.4.0 
     982 * 
     983 * @param int    $attachment_id Image attachment ID. 
     984 * @param string $size          Optional. Name of image size. Default value: 'medium'. 
     985 * @param array  $args { 
     986 *     Optional. Arguments to retrieve attachments. 
     987 * 
     988 *     @type array|string $sizes An array or string containing of size information. 
     989 *     @type int          $width A single width value used in the default `sizes` string. 
     990 * } 
     991 * @return string|bool A valid source size value for use in a 'sizes' attribute or false. 
     992 */ 
     993function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $args = null ) { 
     994 
     995    // Try to get the image width from $args before calling image_downsize(). 
     996    if ( is_array( $args ) && ! empty( $args['width'] ) ) { 
     997        $img_width = (int) $args['width']; 
     998    } elseif ( $img = image_get_intermediate_size( $attachment_id, $size ) ) { 
     999        list( $img_width, $img_height ) = image_constrain_size_for_editor( $img['width'], $img['height'], $size ); 
     1000    } 
     1001 
     1002    // Bail early if $image_width isn't set. 
     1003    if ( ! $img_width ) { 
     1004        return false; 
     1005    } 
     1006 
     1007    // Set the image width in pixels. 
     1008    $img_width = $img_width . 'px'; 
     1009 
     1010    // Set up our default values. 
     1011    $defaults = array( 
     1012        'sizes' => array( 
     1013            array( 
     1014                'size_value' => '100vw', 
     1015                'mq_value'   => $img_width, 
     1016                'mq_name'    => 'max-width' 
     1017            ), 
     1018            array( 
     1019                'size_value' => $img_width 
     1020            ), 
     1021        ) 
     1022    ); 
     1023 
     1024    $args = wp_parse_args( $args, $defaults ); 
     1025 
     1026    /** 
     1027     * Filter arguments used to create 'sizes' attribute. 
     1028     * 
     1029     * @since 4.4.0 
     1030     * 
     1031     * @param array   $args          An array of arguments used to create a 'sizes' attribute. 
     1032     * @param int     $attachment_id Post ID of the original image. 
     1033     * @param string  $size          Name of the image size being used. 
     1034     */ 
     1035    $args = apply_filters( 'wp_image_sizes_args', $args, $attachment_id, $size ); 
     1036 
     1037    // If sizes is passed as a string, just use the string. 
     1038    if ( is_string( $args['sizes'] ) ) { 
     1039        $size_list = $args['sizes']; 
     1040 
     1041    // Otherwise, breakdown the array and build a sizes string. 
     1042    } elseif ( is_array( $args['sizes'] ) ) { 
     1043 
     1044        $size_list = ''; 
     1045 
     1046        foreach ( $args['sizes'] as $size ) { 
     1047 
     1048            // Use 100vw as the size value unless something else is specified. 
     1049            $size_value = ( $size['size_value'] ) ? $size['size_value'] : '100vw'; 
     1050 
     1051            // If a media length is specified, build the media query. 
     1052            if ( ! empty( $size['mq_value'] ) ) { 
     1053 
     1054                $media_length = $size['mq_value']; 
     1055 
     1056                // Use max-width as the media condition unless min-width is specified. 
     1057                $media_condition = ( ! empty( $size['mq_name'] ) ) ? $size['mq_name'] : 'max-width'; 
     1058 
     1059                // If a media_length was set, create the media query. 
     1060                $media_query = '(' . $media_condition . ": " . $media_length . ') '; 
     1061 
     1062            } else { 
     1063                // If no media length was set, $media_query is blank. 
     1064                $media_query = ''; 
     1065            } 
     1066 
     1067            // Add to the source size list string. 
     1068            $size_list .= $media_query . $size_value . ', '; 
     1069        } 
     1070 
     1071        // Remove the trailing comma and space from the end of the string. 
     1072        $size_list = substr( $size_list, 0, -2 ); 
     1073    } 
     1074 
     1075    // Return the sizes value as $size_list or false. 
     1076    return ( $size_list ) ? $size_list : false; 
     1077} 
     1078 
     1079/** 
     1080 * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes. 
     1081 * 
     1082 * @since 4.4.0 
     1083 * 
     1084 * @see wp_img_add_srcset_and_sizes() 
     1085 * 
     1086 * @param string $content The raw post content to be filtered. 
     1087 * @return string Converted content with 'srcset' and 'sizes' attributes added to images. 
     1088 */ 
     1089function wp_make_content_images_responsive( $content ) { 
     1090    $images = get_media_embedded_in_content( $content, 'img' ); 
     1091 
     1092    $attachment_ids = array(); 
     1093 
     1094    foreach( $images as $image ) { 
     1095        if ( preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) ) { 
     1096            $attachment_id = (int) $class_id[1]; 
     1097            if ( $attachment_id ) { 
     1098                $attachment_ids[] = $attachment_id; 
     1099            } 
     1100        } 
     1101    } 
     1102 
     1103    if ( 0 < count( $attachment_ids ) ) { 
     1104        /* 
     1105         * Warm object caches for use with wp_get_attachment_metadata. 
     1106         * 
     1107         * To avoid making a database call for each image, a single query 
     1108         * warms the object cache with the meta information for all images. 
     1109         */ 
     1110        _prime_post_caches( $attachment_ids, false, true ); 
     1111    } 
     1112 
     1113    foreach( $images as $image ) { 
     1114        $content = str_replace( $image, wp_img_add_srcset_and_sizes( $image ), $content ); 
     1115    } 
     1116 
     1117    return $content; 
     1118} 
     1119 
     1120/** 
     1121 * Adds 'srcset' and 'sizes' attributes to an existing 'img' element. 
     1122 * 
     1123 * @since 4.4.0 
     1124 * 
     1125 * @see wp_get_attachment_image_srcset() 
     1126 * @see wp_get_attachment_image_sizes() 
     1127 * 
     1128 * @param string $image An HTML 'img' element to be filtered. 
     1129 * @return string Converted 'img' element with `srcset` and `sizes` attributes added. 
     1130 */ 
     1131function wp_img_add_srcset_and_sizes( $image ) { 
     1132    // Return early if a 'srcset' attribute already exists. 
     1133    if ( false !== strpos( $image, ' srcset="' ) ) { 
     1134        return $image; 
     1135    } 
     1136 
     1137    // Parse id, size, width, and height from the `img` element. 
     1138    $id     = preg_match( '/wp-image-([0-9]+)/i', $image, $match_id     ) ? (int) $match_id[1]     : false; 
     1139    $size   = preg_match( '/size-([^\s|"]+)/i',   $image, $match_size   ) ? $match_size[1]         : false; 
     1140    $width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : false; 
     1141    $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : false; 
     1142 
     1143    if ( $id && false === $size ) { 
     1144        $size = array( $width, $height ); 
     1145    } 
     1146 
     1147    /* 
     1148     * If attempts to parse the size value failed, attempt to use the image 
     1149     * metadata to match the 'src' against the available sizes for an attachment. 
     1150     */ 
     1151    if ( ! $size && ! empty( $id ) && is_array( $meta = wp_get_attachment_metadata( $id ) ) ) { 
     1152        // Parse the image src value from the img element. 
     1153        $src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : false; 
     1154 
     1155        // Return early if the src value is empty. 
     1156        if ( ! $src ) { 
     1157            return $image; 
     1158        } 
     1159 
     1160        /* 
     1161         * First, see if the file is the full size image. If not, loop through 
     1162         * the intermediate sizes until we find a file that matches. 
     1163         */ 
     1164        $image_filename = wp_basename( $src ); 
     1165 
     1166        if ( $image_filename === basename( $meta['file'] ) ) { 
     1167            $size = 'full'; 
     1168        } else { 
     1169            foreach( $meta['sizes'] as $image_size => $image_size_data ) { 
     1170                if ( $image_filename === $image_size_data['file'] ) { 
     1171                    $size = $image_size; 
     1172                    break; 
     1173                } 
     1174            } 
     1175        } 
     1176 
     1177    } 
     1178 
     1179    // If ID and size, try for 'srcset' and 'sizes' and update the markup. 
     1180    if ( $id && $size && $srcset = wp_get_attachment_image_srcset( $id, $size ) ) { 
     1181 
     1182        /* 
     1183         * Pass the 'height' and 'width' to 'wp_get_attachment_image_sizes()' to avoid 
     1184         * recalculating the image size. 
     1185         */ 
     1186        $args = array( 
     1187            'height' => $height, 
     1188            'width'  => $width, 
     1189        ); 
     1190 
     1191        $sizes = wp_get_attachment_image_sizes( $id, $size, $args ); 
     1192 
     1193        // Format the srcset and sizes string and escape attributes. 
     1194        $srcset_and_sizes = sprintf( ' srcset="%s" sizes="%s"', esc_attr( $srcset ), esc_attr( $sizes) ); 
     1195 
     1196        // Add srcset and sizes attributes to the image markup. 
     1197        $image = preg_replace( '/<img ([^>]+)[\s?][\/?]>/', '<img $1' . $srcset_and_sizes . ' />', $image ); 
     1198    } 
     1199 
     1200    return $image; 
    8151201} 
    8161202 
     
    29873373     * 
    29883374     * @since 4.2.0 
     3375     * @since 4.4.0 Added 'img' to the allowed types. 
    29893376     * 
    29903377     * @param array $allowed_media_types An array of allowed media types. Default media types are 
    2991      *                                   'audio', 'video', 'object', 'embed', and 'iframe'. 
    2992      */ 
    2993     $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe' ) ); 
     3378     *                                   'audio', 'video', 'object', 'embed', 'iframe', and 'img'. 
     3379     */ 
     3380    $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe', 'img' ) ); 
    29943381 
    29953382    if ( ! empty( $types ) ) { 
  • trunk/tests/phpunit/includes/factory.php

    r34828 r34855  
    9494    function create_object( $file, $parent = 0, $args = array() ) { 
    9595        return wp_insert_attachment( $args, $file, $parent ); 
     96    } 
     97 
     98    function create_upload_object( $file, $parent = 0 ) { 
     99        $contents = file_get_contents($file); 
     100        $upload = wp_upload_bits(basename($file), null, $contents); 
     101 
     102        $type = ''; 
     103        if ( ! empty($upload['type']) ) { 
     104            $type = $upload['type']; 
     105        } else { 
     106            $mime = wp_check_filetype( $upload['file'] ); 
     107            if ($mime) 
     108                $type = $mime['type']; 
     109        } 
     110 
     111        $attachment = array( 
     112            'post_title' => basename( $upload['file'] ), 
     113            'post_content' => '', 
     114            'post_type' => 'attachment', 
     115            'post_parent' => $parent, 
     116            'post_mime_type' => $type, 
     117            'guid' => $upload[ 'url' ], 
     118        ); 
     119 
     120        // Save the data 
     121        $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $parent ); 
     122        wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 
     123 
     124        return $id; 
    96125    } 
    97126} 
  • trunk/tests/phpunit/tests/media.php

    r34694 r34855  
    1919        $this->img_url = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . $this->img_name; 
    2020        $this->img_html = '<img src="' . $this->img_url . '"/>'; 
    21         $this->img_dimensions = array( 'width' => 100, 'height' => 100 ); 
     21        $this->img_meta = array( 'width' => 100, 'height' => 100, 'sizes' => '' ); 
    2222    } 
    2323 
     
    289289                'post_type' => 'attachment' 
    290290            ) ); 
    291             wp_update_attachment_metadata( $attachment_id, $this->img_dimensions ); 
     291            $metadata = array_merge( array( "file" => "image$i.jpg" ), $this->img_meta ); 
     292            wp_update_attachment_metadata( $attachment_id, $metadata ); 
    292293            $ids1[] = $attachment_id; 
    293294            $ids1_srcs[] = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . "image$i.jpg"; 
     
    301302                'post_type' => 'attachment' 
    302303            ) ); 
    303             wp_update_attachment_metadata( $attachment_id, $this->img_dimensions ); 
     304            $metadata = array_merge( array( "file" => "image$i.jpg" ), $this->img_meta ); 
     305            wp_update_attachment_metadata( $attachment_id, $metadata ); 
    304306            $ids2[] = $attachment_id; 
    305307            $ids2_srcs[] = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . "image$i.jpg"; 
     
    330332                'post_type' => 'attachment' 
    331333            ) ); 
    332             wp_update_attachment_metadata( $attachment_id, $this->img_dimensions ); 
     334            $metadata = array_merge( array( "file" => "image$i.jpg" ), $this->img_meta ); 
     335            wp_update_attachment_metadata( $attachment_id, $metadata ); 
    333336            $ids1[] = $attachment_id; 
    334337            $ids1_srcs[] = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . "image$i.jpg"; 
     
    342345                'post_type' => 'attachment' 
    343346            ) ); 
    344             wp_update_attachment_metadata( $attachment_id, $this->img_dimensions ); 
     347            $metadata = array_merge( array( "file" => "image$i.jpg" ), $this->img_meta ); 
     348            wp_update_attachment_metadata( $attachment_id, $metadata ); 
    345349            $ids2[] = $attachment_id; 
    346350            $ids2_srcs[] = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/' . "image$i.jpg"; 
     
    502506        $this->assertEquals( 200, $_wp_additional_image_sizes['test-size']['width'] ); 
    503507        $this->assertEquals( 600, $_wp_additional_image_sizes['test-size']['height'] ); 
     508 
     509        // Clean up 
    504510        remove_image_size( 'test-size' ); 
    505511    } 
     
    521527        add_image_size( 'test-size', 200, 600 ); 
    522528        $this->assertTrue( has_image_size( 'test-size' ) ); 
     529 
     530        // Clean up 
    523531        remove_image_size( 'test-size' ); 
    524532    } 
     
    738746        $this->assertEquals( $image[0], wp_get_attachment_image_url( $attachment_id ) ); 
    739747    } 
     748 
     749    /** 
     750     * @ticket 33641 
     751     */ 
     752    function test_wp_get_attachment_image_srcset_array() { 
     753        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     754        $id = $this->factory->attachment->create_upload_object( $filename ); 
     755 
     756        $year_month = date('Y/m'); 
     757        $image = wp_get_attachment_metadata( $id ); 
     758 
     759        $expected = array( 
     760            array( 
     761                'url'        => 'http://example.org/wp-content/uploads/' . $year_month . '/' . $image['sizes']['medium']['file'], 
     762                'descriptor' => 'w', 
     763                'value'      => $image['sizes']['medium']['width'], 
     764            ), 
     765            array( 
     766                'url'        => 'http://example.org/wp-content/uploads/' . $year_month . '/' . $image['sizes']['large']['file'], 
     767                'descriptor' => 'w', 
     768                'value'      => $image['sizes']['large']['width'], 
     769            ), 
     770            array( 
     771                'url'        => 'http://example.org/wp-content/uploads/' . $image['file'], 
     772                'descriptor' => 'w', 
     773                'value'      => $image['width'], 
     774            ), 
     775        ); 
     776 
     777        // Set up test cases for all expected size names and a random one. 
     778        $sizes = array( 'medium', 'large', 'full', 'yoav' ); 
     779 
     780        foreach ( $sizes as $size ) { 
     781            $this->assertSame( $expected, wp_get_attachment_image_srcset_array( $id, $size ) ); 
     782        } 
     783    } 
     784 
     785    /** 
     786     * @ticket 33641 
     787     */ 
     788    function test_wp_get_attachment_image_srcset_array_no_date_upoads() { 
     789        // Save the current setting for uploads folders 
     790        $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' ); 
     791 
     792        // Disable date organized uploads 
     793        update_option( 'uploads_use_yearmonth_folders', 0 ); 
     794 
     795        // Make an image. 
     796        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     797        $id = $this->factory->attachment->create_upload_object( $filename ); 
     798 
     799        $image = wp_get_attachment_metadata( $id ); 
     800 
     801        $expected = array( 
     802            array( 
     803                'url'        => 'http://example.org/wp-content/uploads/' . $image['sizes']['medium']['file'], 
     804                'descriptor' => 'w', 
     805                'value'      => $image['sizes']['medium']['width'], 
     806            ), 
     807            array( 
     808                'url'        => 'http://example.org/wp-content/uploads/' . $image['sizes']['large']['file'], 
     809                'descriptor' => 'w', 
     810                'value'      => $image['sizes']['large']['width'], 
     811            ), 
     812            array( 
     813                'url'        => 'http://example.org/wp-content/uploads/' . $image['file'], 
     814                'descriptor' => 'w', 
     815                'value'      => $image['width'], 
     816            ), 
     817        ); 
     818 
     819        // Set up test cases for all expected size names and a random one. 
     820        $sizes = array( 'medium', 'large', 'full', 'yoav' ); 
     821 
     822        foreach ( $sizes as $size ) { 
     823            $this->assertSame( $expected, wp_get_attachment_image_srcset_array( $id, $size ) ); 
     824        } 
     825 
     826        // Leave the uploads option the way you found it. 
     827        update_option( 'uploads_use_yearmonth_folders', $uploads_use_yearmonth_folders ); 
     828    } 
     829 
     830    /** 
     831     * @ticket 33641 
     832     */ 
     833    function test_wp_get_attachment_image_srcset_array_with_edits() { 
     834        // Make an image. 
     835        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     836        $id = $this->factory->attachment->create_upload_object( $filename ); 
     837        // For this test we're going to mock metadata changes from an edit. 
     838        // Start by getting the attachment metadata. 
     839        $meta = wp_get_attachment_metadata( $id ); 
     840 
     841        // Copy hash generation method used in wp_save_image(). 
     842        $hash = 'e' . time() . rand(100, 999); 
     843 
     844        // Replace file paths for full and medium sizes with hashed versions. 
     845        $filename_base = basename( $meta['file'], '.png' ); 
     846        $meta['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['file'] ); 
     847        $meta['sizes']['medium']['file'] = str_replace( $filename_base, $filename_base . '-' . $hash, $meta['sizes']['medium']['file'] ); 
     848 
     849        // Save edited metadata. 
     850        wp_update_attachment_metadata( $id, $meta ); 
     851 
     852        // Get the edited image and observe that a hash was created. 
     853        $img_url = wp_get_attachment_url( $id ); 
     854 
     855        // Calculate a srcset array. 
     856        $sizes = wp_get_attachment_image_srcset_array( $id, 'medium' ); 
     857 
     858        // Test to confirm all sources in the array include the same edit hash. 
     859        foreach ( $sizes as $size ) { 
     860            $this->assertTrue( false !== strpos( $size['url'], $hash ) ); 
     861        } 
     862    } 
     863 
     864    /** 
     865     * @ticket 33641 
     866     */ 
     867    function test_wp_get_attachment_image_srcset_array_false() { 
     868        // Make an image. 
     869        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     870        $id = $this->factory->attachment->create_upload_object( $filename ); 
     871        $sizes = wp_get_attachment_image_srcset_array( 99999, 'foo' ); 
     872 
     873        // For canola.jpg we should return 
     874        $this->assertFalse( $sizes ); 
     875    } 
     876 
     877    /** 
     878     * @ticket 33641 
     879     */ 
     880    function test_wp_get_attachment_image_srcset_array_no_width() { 
     881        // Filter image_downsize() output. 
     882        add_filter( 'wp_generate_attachment_metadata', array( $this, '_test_wp_get_attachment_image_srcset_array_no_width_filter' ) ); 
     883 
     884        // Make our attachment. 
     885        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     886        $id = $this->factory->attachment->create_upload_object( $filename ); 
     887        $srcset = wp_get_attachment_image_srcset_array( $id, 'medium' ); 
     888 
     889        // The srcset should be false. 
     890        $this->assertFalse( $srcset ); 
     891 
     892        // Remove filter. 
     893        remove_filter( 'wp_generate_attachment_metadata', array( $this, '_test_wp_get_attachment_image_srcset_array_no_width_filter' ) ); 
     894    } 
     895 
     896    /** 
     897     * Helper function to filter image_downsize and return zero values for width and height. 
     898     */ 
     899    public function _test_wp_get_attachment_image_srcset_array_no_width_filter( $meta ) { 
     900        $meta['sizes']['medium']['width'] = 0; 
     901        $meta['sizes']['medium']['height'] = 0; 
     902        return $meta; 
     903    } 
     904 
     905    /** 
     906     * @ticket 33641 
     907     */ 
     908    function test_wp_get_attachment_image_srcset() { 
     909        // Make an image. 
     910        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     911        $id = $this->factory->attachment->create_upload_object( $filename ); 
     912        $sizes = wp_get_attachment_image_srcset( $id, 'full-size' ); 
     913 
     914        $image = wp_get_attachment_metadata( $id ); 
     915        $year_month = date('Y/m'); 
     916 
     917        $expected = 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' 
     918            . $image['sizes']['medium']['file'] . ' ' . $image['sizes']['medium']['width'] . 'w, '; 
     919        $expected .= 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' 
     920            . $image['sizes']['large']['file'] . ' ' . $image['sizes']['large']['width'] . 'w, '; 
     921        $expected .= 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; 
     922 
     923        $this->assertSame( $expected, $sizes ); 
     924    } 
     925 
     926    /** 
     927     * @ticket 33641 
     928     */ 
     929    function test_wp_get_attachment_image_srcset_single_srcset() { 
     930        // Make an image. 
     931        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     932        $id = $this->factory->attachment->create_upload_object( $filename ); 
     933        /* 
     934         * In our tests, thumbnails will only return a single srcset candidate, 
     935         * so we shouldn't return a srcset value in order to avoid unneeded markup. 
     936         */ 
     937        $sizes = wp_get_attachment_image_srcset( $id, 'thumbnail' ); 
     938 
     939        $this->assertFalse( $sizes ); 
     940    } 
     941 
     942    /** 
     943     * @ticket 33641 
     944     */ 
     945    function test_wp_get_attachment_image_sizes() { 
     946        // Make an image. 
     947        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     948        $id = $this->factory->attachment->create_upload_object( $filename ); 
     949 
     950 
     951        global $content_width; 
     952 
     953        // Test sizes against the default WP sizes. 
     954        $intermediates = array('thumbnail', 'medium', 'large'); 
     955 
     956        foreach( $intermediates as $int ) { 
     957            $width = get_option( $int . '_size_w' ); 
     958 
     959            // The sizes width gets constrained to $content_width by default. 
     960            if ( $content_width > 0 ) { 
     961                $width = ( $width > $content_width ) ? $content_width : $width; 
     962            } 
     963 
     964            $expected = '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px'; 
     965            $sizes = wp_get_attachment_image_sizes( $id, $int ); 
     966 
     967            $this->assertSame($expected, $sizes); 
     968        } 
     969    } 
     970 
     971    /** 
     972     * @ticket 33641 
     973     */ 
     974    function test_wp_get_attachment_image_sizes_with_args() { 
     975        // Make an image. 
     976        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     977        $id = $this->factory->attachment->create_upload_object( $filename ); 
     978 
     979 
     980        $args = array( 
     981            'sizes' => array( 
     982                array( 
     983                    'size_value'    => '10em', 
     984                    'mq_value'      => '60em', 
     985                    'mq_name'           => 'min-width' 
     986                ), 
     987                array( 
     988                    'size_value'    => '20em', 
     989                    'mq_value'      => '30em', 
     990                    'mq_name'           => 'min-width' 
     991                ), 
     992                array( 
     993                    'size_value'    => 'calc(100vm - 30px)' 
     994                ), 
     995            ) 
     996        ); 
     997 
     998        $expected = '(min-width: 60em) 10em, (min-width: 30em) 20em, calc(100vm - 30px)'; 
     999        $sizes = wp_get_attachment_image_sizes( $id, 'medium', $args ); 
     1000 
     1001        $this->assertSame($expected, $sizes); 
     1002    } 
     1003 
     1004    /** 
     1005     * @ticket 33641 
     1006     */ 
     1007    function test_wp_get_attachment_image_sizes_with_filtered_args() { 
     1008        // Add our test filter. 
     1009        add_filter( 'wp_image_sizes_args', array( $this, '_test_wp_image_sizes_args' ) ); 
     1010 
     1011        // Make an image. 
     1012        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     1013        $id = $this->factory->attachment->create_upload_object( $filename ); 
     1014 
     1015        $sizes = wp_get_attachment_image_sizes($id, 'medium'); 
     1016 
     1017        // Evaluate that the sizes returned is what we expected. 
     1018        $this->assertSame( $sizes, '100vm'); 
     1019 
     1020        remove_filter( 'wp_image_sizes_args', array( $this, '_test_wp_image_sizes_args' ) ); 
     1021    } 
     1022 
     1023    /** 
     1024     * A simple test filter for wp_get_attachment_image_sizes(). 
     1025     */ 
     1026    function _test_wp_image_sizes_args( $args ) { 
     1027        $args['sizes'] = "100vm"; 
     1028        return $args; 
     1029    } 
     1030 
     1031    /** 
     1032     * @ticket 33641 
     1033     */ 
     1034    function test_wp_make_content_images_responsive() { 
     1035        // Make an image. 
     1036        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     1037        $id = $this->factory->attachment->create_upload_object( $filename ); 
     1038 
     1039        $srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( $id, 'medium' ) ); 
     1040        $sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( $id, 'medium' ) ); 
     1041 
     1042        // Function used to build HTML for the editor. 
     1043        $img = get_image_tag( $id, '', '', '', 'medium' ); 
     1044        $img_no_size = str_replace( 'size-', '', $img ); 
     1045        $img_no_size_id = str_replace( 'wp-image-', 'id-', $img_no_size ); 
     1046 
     1047        // Manually add srcset and sizes to the markup from get_image_tag(); 
     1048        $respimg = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img); 
     1049        $respimg_no_size = preg_replace('|<img ([^>]+) />|', '<img $1 ' . $srcset . ' ' . $sizes . ' />', $img_no_size); 
     1050 
     1051        $content = '<p>Welcome to WordPress!  This post contains important information.  After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.</p> 
     1052            <p>First things first:</p> 
     1053 
     1054            %1$s 
     1055 
     1056            <ul> 
     1057            <li><a href="http://wordpress.org" title="Subscribe to the WordPress mailing list for Release Notifications">Subscribe to the WordPress mailing list for release notifications</a></li> 
     1058            </ul> 
     1059 
     1060            %2$s 
     1061 
     1062            <p>As a subscriber, you will receive an email every time an update is available (and only then).  This will make it easier to keep your site up to date, and secure from evildoers.<br /> 
     1063            When a new version is released, <a href="http://wordpress.org" title="If you are already logged in, this will take you directly to the Dashboard">log in to the Dashboard</a> and follow the instructions.<br /> 
     1064            Upgrading is a couple of clicks!</p> 
     1065 
     1066            %3$s 
     1067 
     1068            <p>Then you can start enjoying the WordPress experience:</p> 
     1069            <ul> 
     1070            <li>Edit your personal information at <a href="http://wordpress.org" title="Edit settings like your password, your display name and your contact information">Users &#8250; Your Profile</a></li> 
     1071            <li>Start publishing at <a href="http://wordpress.org" title="Create a new post">Posts &#8250; Add New</a> and at <a href="http://wordpress.org" title="Create a new page">Pages &#8250; Add New</a></li> 
     1072            <li>Browse and install plugins at <a href="http://wordpress.org" title="Browse and install plugins at the official WordPress repository directly from your Dashboard">Plugins &#8250; Add New</a></li> 
     1073            <li>Browse and install themes at <a href="http://wordpress.org" title="Browse and install themes at the official WordPress repository directly from your Dashboard">Appearance &#8250; Add New Themes</a></li> 
     1074            <li>Modify and prettify your website&#8217;s links at <a href="http://wordpress.org" title="For example, select a link structure like: http://example.com/1999/12/post-name">Settings &#8250; Permalinks</a></li> 
     1075            <li>Import content from another system or WordPress site at <a href="http://wordpress.org" title="WordPress comes with importers for the most common publishing systems">Tools &#8250; Import</a></li> 
     1076            <li>Find answers to your questions at the <a href="http://wordpress.orgs" title="The official WordPress documentation, maintained by the WordPress community">WordPress Codex</a></li> 
     1077            </ul>'; 
     1078 
     1079        $content_unfiltered = sprintf( $content, $img, $img_no_size, $img_no_size_id ); 
     1080        $content_filtered = sprintf( $content, $respimg, $respimg_no_size, $img_no_size_id ); 
     1081 
     1082        $this->assertSame( $content_filtered, wp_make_content_images_responsive( $content_unfiltered ) ); 
     1083    } 
     1084 
     1085    /** 
     1086     * @ticket 33641 
     1087     */ 
     1088    function test_wp_make_content_images_responsive_with_preexisting_srcset() { 
     1089        // Make an image. 
     1090        $filename = DIR_TESTDATA . '/images/test-image-large.png'; 
     1091        $id = $this->factory->attachment->create_upload_object( $filename ); 
     1092 
     1093        // Generate HTML and add a dummy srcset attribute. 
     1094        $image_html = get_image_tag( $id, '', '', '', 'medium' ); 
     1095        $image_html = preg_replace('|<img ([^>]+) />|', '<img $1 ' . 'srcset="image2x.jpg 2x" />', $image_html ); 
     1096 
     1097        // The content filter should return the image unchanged. 
     1098        $this->assertSame( $image_html, wp_make_content_images_responsive( $image_html ) ); 
     1099    } 
    7401100} 
  • trunk/tests/phpunit/tests/post/thumbnails.php

    r34664 r34855  
    1414        $this->post          = $this->factory->post->create_and_get(); 
    1515        $file                = DIR_TESTDATA . '/images/canola.jpg'; 
    16         $this->attachment_id = $this->factory->attachment->create_object( $file, $this->post->ID, array( 
     16        $this->attachment_id = $this->factory->attachment->create_upload_object( $file, $this->post->ID, array( 
    1717            'post_mime_type' => 'image/jpeg', 
    1818        ) ); 
  • trunk/tests/phpunit/tests/xmlrpc/mw/editPost.php

    r34682 r34855  
    127127        // create attachment 
    128128        $filename = ( DIR_TESTDATA.'/images/a2-small.jpg' ); 
    129         $contents = file_get_contents( $filename ); 
    130         $upload = wp_upload_bits( $filename, null, $contents ); 
    131         $this->assertTrue( empty( $upload['error'] ) ); 
    132  
    133         $attachment = array( 
    134             'post_title' => 'Post Thumbnail', 
    135             'post_type' => 'attachment', 
    136             'post_mime_type' => 'image/jpeg', 
    137             'guid' => $upload['url'] 
    138         ); 
    139         $attachment_id = wp_insert_attachment( $attachment, $upload['file'], $post_id ); 
     129        $attachment_id = $this->factory->attachment->create_upload_object( $filename, $post_id ); 
    140130 
    141131        // add post thumbnail to post that does not have one 
     
    152142 
    153143        // create another attachment 
    154         $attachment2 = array_merge( $attachment, array( 'title' => 'Post Thumbnail 2' ) ); 
    155         $attachment2_id = wp_insert_attachment( $attachment2, $upload['file'], $post_id ); 
     144        $attachment2_id = $this->factory->attachment->create_upload_object( $filename, $post_id ); 
    156145 
    157146        // change the post's post_thumbnail 
  • trunk/tests/phpunit/tests/xmlrpc/mw/getPost.php

    r25002 r34855  
    9696        // create attachment 
    9797        $filename = ( DIR_TESTDATA.'/images/a2-small.jpg' ); 
    98         $contents = file_get_contents( $filename ); 
    99         $upload = wp_upload_bits( $filename, null, $contents ); 
    100         $this->assertTrue( empty( $upload['error'] ) ); 
    101  
    102         $attachment = array( 
    103             'post_title' => 'Post Thumbnail', 
    104             'post_type' => 'attachment', 
    105             'post_mime_type' => 'image/jpeg', 
    106             'guid' => $upload['url'] 
    107         ); 
    108         $attachment_id = wp_insert_attachment( $attachment, $upload['file'], $this->post_id ); 
     98        $attachment_id = $this->factory->attachment->create_upload_object( $filename ); 
    10999 
    110100        set_post_thumbnail( $this->post_id, $attachment_id ); 
  • trunk/tests/phpunit/tests/xmlrpc/mw/getRecentPosts.php

    r25002 r34855  
    4040        $this->assertEquals( 401, $result->code ); 
    4141    } 
    42      
     42 
    4343    function test_no_editable_posts() { 
    4444        wp_delete_post( $this->post_id ); 
     
    101101        // create attachment 
    102102        $filename = ( DIR_TESTDATA.'/images/a2-small.jpg' ); 
    103         $contents = file_get_contents( $filename ); 
    104         $upload = wp_upload_bits( $filename, null, $contents ); 
    105         $this->assertTrue( empty( $upload['error'] ) ); 
    106  
    107         $attachment = array( 
    108             'post_title' => 'Post Thumbnail', 
    109             'post_type' => 'attachment', 
    110             'post_mime_type' => 'image/jpeg', 
    111             'guid' => $upload['url'] 
    112         ); 
    113         $attachment_id = wp_insert_attachment( $attachment, $upload['file'], $this->post_id ); 
     103        $attachment_id = $this->factory->attachment->create_upload_object( $filename, $this->post_id ); 
    114104        set_post_thumbnail( $this->post_id, $attachment_id ); 
    115105 
  • trunk/tests/phpunit/tests/xmlrpc/mw/newPost.php

    r34681 r34855  
    118118        // create attachment 
    119119        $filename = ( DIR_TESTDATA.'/images/a2-small.jpg' ); 
    120         $contents = file_get_contents( $filename ); 
    121         $upload = wp_upload_bits( $filename, null, $contents ); 
    122         $this->assertTrue( empty( $upload['error'] ) ); 
    123  
    124         $attachment = array( 
    125             'post_title' => 'Post Thumbnail', 
    126             'post_type' => 'attachment', 
    127             'post_mime_type' => 'image/jpeg', 
    128             'guid' => $upload['url'] 
    129         ); 
    130         $attachment_id = wp_insert_attachment( $attachment, $upload['file'] ); 
     120        $attachment_id = $this->factory->attachment->create_upload_object( $filename ); 
    131121 
    132122        $post = array( 'title' => 'Post Thumbnail Test', 'wp_post_thumbnail' => $attachment_id ); 
  • trunk/tests/phpunit/tests/xmlrpc/wp/editPost.php

    r31983 r34855  
    114114        $this->assertEquals( $editor_id, $out->post_author ); 
    115115    } 
    116      
     116 
    117117    function test_post_thumbnail() { 
    118118        add_theme_support( 'post-thumbnails' ); 
     
    127127        // create attachment 
    128128        $filename = ( DIR_TESTDATA.'/images/a2-small.jpg' ); 
    129         $contents = file_get_contents( $filename ); 
    130         $upload = wp_upload_bits( $filename, null, $contents ); 
    131         $this->assertTrue( empty( $upload['error'] ) ); 
    132  
    133         $attachment = array( 
    134             'post_title' => 'Post Thumbnail', 
    135             'post_type' => 'attachment', 
    136             'post_mime_type' => 'image/jpeg', 
    137             'guid' => $upload['url'] 
    138         ); 
    139         $attachment_id = wp_insert_attachment( $attachment, $upload['file'], $post_id ); 
     129        $attachment_id = $this->factory->attachment->create_upload_object( $filename, $post_id ); 
    140130 
    141131        // add post thumbnail to post that does not have one 
     
    159149 
    160150        // create another attachment 
    161         $attachment2 = array_merge( $attachment, array( 'post_title' => 'Post Thumbnail 2' ) ); 
    162         $attachment2_id = wp_insert_attachment( $attachment2, $upload['file'], $post_id ); 
     151        $attachment2_id = $this->factory->attachment->create_upload_object( $filename, $post_id ); 
    163152 
    164153        // change the post's post_thumbnail 
  • trunk/tests/phpunit/tests/xmlrpc/wp/newPost.php

    r34681 r34855  
    129129        // create attachment 
    130130        $filename = ( DIR_TESTDATA.'/images/a2-small.jpg' ); 
    131         $contents = file_get_contents( $filename ); 
    132         $upload = wp_upload_bits( $filename, null, $contents ); 
    133         $this->assertTrue( empty( $upload['error'] ) ); 
    134  
    135         $attachment = array( 
    136             'post_title' => 'Post Thumbnail', 
    137             'post_type' => 'attachment', 
    138             'post_mime_type' => 'image/jpeg', 
    139             'guid' => $upload['url'] 
    140         ); 
    141         $attachment_id = wp_insert_attachment( $attachment, $upload['file'] ); 
     131        $attachment_id = $this->factory->attachment->create_upload_object( $filename ); 
    142132 
    143133        $post = array( 'post_title' => 'Post Thumbnail Test', 'post_thumbnail' => $attachment_id ); 
Note: See TracChangeset for help on using the changeset viewer.