3

Is there a hook for the popup window which shows up when you click on the edit button on an image in a post?

6
  • It might be better to upgrade to 3.5 before asking media UI questions. This version will be released in some days and massively change everything.
    – kaiser
    Dec 3, 2012 at 14:53
  • Thank you for the comment but unfortunately I need that now. But I'll have a look at 3.5
    – Stephan S.
    Dec 3, 2012 at 14:55
  • Then just update to 3.5 RC1. Will simplify your life a lot.
    – kaiser
    Dec 3, 2012 at 14:57
  • Considering we're far past 3.5 at this stage, can somebody answer this for the current version of WordPress?
    – aendra
    Aug 6, 2014 at 10:18
  • The one to edit an image, correct? Aug 6, 2014 at 13:46

2 Answers 2

2

The answer is there is no damn hook for the edit button.

It's just a bunch of JS contained within wp-includes/js/tinymce/plugins/wpeditimage/plugin.js.

I've included the barebones of what you need below. Key points:

a. Clicking an element with a data-wp-imgselect attribute will open the image edit dialogue. You need to change that to something else if you don't want that to happen (data-wp-chartselect in my example).

b. A good way of preventing wpeditimage from conflicting is to give whatever element you're editing a mceItem class. This will make WordPress think it's a placeholder and thus not select it.

c. The bit you can't see is my Angular app loaded from the datacharts.cb_url global. I have a button in that which does the following when clicked:

parent.tinymce.activeEditor.insertContent('<div class="mceNonEditable"><img src="' + angular.element('.savePNG').attr('href') + '" data-llama=\'' + window.btoa(angular.toJson(scope.config)) + '\' class="mceItem" /></div><br />');

parent.tinymce.activeEditor.windowManager.close();

The key thing here is that it creates an image with a data attribute (data-llama) containing a Base64-encoded representation of my chart configuration. This is decoded and deserialised when it gets passed back to my Angular app via TinyMCE, which is then used to populate the chart. I'm open-sourcing my entire codebase and will link to it here once I've done so, in case you want to see a complete implementation.

Without any further ado, here's my TinyMCE plugin:

/**
 *  datacharts TinyMCE plugin
 */

tinymce.PluginManager.add('datacharts', function(editor, url) {
  var toolbarActive = false;

  // Add a button that opens a window. This is just the toolbar.
  editor.addButton('datacharts', {
    text: false,
    icon: 'icon dashicons-chart-area',
    onclick: function() {
      // Open window
      editor.windowManager.open({
        title: 'datacharts',
        width: jQuery(window).width() - 100,
        height: jQuery(window).height() - 100,
        url: datacharts.cb_url,
        buttons: [
          {
            text: 'Cancel',
            onclick: 'close'
          }
        ]
      });
    }
  });

  function editImage( img ) {
    // Open window
    editor.windowManager.open({
      title: 'datacharts',
      width: jQuery(window).width() - 100,
      height: jQuery(window).height() - 100,
      url: datacharts.cb_url,
      buttons:
        [
          {
            text: 'Cancel',
            onclick: 'close'
          }
        ]
      },
      { // This object is passed to the receiving URL via parent.tinymce.activeEditor.windowManager.getParams()
        llama: img.dataset.llama
      }
    );
  }

  // Remove the element if the "delete" button is clicked.
  function removeImage( node ) {
    var wrap;

    if ( node.nodeName === 'DIV' && editor.dom.hasClass( node, 'mceTemp' ) ) {
      wrap = node;
    } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) {
      wrap = editor.dom.getParent( node, 'div.mceTemp' );
    }

    if ( wrap ) {
      if ( wrap.nextSibling ) {
        editor.selection.select( wrap.nextSibling );
      } else if ( wrap.previousSibling ) {
        editor.selection.select( wrap.previousSibling );
      } else {
        editor.selection.select( wrap.parentNode );
      }

      editor.selection.collapse( true );
      editor.nodeChanged();
      editor.dom.remove( wrap );
    } else {
      editor.dom.remove( node );
    }
    removeToolbar();
  }

  // This adds the "edit" and "delete" buttons.
  function addToolbar( node ) {
    var rectangle, toolbarHtml, toolbar, left,
      dom = editor.dom;

    removeToolbar();

    // Don't add to placeholders
    if ( ! node || node.nodeName !== 'IMG' || isPlaceholder( node ) ) {
      return;
    }

    dom.setAttrib( node, 'data-wp-chartselect', 1 );
    rectangle = dom.getRect( node );

    toolbarHtml = '<div class="dashicons dashicons-edit edit" data-mce-bogus="1"></div>' +
      '<div class="dashicons dashicons-no-alt remove" data-mce-bogus="1"></div>';

    toolbar = dom.create( 'div', {
      'id': 'wp-image-toolbar',
      'data-mce-bogus': '1',
      'contenteditable': false
    }, toolbarHtml );

    if ( editor.rtl ) {
      left = rectangle.x + rectangle.w - 82;
    } else {
      left = rectangle.x;
    }

    editor.getBody().appendChild( toolbar );
    dom.setStyles( toolbar, {
      top: rectangle.y,
      left: left
    });

    toolbarActive = true;
  }

  // This removes the edit and delete buttons.
  function removeToolbar() {
    var toolbar = editor.dom.get( 'wp-image-toolbar' );

    if ( toolbar ) {
      editor.dom.remove( toolbar );
    }

    editor.dom.setAttrib( editor.dom.select( 'img[data-wp-chartselect]' ), 'data-wp-chartselect', null );

    toolbarActive = false;
  }

  function isPlaceholder( node ) {
    var dom = editor.dom;

    if ( /*dom.hasClass( node, 'mceItem' ) ||*/ dom.getAttrib( node, 'data-mce-placeholder' ) ||
      dom.getAttrib( node, 'data-mce-object' ) ) {

      return true;
    }

    return false;
  }

  editor.on( 'mousedown', function( event ) {
    if ( editor.dom.getParent( event.target, '#wp-image-toolbar' ) ) {
      if ( tinymce.Env.ie ) {
        // Stop IE > 8 from making the wrapper resizable on mousedown
        event.preventDefault();
      }
    } else if ( event.target.nodeName !== 'IMG' ) {
      removeToolbar();
    }
  });

  editor.on( 'mouseup', function( event ) {
    var image,
      node = event.target,
      dom = editor.dom;

    // Don't trigger on right-click
    if ( event.button && event.button > 1 ) {
      return;
    }

    if ( node.nodeName === 'DIV' && dom.getParent( node, '#wp-image-toolbar' ) ) {
      image = dom.select( 'img[data-wp-chartselect]' )[0];

      if ( image ) {
        editor.selection.select( image );
        if ( dom.hasClass( node, 'remove' ) ) {
          removeImage( image );
        } else if ( dom.hasClass( node, 'edit' ) ) {
          editImage( image );
        }
      }
    } else if ( node.nodeName === 'IMG' && ! editor.dom.getAttrib( node, 'data-wp-chartselect' ) && ! isPlaceholder( node ) ) {
      addToolbar( node );
    } else if ( node.nodeName !== 'IMG' ) {
      removeToolbar();
    }
  });

  editor.on( 'cut', function() {
    removeToolbar();
  });


  // This might not be needed, not sure what it does.
  editor.on( 'PostProcess', function( event ) {
    if ( event.get ) {
      event.content = event.content.replace( / data-wp-chartselect="1"/g, '' );
    }
  });

});
1
  • I don't think I can award myself my own bounty, so will award it to anyone who knows of an easier or cleaner way of accomplishing this.
    – aendra
    Aug 6, 2014 at 17:36
-2
add_filter( 'attachment_fields_to_edit', 'attachment_fields_to_edit', null, 2 );
add_filter( 'attachment_fields_to_save', 'attachment_fields_to_save', null, 2 );

function attachment_fields_to_edit( $form_fields, $post ) {
    $form_fields['my_attachment_field'] = array(
        'label' => 'My Label',
        'input' => 'text',
        'value' => get_post_meta( $post->ID, '_my_attachment_field', true )
    );

    return $form_fields;
}

function attachment_fields_to_save( $post, $attachment ) {
    if ( ! empty( $attachment['my_attachment_field'] ) )
        update_post_meta( $post['ID'], '_my_attachment_field', $attachment['my_attachment_field'] );
    else
        delete_post_meta( $post['ID'], '_my_attachment_field' );

    return $post;
}
2
  • Thank you but that was not what I was looking for, I was looking for a hook to edit the content/add form fields when I click on edit image in a post and not in the media library - see my opening post.
    – Stephan S.
    Dec 4, 2012 at 13:50
  • 2
    Downvoting, doesn't answer the question.
    – aendra
    Aug 6, 2014 at 10:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.