logo

Solución: problema en WordPress REST API con imágenes atadas a borradores

08 junio 2024

Si estás acostumbrado a trabajar con la API de WordPress, es posible que hayas notado un problema con las imágenes destacadas que no se muestran o no se sirven en la respuesta de la API como esperabas.
Esto se puede deber a que la imagen en la base de datos está atada a un post que no es público, es decir, o bien está en la papelera o es un borrador, etc.

Eso es porque cuando asignas una imagen destacada a un post, en la base de datos el estado de la imagen es «inherit» es decir que hereda el estado de su «parent» (la entrada padre) que es el post en sí, por lo tanto el valor de post_parent para la imagen es el ID del post al que se ha asignado la image destacada, y si este post es un borrador, la imagen también es considerada borrador.

Por lo tanto, cuando hacemos una petición via REST API a dicha imagen obtenemos la siguiente respuesta:

1
2
3
4
5
6
7
{
   code: "rest_forbidden",
   message: "Sorry, you are not allowed to do that.",
   data: {
     status: 401
   }
}

Y esto es un problema si hemos asignado la misma imagen como destacada a otros posts que si son públicos.

Hay un ticket abierto https://core.trac.wordpress.org/ticket/41445 pero tiene 7 años de antigüedad, esperemos que se resuelva pronto y no se tenga que buscar soluciones alternativas, pero de momento os dejo aquí una solución.

Con el hook rest_post_dispatch podemos modificar la respuesta recibida evitando la verificación de permisos y forzando retornar los datos para el archivo solicitado.
No podemos usar rest_prepare_attachment en este caso porque el hook no llega a ejecutarse cuando la respuesta es 401 ret_forbidden.

El código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
add_filter( 'rest_post_dispatch', 'override_attachment_visibility', 10, 3 );

/**
 * Force returning media bypassing permission check for those that are attached to
 * a draft post and have inherit status.
 * 
 * @param mixed $result Response to replace the requested version with.
 * @param \WP_REST_Server Server Instance
 * @param \WP_REST_Request Request used to generate the response.
 * 
 * @return mixed
 */
function override_attachment_visibility( $result, $server, $request ) {

     $route = $request->get_route();

     if ( 
       str_contains( $route, '/wp/v2/media' ) &&
       'GET' === $request->get_method()
     ) {
        $media_id = (int) $request->get_param( 'id' );

	if ( ! empty( $media_id ) ) {
	    $media_post = get_post( $media_id );

	    // Bypass permission check if attachment's post paret is draft.
	    if ( 
		! empty( $media_post ) && 
		0 < $media_post->post_parent &&
		'attachment' === $media_post->post_type &&
		'inherit' === $media_post->post_status &&
		'publish' !== get_post_status( $media_post->post_parent )
	     ) {

	         $controller = new \WP_REST_Attachments_Controller( 'attachment' );

		 $item = $controller->prepare_item_for_response(
                   $media_post,
                   $request
                 );

                 $result = rest_ensure_response( $prepared_response );
	      }
	 }
      }

      return $result;
  }