Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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

share|improve this question
    
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 '12 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 '12 at 14:55
    
Then just update to 3.5 RC1. Will simplify your life a lot. –  kaiser Dec 3 '12 at 14:57
    
Considering we're far past 3.5 at this stage, can somebody answer this for the current version of WordPress? –  aendrew Aug 6 at 10:18
    
The one to edit an image, correct? –  Manny Fleurmond Aug 6 at 13:46

2 Answers 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, '' );
    }
  });

});
share|improve this answer
    
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. –  aendrew Aug 6 at 17:36
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;
}
share|improve this answer
    
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 '12 at 13:50
2  
Downvoting, doesn't answer the question. –  aendrew Aug 6 at 10:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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