Mike Kelly

Happy happy joy joy

Tracking down a WordPress bug

| 32 Comments

“It doesn’t work”

I recently received quite a few reports from WordPress users that media uploads weren’t working on one of the blogs here. I was unable to reproduce the problem, so I couldn’t do much to diagnose the problem. I contacted the users having the problem and asked for more specific details. The information they provided helped me to track down the cause of the issue.

What was particularly useful was this screenshot which shows the error generated when uploading an image:

uploaderror

It turned out that the error message ‘An error occurred in the upload. Please try again later’ is quite a common one among WordPress users. What’s more, there is no real consensus about what causes the problem, or what the solution is. That’s why I’m writing this post – in case my findings may be useful to other WordPress users.

The error message itself is very unhelpfully non-specific. It transpires that it is a ‘default error’ in WordPress, which means that it is likely to be thrown in a number of different scenarios. Hence the confusion about fixes.
The crucial information I picked up from my research was that for some people the error was only thrown for users with certain WordPress roles – for Authors but not for Administrators.

So, in trying to reproduce the error, I switched my user role from Administrator to Author. Immediately the error occurred, and I was able to start the debugging process to find out what was causing the problem.

Digging into the code

The trail led immediately to this function in wp-admin/includes/ajax-actions.php

function wp_ajax_upload_attachment() {
    check_ajax_referer( 'media-form' );

    if ( ! current_user_can( 'upload_files' ) )
        wp_die();

    if ( isset( $_REQUEST['post_id'] ) ) {
        $post_id = $_REQUEST['post_id'];
        if ( ! current_user_can( 'edit_post', $post_id ) )
            wp_die();
    } else {
        $post_id = null;
    }

I’ve highlighted the relevant section in red. In my tests, when I arrived at this section of the code,

$post_id

was always set to 1.

current_user_can()

refers to the permissions the current WordPress user has. If the user is an ‘Author’ in the blog, and didn’t create the post with id=1, they will not have ‘edit_post’ permission, because Authors only have rights to edit their own posts. So the upload process fails, and shows us that unhelpful error message.

In this particular case the error was happening with a blog using the P2 theme. The nice thing about this theme is that it allows users to post from the blog’s front page, without having to deal with the WordPress Dashboard. When posting from the front page of the blog, there is no

$post_id

value until the post has been submitted. So why was the function above checking for authoring rights on an unrelated post, the post with id = 1?

To figure that out I had to find out where post_id was being set in the request.

The answer was that the request was made in a javascript file, wp-includes/js/media-views.js, but the post_id value it sent was passsed to it via the wp_enqueue_media function in wp-includes/media.php, and was initialised in wp-admin/includes/media.php, in the media_buttons function called by the P2 theme.

And the post_id value there was pulled from the

$_GLOBALS['post']

or

$_GLOBALS['post_ID']

value.

wordpressflowchart_annotated

At this point I stopped tracing backwards, as global values can be set from any number of places. It seemed however that this was one use case where the global post value was irrelevant to the upload process, and that what I was seeing – the post_id set to the global post value, which happened to be 1 – was the remnant of a previous interaction.

OK, just tell me how to fix it

So how to fix it? If we look back up at the wp_ajax_upload_attachment() function, we can see that the process only fails if a post_id value is set and the user doesn’t have rights to edit the post with that id. If no post_id is set at all, everything works as expected. Ideally we would be able to unset post_id in the theme, leaving core files untouched. I was wary of unsetting a global variable however, in case of unwanted side effects. Instead I added a simple check to the wp_enqueue_media function in wp-includes/media.php, to test whether to set the post_id value in the javascript file.

Solution 1:

Replace this code in in the wp_enqueue_media function:

$post = null;
    if ( isset( $args['post'] ) ) {
        $post = get_post( $args['post'] );

            $settings['post'] = array(
                'id' => $post->ID,
                'nonce' => wp_create_nonce( 'update-post_' . $post->ID ),
            );

With this:

$post = null;
    if ( isset( $args['post'] ) ) {
        $post = get_post( $args['post'] );

        // Test for front page submissions, as with P2 theme
        // In this case global post_id is not applicable and can cause
        // permissions problems for authors, resulting in the dreaded 
        // message 'An error occurred in the upload. Please try again later.'
        if (is_admin()) {
            $settings['post'] = array(
                'id' => $post->ID,
                'nonce' => wp_create_nonce( 'update-post_' . $post->ID ),
            );
        }

Done.
The confusingly titled WordPress function

is_admin()

returns a boolean telling us whether we are in the blog’s Dashboard area or not. This will be true in the vast majority of cases when making a post or uploading a media file. When posting from the front page of P2 however, is_admin() returns false, so no value for the post id is passed to the javascript file wp-includes/js/media-views.js, and the upload does not encounter the permissions problem later in the process.
Previous tests showed that the error did not occur when posting from the blog’s Dashboard, so this is a reasonable workaround for now.

Alternative solution

It would seem that the global post and post_ID values are usually set when making a post from the Dashboard, which is the standard WordPress approach. If you are sure that’s the case, and don’t mind using the WordPress approach of setting global values from here there and everywhere, you could apply a different solution to the problem: in the p2_media_buttons function, simply reset the post and post_ID global values to 0. (As highlighted in red below.)

Solution 2:
In the p2_media_buttons function, add the code highlighted in red below.

function p2_media_buttons() {
// If we're using http and the admin is forced to https, bail.
if ( ! is_ssl() && ( force_ssl_admin() || get_user_option( 'use_ssl' ) )  ) {
return;
}

include_once( ABSPATH . '/wp-admin/includes/media.php' );
ob_start();
global $post_ID, $post;
$post_ID = $post = 0;
do_action( 'media_buttons' );
$buttons = ob_get_clean();

// Replace any relative paths to media-upload.php
$buttons = preg_replace( '/([\'"])media-upload.php/', '${1}' . admin_url( 'media-upload.php' ), $buttons );

// Remove any images.
$buttons = preg_replace( '/<img [^>]*src=(\"|\')(.+?)(\1)[^>]*>/i', '', $buttons );

echo $buttons;
}

This approach has the advantage of restricting changes to the theme, leaving the core file, wp-includes/media.php, unchanged.

Conclusion

So that’s a solution for one instance of the ‘An error occurred in the upload. Please try again later’ message. If you’re seeing the same message your issue may be similar – or not. As I mentioned before it’s a generic message.

Some questions remain. Is this problem unique to our WordPress installation and plugin configuration, or is it common to all P2 users? Is it a recent problem as a result of an update we have made, or have we always suffered from the problem, and just not had it properly reported?

In any case, the issue points out one of WordPress’s significant weaknesses – overreliance on the use of global variables – resulting in horrible bugs like this which come about in a non-standard use-case scenario, and can often be difficult to troubleshoot and fix.

 

32 Comments

  1. You lost me…. And you have been lost within the wordpess code….I didn’t get eventually what I have to do….

  2. Pingback: WordPress-An error occurred in the upload. Please try again later |

  3. Greetings from Russia.
    Thank you for solution N2. I some days try understand why P2 theme have error when authors try upload image or another media file. Editor role have fine upload, but authors have this problem. But i not edited core file, i replace your solution in template-tags.php (theme file).

    • Hi, Mike,
      I am using different theme than P2. I tried solution No.1 but it didn’t work. Do you have any other suggestions? Thanks. The code is listed below.

      $post = null;
      if ( isset( $args['post'] ) ) {
      $post = get_post( $args['post'] );

      if (is_admin()) {
      $settings['post'] = array(
      ‘id’ => $post->ID,
      ‘nonce’ => wp_create_nonce( ‘update-post_’ . $post->ID ),
      );
      }

  4. Is there a WP 3.5.1 solution. I don’t see the example code in the media.php file and suspect it may be a versioning issue? Any thoughts?

  5. Hey Mike, I had the same problem with the p2 theme and your solution helped me, so thanks! Have you mentioned this to the p2 developers?

    Maybe overlooked it, but did you mention where in the p2 theme files to find p2_media_buttons() ?
    I finally found it in template-tags.php, but maybe that is completely obvious to anyone more familiar with WordPress than myself.

  6. Dear Mike,
    Hello. Is such issue likely to happen again for P2 ?

  7. No they did not. I had reloaded p2 freshly to my install and it did appear not to happen again at first but I then shortly after had to come back to this page and see your answer so that I could fix it again as it – big bug – appeared again. P2 seems “unaffected” in the french meaning of could not care less. If you could “bug” them.

  8. Profile photo of admin

    Hi Cif,
    I posted a notice about this to the P2 forum. Let’s hope they pick up on it.
    Mike

  9. Hi, I’ve been stuck on this for ages. Solution 1 worked fantastically! Thank you very much :)

  10. Profile photo of Mike Kelly

    Glad it was helpful Jamie…
    Mike

  11. Neither of the above solutions worked for me running P2 on localhost.

    Digging further, current_user_can calls a function of WP_User named has_cap located in wp-includes/capabilities.php.

    This function calls a filter named user_has_cap. So it seems to me that by adding this filter to the functions.php file one could make this work. Everything is passed into the filter function, the post id, the capabilities, etc. However, for the life of me I do not understand the documentation of this filter. Perhaps someone can take it from here:

    http://codex.wordpress.org/Plugin_API/Filter_Reference/user_has_cap

    • Yours is a completely different problem, and it’s not a bug. Certain registered user roles, such as “contributor” or “subscriber” usually don’t have permission to upload media. If you want to give, for example, contributors capability to upload media, add the following code in your theme’s function.php

      if ( current_user_can(‘contributor’) ){
      add_action(‘admin_init’, ‘allow_contributor_uploads’);
      }
      function allow_contributor_uploads() {
      $contributor = get_role(‘contributor’);
      $contributor->add_cap(‘upload_files’);
      }

  12. Broken with the WP3.9 update. Any chance you can fix the workaround for the latest WP3.9? Best regards!

    • Profile photo of Mike Kelly

      Hi, I’ll be upgrading to 3.9 soon, so I’ll update the post after I’ve done some tests.

    • Profile photo of Mike Kelly

      I’ve just updated to WordPress 3.9.1. With P2 1.5.2, media uploads are working for users with author status OK, without any fixes required.

      In wp-admin/includes/ajax-actions.php, the post_id value is now set to the current post instead of always to 1.

    • Profile photo of Mike Kelly

      Update – the problem was still there after all – I’m guessing my initial tests were using a cached version of the old site.
      I was able to reapply the fix using solution no. 1
      Mike

  13. Second solution works perfectly for my site using P2. Thank you so much Mike. I owe you a beer

  14. the first solution 1 did not work with me on WP 3.9.1 is away to make it work on WP 3.9.1 ??

  15. Profile photo of Mike Kelly

    Good news – it looks as though the P2 development team have finally picked up on this issue, and a fix is in the pipeline:
    http://wordpress.org/support/topic/media-upload-bug?replies=3

  16. Hey Mike, this article helped me a great deal in getting things done. It was very very helpful. Thanks a lot.

  17. Wouldn’t an easy fix be to make the user an editor?

  18. Hi,

    I have using different theme… Facing the same problem… Pls suggest me to fix the issue…

  19. Problem cropped up for me in WordPress 3.9.2, using a child theme of 2010 — so perhaps you wouldn’t expect your article to help — but it did.

    I saw the line about “current_user_can(‘edit_post’)…” and it triggered my realization that since I had created the post yesterday, then come back to it today, perhaps something had timed out and I wasn’t “really” logged in any more.

    So I logged out and logged back in and the problem was resolved. [I probably should have tried to do something else that required authentication to test the "no longer logged in" hypothesis, but -- next time ...]

    Thanks for sharing!

Leave a Reply

Required fields are marked *.


  *