Cuándo un usuario con rol “autor” entra en el panel de administración de WordPress, puede ver el listado de todos los posts que hay en el sitio, incluso posts de otros autores que están en estado de borrador. Aunque no podrá ver el contenido ni editarlo, creo que es muy interesante que en una web multi-autor esa información pueda quedar reservada a editores y administradores.

En realidad, como decía en el post sobre permitir acceso a wp-admin sólo a administradores, lo más lógico no es hablar de roles de usuario, como es “autor”, sino de las capacidades del usuario (user capabilities, yo prefiero traducirlo como “competencias de usuario”, que tiene más sentido en este contexto pero parece que se ha impuesto el término “capacidades”). Cada rol de usuario tiene unas capacidades preestablecidas pero se pueden modificar, por eso comprobar roles de usuario no es un método fiable para decidir lo que le permitimos o no al usuario.

En el caso que nos ocupa, lo más lógico sería que los usuarios sin capacidad para editar posts de otros autores sólo puedan ver los suyos propios. Dicho de otro modo, sólo los usuarios con la competencia edit_others_posts asignada podrán ver todos los posts en la administración de WordPress. Cosa lógica, si sólo puedo editar mis posts, ¿para que quiero ver los posts de los demás en el dashboard?

La lógica a seguir podría resumirse en tres pasos:

  1. Comprobar que estamos en la pantalla de posts del panel de administración
  2. Comprobar si el usuario actual puede editar los posts de otros autores
  3. Modificar el query para incluir el argumento author y establecerlo en el ID del usuario actual

De forma general, el query de posts en WordPress se puede modificar utilizando el action hook pre_get_posts. El siguiente código utiliza este action y funcionaría tanto para el tipo de posts estándar como para custom post types:

add_action( 'pre_get_posts', 'cyb_list_own_posts_for_authors' );
function cyb_list_own_posts_for_authors( $query ) {

  // solo para la administración y el query principal
  if( is_admin() && $query->is_main_query() ) {

    // Obtenemos la inforamción sobre la pantalla actual
    // y el post type solicitado
    $current_screen = get_current_screen();
    $post_type_object = get_post_type_object( get_query_var( 'post_type' ) );

    // Comprobamos que la pantalla sea la de edición de posts
    // y si el usuario actual puede editar los posts de otros autores
    // Nota: get_current_screen() y get_post_type_object() pueden devolver null
    if(
        ( ! is_null( $current_screen ) && $current_screen->base == 'edit' )
        && ( ! is_null( $post_type_object ) && ! current_user_can( $post_type_object->cap->edit_others_posts ) )
    ) {

      // Establecer el parámetro "author" igual al usuario actual
      $query->set( 'author', get_current_user_id() );

    }

  }
}

También se puede conseguir el mismo resultado pero con menos código utilizando el action load-edit.php junto al action parse_resquest. Al utilizar el action load-edit.php ya no necesitamos comprobar si estamos en el backend ni comprobar la pantalla actual. Y como el action parse_request sólo afecta al query principal también nos podemos ahorrar esa comprobación:

add_action( 'load-edit.php', 'cyb_list_own_posts_for_authors' );
function cyb_list_own_posts_for_authors() {

    add_action( 'parse_request', function( $query ) {
        $post_type_object = get_post_type_object( $query->query_vars['post_type'] );
 
        if ( ! is_null( $post_type_object ) && ! current_user_can( $post_type_object->cap->edit_others_posts ) ) {
            $query->query_vars['author'] = get_current_user_id();
        }

    } );

}

Ambos métodos son igualmente válidos; el segundo es mucho más limpio pero el primero podría ser más conveniente en casos de que necesitemos más libertad o aplicar la restricción de una forma más amplia.

Mostrar el recuento correcto de posts

Con cualquiera de los métodos anteriores, el usuario actual ya vería sólo sus propios posts pero ahora algunos de los filtros (views) en las pantallas de edición muestran un recuento de posts incorrecto. Se puede arreglar utilizando el filtro views_edit-{post-type} y haciendo de nuevo el recuento. Por ejemplo, el siguiente código haría el recuento para el tipo de post estándar (post) y el tipo de post news:

add_filter('views_edit-post', 'cyb_views_filter_for_own_posts' );
add_filter('views_edit-news', 'cyb_views_filter_for_own_posts' );
function cyb_views_filter_for_own_posts( $views ) {
 
  $post_type = get_query_var( 'post_type' );
  $post_type_object = get_post_type_object( $post_type );
  
  // No seguir si el usuario puede editar los posts de otros autores
  if ( is_null( $post_type_object ) || current_user_can( $post_type_object->cap->edit_others_posts ) ) {
    return $views;
  }

  unset( $views['mine'] );
 
  $new_views = array(
    'all' => __('All'),
    'publish' => __('Published'),
    'private' => __('Private'),
    'pending' => __('Pending Review'),
    'future' => __('Scheduled'),
    'draft' => __('Draft'),
    'trash' => __('Trash')
  );

  $author = get_current_user_id();
 
  foreach( $new_views as $view => $name ) {
 
    $query = array(
      'author' => $author,
      'post_type' => $post_type
    );
 
    if($view == 'all') {
 
      $query['all_posts'] = 1;
      $class = ( get_query_var('all_posts') == 1 || get_query_var('post_status') == '' ) ? ' class="current"' : '';
      $url_query_var = 'all_posts=1';
 
    } else {
 
      $query['post_status'] = $view;
      $class = ( get_query_var('post_status') == $view ) ? ' class="current"' : '';
      $url_query_var = 'post_status='.$view;
 
    }
 
    $result = new WP_Query($query);
 
    if($result->found_posts > 0) {
 
      $views[$view] = sprintf( 
        '<a href="%s"'. $class .'>'.__($name).' <span class="count">(%d)</span></a>',
        admin_url('edit.php?'.$url_query_var.'&post_type='.$post_type),
        $result->found_posts
      );
 
    } else {
 
      unset($views[$view]);
 
    }
 
  }

  return $views;

}

Ten en cuenta que este “recuento” es efectivamente un re-cuento, quiero decir que se hace dos veces. Una vez la que hizo WordPress y otra la que hacemos nosotros después. Esto supone más queries a la base de datos y puede tener implicaciones negativas en la performance, aunque no es en la parte pública y no habría impacto negativo sobre los usuarios del frontend.

También puedes quitar el contador de posts en cada filtro:

add_filter( 'views_edit-post', 'cyb_remove_views_filter_counter' );
add_filter( 'views_edit-news', 'cyb_remove_views_filter_counter' );

function cyb_remove_views_filter_counter( $views ) {

  foreach ( $views as $index => $view ) {

    $views[ $index ] = preg_replace( '/ <span class="count">\([0-9]+\)<\/span>/', '', $view );

  }

  return $views;

}

O quitar todos esos filtros si nos los quieres:

add_filter('views_edit-post', 'cyb_remove_views_filter' );
add_filter('views_edit-news', 'cyb_remove_views_filter' );

function cyb_remove_views_filter( $views ) {

  return array();

}

Y si lo queréis en un plugin listo para usar, aquí lo tenéis.

  • nacho

    he creado el plugin correspondiente y funciona bien solo una pega cuando vas a administrar algo se queda la pantalla en planco

  • José Carlos

    Buenas tardes. Perdona una pregunta, qué archivo debo de abrir para meter esa parte de código que comentas para mostrar sólo las entradas de un autor en su área personal? Muchas gracias.

  • Antoni

    Muy bien, pero… ¿Qué ocurre con las imágenes o los media files? De igual manera, cuando un autor va a publicar un nuevo post, y quiere adjuntar una imagen, tiene acceso a todas las imagenes subidas por otros autores, editores, administradores. Cómo evitarlo y que solo pueda ver las imágenes que él ha subido anteriormente?

    • Puedes seguir la lógica similar a la descrita en el tutorial para el tipo tipo de post attachment que se corresponde con los media files. En el código propuesto se comprueba que se está en la pantlla de edición de posts y por eso no funcionará para los attachement, pero la idea es la misma: establecer el author al usuario actual en el query principal. Por ejemplo, puedes modificar el action pre_get_post del post con algo así (no probado):

      if( is_admin() && $wp_query->query['post_type'] === 'attachment' ){

      $wp_query->set( 'author', get_current_user_id() );

      }

      Luego el recuento de attachments para refleje sólo los del usuario actual es más fácil de hacer que con el resto de posts. Basta con utilizar el filtro wp_count_attachments.

      Hay varios plugins que limitan el acceso en el media manager sólo a los archivos subidos por el usuario actual y de ellos el que más me gusta es WP User Media. No suelo recomendar plugins, pero este plugin hace muy bien su trabajo y no es nada invasivo.

      De todas formas, en general creo que es buena idea que los autores tengan acceso a los archivos de todo el sitio. Lo perjudicial del contenido duplicado también se aplica a los archivos multimedia. (aunque debe admitir que una de mis webs utilizo WP User Media).

  • Gilles

    Genial 🙂