3

I created a taxonomy and attached it to the attachments or Media.

I am using Advanced Custom Fields to add a taxonomy field which is displayed as checkbox group. It adds the fields correctly to the attachments pages in both grid and list views.

In list view, the fields update properly but in the grid view, it only saves the last checkbox clicked. This is caused by some sort of onclick function which fires admin-ajax when each checkbox is clicked. Due to this it only sends the value of the box is clicked. I am trying to find a way to alter the js functions to add in checkbox groups and multi-select.

Update: I have tracked the function down to an onchange.

function: save

file: media-views.min.js

Code to replicate the issue:

Taxonomy:

function cptui_register_my_taxes() {
    $labels = array(
        "name" => __( "Image Tags", "" ),
        "singular_name" => __( "Image Tag", "" ),
    );
    $args = array(
        "label" => __( "Image Tags", "" ),
        "labels" => $labels,
        "public" => true,
        "hierarchical" => false,
        "label" => "Image Tags",
        "show_ui" => true,
        "show_in_menu" => true,
        "show_in_nav_menus" => false,
        "query_var" => true,
        "rewrite" => false,
        "show_admin_column" => false,
        "show_in_rest" => false,
        "rest_base" => "imagetags",
        "show_in_quick_edit" => false,
    );
    register_taxonomy( "imagetags", array( "attachment" ), $args );
}
add_action( 'init', 'cptui_register_my_taxes' );

Custom Fields:

if( function_exists('acf_add_local_field_group') ):
    acf_add_local_field_group(array(
        'key' => 'group_5bc3f242c39e3',
        'title' => 'Gallery - Image Tags',
        'fields' => array(
            array(
                'key' => 'field_5bc3f249f009c',
                'label' => 'Image Tags',
                'name' => 'new_image_tags',
                'type' => 'taxonomy',
                'instructions' => '',
                'required' => 0,
                'conditional_logic' => 0,
                'wrapper' => array(
                    'width' => '',
                    'class' => '',
                    'id' => '',
                ),
                'taxonomy' => 'imagetags',
                'field_type' => 'checkbox',
                'add_term' => 1,
                'save_terms' => 1,
                'load_terms' => 1,
                'return_format' => 'id',
                'multiple' => 0,
                'allow_null' => 0,
            ),
        ),
        'location' => array(
            array(
                array(
                    'param' => 'attachment',
                    'operator' => '==',
                    'value' => 'all',
                ),
            ),
        ),
        'menu_order' => 0,
        'position' => 'normal',
        'style' => 'default',
        'label_placement' => 'top',
        'instruction_placement' => 'label',
        'hide_on_screen' => '',
        'active' => 1,
        'description' => '',
    ));

endif;

You can also remove the default taxonomy field if you want with the following code. It does not effect the code but removes the text field:

// Remove taxonomy from the attachment pages so the acf taxonomy can work
add_action( 'admin_menu', function (){ remove_meta_box('imagetagsdiv', 'attachment', 'side' ); } );

// Add this in to remove it from the popup editor
add_filter( 'attachment_fields_to_edit', function( $fields ){
    unset($fields['imagetags']); 
    return $fields;
} );

Problem Found Now to find a solution

Open wp-includes/js/media-views.js - Line: 8353

_.each( this.$el.serializeArray(), function( pair ) {
    data[ pair.name ] = pair.value;
});

It appears they used this code to prevent duplicates but in effect it is deleting everything that can be submitted as an array.

Note: This is the full file WP loads the .min version by default

James
  • 702
  • 2
  • 15
  • 39
  • Can you post some code to replicate the issue? – Outsource WordPress Oct 18 '18 at 17:14
  • I just added the taxonomy and custom field – James Oct 19 '18 at 00:41
  • Can you post even more - I feel we are still missing some context. The more, the better – JJ Rohrer Oct 19 '18 at 01:58
  • That is all the code I have installed. Install ACF put the above code in functions.php. Create two or more image tags by mousing over media and clicking on image tags. Go to media->click on an image in grid mode-> scroll down to see the check boxes on the right and try to click more than one. refresh the page and see that only one saved. – James Oct 19 '18 at 10:37
  • 1
    I found what is causing the issue.... Now I need help finding a solution. I updated my submission to show the js problem. – James Oct 19 '18 at 11:07

1 Answers1

4

The issue can be fixed by overriding the prototype function (wp.media.view.AttachmentCompat.prototype.save()), like so:

add_action( 'admin_print_footer_scripts', 'so52810006', 11 );
function so52810006() {
    // Make sure the media-views script has been enqueued.
    if ( ! did_action( 'wp_enqueue_media' ) ) {
        return;
    }
    ?>
<script>
wp.media.view.AttachmentCompat.prototype.save = function( event ) {
    var data = {};

    if ( event ) {
        event.preventDefault();
    }

    _.each( this.$el.serializeArray(), function( pair ) {
        if ( /\[\]$/.test( pair.name ) ) {
            if ( undefined === data[ pair.name ] ) {
                data[ pair.name ] = [];
            }
            data[ pair.name ].push( pair.value );
        } else {
            data[ pair.name ] = pair.value;
        }
    });

    this.controller.trigger( 'attachment:compat:waiting', ['waiting'] );
    this.model.saveCompat( data ).always( _.bind( this.postSave, this ) );
};
</script>
    <?php
}

As you can see, the main part is here, which properly builds the data by taking into account the field name which is pair.name — i.e. whether it ends with a [] which indicates an array value.

_.each( this.$el.serializeArray(), function( pair ) {
    if ( /\[\]$/.test( pair.name ) ) {
        if ( undefined === data[ pair.name ] ) {
            data[ pair.name ] = [];
        }
        data[ pair.name ].push( pair.value );
    } else {
        data[ pair.name ] = pair.value;
    }
});

Tried and tested working on WordPress 4.9.8 and ACF 5.7.7.

Sally CJ
  • 15,362
  • 2
  • 16
  • 34
  • Beautiful... I have been stuck on this for days. – James Oct 19 '18 at 13:12
  • Yep, things like this can really make us go crazy.. esp. when there's no documentation on where or what to edit in the WordPress media scripts. Btw, note that WordPress may change the code in future WordPress (stable) releases, so make sure to check the original prototype function code - and copy then edit the code accordingly. Also, you can actually disable the "Image Tags" standard meta box by setting [`meta_box_cb`](https://developer.wordpress.org/reference/functions/register_taxonomy/#parameters) to `false` when you register the custom taxonomy. – Sally CJ Oct 19 '18 at 13:33
  • 1
    Thanks for the suggestion. I also went on github and forked the file for modification based on your code. Hopefully they will include it in a future update so we can all use it. – James Oct 19 '18 at 13:48
  • You're welcome. And yes, hopefully. :) Also, we could actually use jQuery to change the fields `name` to using `[0]`, `[1]`, etc. instead of just `[]` (so that the name would be *unique*), but (I think) it's not very ideal compared to what I've suggested in my answer. (i.e. changing the `data`) – Sally CJ Oct 19 '18 at 14:05
  • 1
    You can change checkboxes but your answer will also work for multi-select fields which I noticed many plugins were trying to use but failed. – James Oct 19 '18 at 14:07
  • I totally agree with that. ;) Cheers! – Sally CJ Oct 19 '18 at 14:13