4.1 (WP Bileti # 18532) 'de görüntü ölçeklendirme (yuvarlama) değişikliği ile ilgili sorunları ele alın


17

Şu anda site içeriğini eski bir 4.1 öncesi siteden yeni bir düzene geçirme ve # 18532'nin yuvarlama hatası sorunu ve ilgili düzeltmeyle ilgili bir sorunla karşılaşıyorum .

Özetlemek gerekirse, WordPress'in yan tarafındaki uzun soluklu bir yanlış davranış düzeltildi:

693x173 boyutunda bir resim yüklediğimizi ve 300 genişliğe ölçeklendirdiğimizi düşünün:

  • öncesi 4.1: 300x74
  • 4.1 sonrası: 300x75

Sorun

Genellikle bu, mevcut dosyalar ve <img>dokunulmadığı için herhangi bir soruna neden olmaz.

Bir WXR dosyasından başparmak veya ithal ekleri yenileyici Ama hepsi bırakarak dosya sisteminde farklı hazırlanmayacağından <img>içinde post_contentöldü.

Bir çözüm mü arıyorsunuz?

Çeşitli çözümler düşünüyordum:

Kötü eski zamanlara geri dönmek

Changeset 30660 , 4.1'denwp_constrain_dimensions önceki eski davranışı takmak için kullanılabilecek yeni bir filtre tanıttı . Bu sorunu çözüyor . Ama bunun daha sonra sorunlara neden olup olmayacağını merak ediyorum ve genellikle düzeltmeyi yapmak istiyorum, bu yüzden bu işe rağmen ben ideal olmadığını düşünürdüm.

Değişen Zamanlar '

Yani bu bizi başka bir hedefle bırakıyor: DB'yi temizleyin ve eski dosyalara yapılan tüm referansları yeni dosyalara yapılan referanslarla değiştirin. Şimdi burada sorduğum soru bunu nasıl yapacağım. Bu sorunun çok sayıda insanı etkilediğinden ve etkileyeceğinden şüphelendiğim için etkili ve genel olarak uygulanabilir bir çözüm arıyorum

Şu anki fikrim şudur:

  1. İçe aktarın, yeniden oluşturun veya yeni dosyalar ve bozuk etiketlerle bizi terk eden her şey.
  2. Dosya sistemindeki tüm yeniden boyutlandırılmış dosyalardan A listesi oluşturun veya alternatif olarak bu bilgileri veritabanından alın
  3. Bu listeyi ayrıştırın ve 4.1'den önce göründüğü gibi bir piksel ile kaydırılmış dosya adlarıyla ikinci bir B listesi oluşturun
  4. B'nin tüm oluşumlarını A'daki ilgili girişle değiştirerek tüm veritabanını araştırın ve değiştirin

Bunun bu durumu ele almanın en akıllı ve verimli yolu olup olmadığından emin değilim. Ayrıca biraz fazla kaba kuvvet hissediyor. Bu yüzden uygulamadan önce sadece WPSE kalabalığının sonsuz bilgeliğini kontrol etmek istedim;)

[değiştir] ck-macleod s cevap okuduktan (teşekkürler!) Ben bir düzeltme bunu bir kez ve herkes için çözmesi gerektiğini düşünüyorum, bu yüzden sürekli bu sorunu kafanın arkasında tutmak gerekmez. [/Düzenle]

Trac'de ilgili bir bilet buldum . Referans için ekleniyor. [/ edit2]


Eğer nereye error issue of #13852bunu mu demek istediniz #18532? :)
Aravona

2
Hata! Evet, sabit. :)
Kraft

Yanıtlar:


4

Bu, ithalatçı ile içerik içe aktarırken çalışan ve URL'leri bir kez ve herkes için sabitleyen diğer yanıttan başka bir yaklaşımdır . Tekrar: Bu savaşta test edilmedi, ancak çözdüğüm çözüm ve işe yaradı.

Sorunu bir kez ve herkes için çözdüğü ve işe yaraması durumunda işe yaradığı için bunu tercih ederim. DB kırık şeyler bırakarak ve ekranda düzeltmek değil gibi daha sonra kırma şeyler hakkında endişelenmenize gerek yok.

/*
Plugin Name:  Bugfix Ticket 31581 for WP_Importer
Plugin URI:   /wordpress//a/206992/47733
Description:  Fixes image references after post WP 4.1 image scaling change in post content when using the WP_Importer  (see https://core.trac.wordpress.org/ticket/31581)
Version:      0.0.1
*/

class Bugfix31581WPImporter {

    protected $remaps;

    /**
     * Initialize class, mainly setting up hooks
     */
    public function init(){

        if (WP_IMPORTING === true){

            $this->remaps = array();

            //This hook is chosen because it is pretty close to where the actual attachment import is happening.
            //TODO: May be reconsidered.
            add_filter('wp_update_attachment_metadata', array($this, 'collectRemaps'), 10 , 2);

            add_action('import_end', array($this, 'remap'));
            add_action('import_end', array($this, 'importEnded'));
        }

    }

    /**
     * Cleans up hooks after the import has ended.
     */
    public function importEnded(){
        remove_filter('wp_update_attachment_metadata', array($this, 'collectRemaps'), 10);

        remove_action('import_end', array($this, 'remap'), 10);
        remove_action('import_end', array($this, 'importEnded'), 10);
    }

    /**
     * When an attachment is added compare the resulting sizes with the sizes from the legacy algorithm and setup remap.
     *
     * @param $data
     * @param $post_id
     *
     * @return mixed
     */
    public function collectRemaps($data, $post_id ){

        $intermediate_sizes = $this->getIntermediateSizes();

        if(empty($data) || !array_key_exists('sizes', $data)){
            return $data;
        }

        foreach($data['sizes'] as $key => $size){

            $size_new_algorithm = array($size['width'], $size['height']);

            $dest_w = $intermediate_sizes[$key]['width'];
            $dest_h = $intermediate_sizes[$key]['height'];
            $crop = $intermediate_sizes[$key]['crop'];

            add_filter('wp_constrain_dimensions', array($this, 'legacy_wp_constrain_dimensions'), 10, 5);

            $size_old_algorithm = image_resize_dimensions($data['width'], $data['height'], $dest_w, $dest_h, $crop);

            //Bail out in the rare case of `image_resize_dimensions` returning false
            if($size_old_algorithm===false){
                continue;
            }

            $size_old_algorithm = array($size_old_algorithm[4], $size_old_algorithm[5]);

            remove_filter('wp_constrain_dimensions', array($this, 'legacy_wp_constrain_dimensions'), 10);

            // Compare the current size with the calculation of the old algorithm...
            $diff = array_diff($size_new_algorithm, $size_old_algorithm);

            // ...to detect any mismatches
            if(!empty($diff)){

                $oldFilename = $this->getOldFilename($size['file'], $size_old_algorithm);

                // If getting the old filename didn't work for some reason (probably other filename-structure) bail out.
                if($oldFilename===false){
                    continue;
                }

                if(!array_key_exists($post_id, $this->remaps)){
                    $this->remaps[$post_id] = array();
                }

                $this->remaps[$post_id][$size['file']] = array(
                    'file' => $oldFilename,
                    'size' => $key
                );
            }

        }

        return $data;
    }

    /**
     * Get resize settings for all image sizes
     *
     * Taken from wp_generate_attachment_metadata() in includes/image.php
     *
     * @return array
     */
    public function getIntermediateSizes(){

        global $_wp_additional_image_sizes;

        $sizes = array();
        foreach ( get_intermediate_image_sizes() as $s ) {
            $sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false );
            if ( isset( $_wp_additional_image_sizes[$s]['width'] ) )
                $sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
            else
                $sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
            if ( isset( $_wp_additional_image_sizes[$s]['height'] ) )
                $sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
            else
                $sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
            if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) )
                $sizes[$s]['crop'] = $_wp_additional_image_sizes[$s]['crop']; // For theme-added sizes
            else
                $sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
        }

        return $sizes;
    }

    /**
     * Turn the new filename into the old filename reducing the height by one
     *
     * @param $newFilename
     * @param $size
     *
     * @return mixed
     */
    public function getOldFilename($newFilename, $size){

        $dimensions = array();

        $filetypes = $this->getAllowedImageExtentions();

        // TODO: This pattern can be different. See `image_make_intermediate_size` in image editor implementation.
        $matchFiles = '/([0-9]{1,5})x([0-9]{1,5}).(' . $filetypes . ')$/';

        // Extract the dimensions
        preg_match($matchFiles,$newFilename,$dimensions);

        // If the file URL doesn't allow guessing the dimensions bail out.
        if(empty($dimensions)){
            return $newFilename;
        }

        $newStub = $dimensions[1] . 'x' . $dimensions[2] . '.' . $dimensions[3];

        $oldStub = $size[0] . 'x' . $size[1] . '.' . $dimensions[3];

        $oldFilename = str_replace($newStub,$oldStub,$newFilename);

        return $oldFilename;
    }

    /**
     * Extract all file extensions that match an image/* mime type
     *
     * @return string
     */
    protected function getAllowedImageExtentions(){
        $allowed_filetypes = get_allowed_mime_types();

        $allowed_images = array();

        foreach($allowed_filetypes as $extensions => $mimetype){
            if( substr($mimetype,0,6) == 'image/' ){
                $allowed_images[] = $extensions;
            }
        }

        return implode('|',$allowed_images);
    }


    /**
     * This is the heart of this class. Based on the collected remaps from earlier it does a S&R on the DB.
     */
    public function remap(){

        global $wpdb;

        foreach($this->remaps as $attachment_id => $replaces){

            foreach($replaces as $new_url => $old_data){

                $to_url = wp_get_attachment_image_src($attachment_id,$old_data['size']);
                $to_url = $to_url[0];

                $from_url = str_replace($new_url, $old_data['file'], $to_url);

                // remap urls in post_content
                $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) );

                //TODO: This is disabled as enclosures can't be images, right?
                // remap enclosure urls
                //$result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) );

            }

        }

    }

    /**
     * This is a copy of the legacy pre 4.1 wp_constrain_dimensions()
     *
     * @param $dimensions
     * @param $current_width
     * @param $current_height
     * @param $max_width
     * @param $max_height
     *
     * @return array
     */
    public function legacy_wp_constrain_dimensions($dimensions, $current_width, $current_height, $max_width, $max_height){
        if ( !$max_width and !$max_height )
            return array( $current_width, $current_height );

        $width_ratio = $height_ratio = 1.0;
        $did_width = $did_height = false;

        if ( $max_width > 0 && $current_width > 0 && $current_width > $max_width ) {
            $width_ratio = $max_width / $current_width;
            $did_width = true;
        }

        if ( $max_height > 0 && $current_height > 0 && $current_height > $max_height ) {
            $height_ratio = $max_height / $current_height;
            $did_height = true;
        }

        // Calculate the larger/smaller ratios
        $smaller_ratio = min( $width_ratio, $height_ratio );
        $larger_ratio  = max( $width_ratio, $height_ratio );

        if ( intval( $current_width * $larger_ratio ) > $max_width || intval( $current_height * $larger_ratio ) > $max_height )
            // The larger ratio is too big. It would result in an overflow.
            $ratio = $smaller_ratio;
        else
            // The larger ratio fits, and is likely to be a more "snug" fit.
            $ratio = $larger_ratio;

        // Very small dimensions may result in 0, 1 should be the minimum.
        $w = max ( 1, intval( $current_width  * $ratio ) );
        $h = max ( 1, intval( $current_height * $ratio ) );

        // Sometimes, due to rounding, we'll end up with a result like this: 465x700 in a 177x177 box is 117x176... a pixel short
        // We also have issues with recursive calls resulting in an ever-changing result. Constraining to the result of a constraint should yield the original result.
        // Thus we look for dimensions that are one pixel shy of the max value and bump them up
        if ( $did_width && $w == $max_width - 1 )
            $w = $max_width; // Round it up
        if ( $did_height && $h == $max_height - 1 )
            $h = $max_height; // Round it up

        return array( $w, $h );
    }

}

add_filter('import_start',array(new Bugfix31581WPImporter(),'init'));

+1 iyi iş.
gmazzap

1

Büyük bir sitedeki TÜM görüntü dosyaları (ve bağlantılar) için sorunu küresel ve mükemmel bir şekilde çözmek - örneğin, bireylerin WP stilini ve diğer garip varyasyonları manuel olarak taklit eden görüntü dosyalarını zaman zaman yeniden adlandırmış olma olasılığı zor olabilir. Veritabanı arama ve değiştirme işlemleri de komplikasyonları (ve riskleri!) İçerir.

Sanırım hataların büyük çoğunluğunu (kırık görüntüler ve kırık görüntü bağlantıları) ele alabilir ve aşağıdaki yöntemle istenen sonucu veya makul faksı elde edebilir misiniz?

  1. Tüm yeniden boyutlandırılan görüntülerin yeni "yuvarlak" yöntem yerine eski "aralık" yöntemi ile yeniden boyutlandırıldığı tarihi belirleyin. (Farklı bir kesme türü de oluşturulabilir, ancak tarih en kolay görünüyor.)

  2. <= Kesme tarihi yayınlanan tüm gönderiler için, yükleme / oluşturma zamanında preg_replace () öğesini çalıştırın, sorunlu desen veya desenlere sahip tüm görüntü dosyalarını yakalayın ve bunları istenen desenle değiştirin. Veritabanı değiştirilmeden kalır, ancak çoğu durumda çıktı hatasız olur. Çözümün hem "tekil" sayfa yayın içeriğine, hem de arşiv sayfalarına ve diğer işlemlere uygulanması gerekip gerekmediğinden emin değilim.

Bu tür bir çözüm yararlı olursa, bir sonraki soru, sorunlu kalıpların ve değiştirmelerin yeterince tanımlanıp tanımlanamayacağı olacaktır. Önerilen çözümler listenizden, muhtemelen birkaç tipik paternin izole edilebileceği anlaşılmaktadır (belki de küçük resimler ve diğer bazı görüntüler üreten önceki ortam ayarlarından alınmıştır).

Zaten kullandığım daha basit bir işlev yazdım (ve bir eklentiye dönüşme sürecindeyim), genel olarak belirli bir tarihe kadar, belirli bir tarihe kadar, varsayılan bir görüntü veya görüntü bağlantısıyla, Yukarıda açıklanan yönteme göre. Bu, aşırı telif hakkı uyarısında, operatörlerin, eski sayfalarda çirkin sonuçlar üretmenin yanı sıra, her biri iki tane olmak üzere binlerce hata çıkardıklarından habersiz, tüm görüntülerini sildikleri bir siteydi. görüntüsü.

Sorun desenini daha spesifik olarak daraltabiliyorsanız ve çıktının değiştirilmesi gereken durumlar varsa, bunu biçimime takmayı görebilirsiniz - ki bu çok karmaşık değil ve hangisinden daha iyi bir RegExer için hatta kolay olabilir. Öte yandan, bu yaklaşım sizin için soruna cevap vermezse, zamanınızı veya zamanımı boşa harcamak istemem.


Bu konuyla ilgilendiğiniz için teşekkürler! Sadece bazı düşünceler: Bence DB yanlış veri ve sadece ekranda maymun yaması çok temiz ve sürdürülebilir bir çözüm değildir. Her zaman kırılabilir ve her görünümde performansa zarar verebilir. Ayrıca, içeriği başka bir şekilde ayrıştıran veya değiştiren diğer eklentiler için öngörülemeyen yan etkilere de sahip olabilir. Nasıl yapıldığına bağlı olarak görüntüler hala arka uçta kırık. Bu durumda sadece wp_constrain_dimensionsiçe aktarma yaparken başparmak ölçekleme sıfırlama ve başparmak yeniden oluşturma kaçınmak daha temiz olacağını düşünüyorum.
kraftner

Hiç önemli değil. Şey, DB veri yanlış veri değil, sadece yeni rejim altında artık istediğiniz veri değil. Performans isabetinde, muhtemelen teorik olarak X tarihinden önceki gönderiler için geçerli olduğundan, muhtemelen en az olacağını düşünüyorum. Daha genel olarak, tek bedene uyan en iyi çözüm olmayabilir: Bence iyi yeterli çözüm, sitenin karakterine, geçmiş görüntü işleme uygulamalarına ve alışkanlıklarına, DB'nin büyüklüğüne, pratik ve zaman kısıtlamalarına vb.
CK MacLeod

Muhtemelen, tek bedene uyan bir çözüm olmayacaksınız. Şu anda bunu sizin içinkine benzer bir renderleme yaklaşımı ve bunu bir kez ve herkes için çözdüğü için tercih ettiğim bir ithalatçı yaklaşım olan çeşitli kullanım yollarını araştırıyorum. Bunun nereye gittiğini göreceğiz. :)
kraftner

1

Tamam, bu kırık görüntüleri anında değiştirmek için temel bir yaklaşımdır. Bunun savaşta test edilmiş bir çözümden çok bir kavram kanıtı olduğunu unutmayın. Sadece the_contentbazı durumlarda istenmeyen bazı yan etkilere neden olabilecek (muhtemelen) filtreyi kancalar . Dikkatli tutun. :)

Her ne kadar kodda çok söylese de ben de benim kod kullanılan bu cevap için @Rarst kredi istiyorum .

/*
Plugin Name:  Bugfix 31581 Live
Plugin URI:   /wordpress//a/206986/47733
Description:  Fixes image references in post content after post 4.1 image scaling change (see https://core.trac.wordpress.org/ticket/31581)
Version:      0.0.1
*/

class Bugfix31581Live {

    protected $matchURLs;
    protected $matchFiles;

    protected $remaps;

    public function init(){

        $filetypes = $this->get_allowed_image_extentions();

        $baseurl = wp_upload_dir();
        $baseurl = preg_quote($baseurl['baseurl'], '/');

        $this->matchURLs = '/' . $baseurl . '\/.??([a-zA-Z0-9_-]*?\.(?:' . $filetypes . '))/';

        //TODO: This pattern can be different. See `image_make_intermediate_size` in image editor implementation
        $this->matchFiles = '/([0-9]{1,4})x([0-9]{1,4}).(' . $filetypes . ')$/';

        add_filter('the_content', array($this, 'update_urls') );
    }

    public function update_urls($content){

        $urls = array();

        preg_match_all($this->matchURLs,$content,$urls);

        // Bail out early if we don't have any match.
        if($urls === false || empty($urls[0])){
            return $content;
        }

        // Loop through all matches
        foreach($urls[0] as $url){

            // Try to resolve this URL to an attachment ID
            $id = $this->get_attachment_id($url);

            // If not  let's see if this might be a URL that has been broken by our beloved Changeset 30660
            if( $id === false ){

                $dimensions = array();

                // Extract the dimensions
                preg_match($this->matchFiles,$url,$dimensions);

                // If the file URL doesn't allow guessing the dimensions bail out.
                if(empty($dimensions)){
                    continue;
                }

                // Old filename
                $old = $dimensions[1] . 'x' . $dimensions[2] . '.' . $dimensions[3];

                // New filename (not sure if this exists yet)
                $new = $dimensions[1] . 'x' . ($dimensions[2]+1) . '.' . $dimensions[3];

                // Build the new URL (not sure if this exists yet)
                $new_url = str_replace($old,$new,$url);

                // Try to get the attachment with the new url
                $id = $this->get_attachment_id($new_url);

                // Bad luck. This also doesn't exist.
                if( $id === false ) {
                    continue;
                }

                // Just to be sure everything is in sync we get the URL built from id and size.
                $db_url = wp_get_attachment_image_src($id,array($dimensions[1], $dimensions[2]+1));

                // Check if the URL we created and the one wp_get_attachment_image_src builds are the same.
                if($new_url === $db_url[0]){

                    // Awesome let's replace the broken URL.
                    $content = str_replace($url,$new_url,$content);
                }

            }

        }

        return $content;
    }

    /**
     * Get the Attachment ID for a given image URL.
     *
     * @link   /wordpress//a/7094
     *
     * @param  string $url
     *
     * @return boolean|integer
     */
    protected function get_attachment_id( $url ) {

        $dir = wp_upload_dir();

        // baseurl never has a trailing slash
        if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
            // URL points to a place outside of upload directory
            return false;
        }

        $file  = basename( $url );
        $query = array(
            'post_type'  => 'attachment',
            'fields'     => 'ids',
            'meta_query' => array(
                array(
                    'value'   => $file,
                    'compare' => 'LIKE',
                ),
            )
        );

        $query['meta_query'][0]['key'] = '_wp_attached_file';

        // query attachments
        $ids = get_posts( $query );

        if ( ! empty( $ids ) ) {

            foreach ( $ids as $id ) {

                $tmp = wp_get_attachment_image_src( $id, 'full' );

                // first entry of returned array is the URL
                if ( $url === array_shift( $tmp ) )
                    return $id;
            }
        }

        $query['meta_query'][0]['key'] = '_wp_attachment_metadata';

        // query attachments again
        $ids = get_posts( $query );

        if ( empty( $ids) )
            return false;

        foreach ( $ids as $id ) {

            $meta = wp_get_attachment_metadata( $id );

            foreach ( $meta['sizes'] as $size => $values ) {

                $tmp = wp_get_attachment_image_src( $id, $size );

                if ( $values['file'] === $file && $url === array_shift( $tmp ) )
                    return $id;
            }
        }

        return false;
    }

    protected function get_allowed_image_extentions(){
        $allowed_filetypes = get_allowed_mime_types();

        $allowed_images = array();

        foreach($allowed_filetypes as $extensions => $mimetype){
            if( substr($mimetype,0,6) == 'image/' ){
                $allowed_images[] = $extensions;
            }
        }

        return implode('|',$allowed_images);
    }

}

add_filter('init',array(new Bugfix31581Live(),'init'));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.