¿Cómo restringir a los usuarios para que solo vean los elementos de la biblioteca de medios que han subido?
Quiero que los usuarios puedan subir fotos usando add_cap('upload_files')
pero en su página de perfil, la Biblioteca de Medios muestra todas las imágenes que se han subido. ¿Cómo puedo filtrar esto para que solo puedan ver las imágenes que ellos subieron?
Aquí está mi solución por el momento... Estoy haciendo una consulta simple de WP, luego un bucle en la página de "Perfil" del usuario
$querystr = " SELECT wposts.post_date,wposts.post_content,wposts.post_title, guid
FROM $wpdb->posts wposts
WHERE wposts.post_author = $author
AND wposts.post_type = 'attachment'
ORDER BY wposts.post_date DESC";
$pageposts = $wpdb->get_results($querystr, OBJECT);
Siempre podrías filtrar la lista de medios usando un filtro pre_get_posts
que primero determine la página y las capacidades del usuario, y establezca el parámetro de autor cuando se cumplan ciertas condiciones.
Ejemplo
add_action('pre_get_posts','users_own_attachments');
function users_own_attachments( $wp_query_obj ) {
global $current_user, $pagenow;
$is_attachment_request = ($wp_query_obj->get('post_type')=='attachment');
if( !$is_attachment_request )
return;
if( !is_a( $current_user, 'WP_User') )
return;
if( !in_array( $pagenow, array( 'upload.php', 'admin-ajax.php' ) ) )
return;
if( !current_user_can('delete_pages') )
$wp_query_obj->set('author', $current_user->ID );
return;
}
Utilicé la capacidad de eliminar páginas como condición para que los Administradores y Editores sigan viendo la lista completa de medios.
Hay un pequeño efecto secundario, para el cual no veo ningún hook disponible, y es con los conteos de adjuntos que se muestran encima de la lista de medios (que seguirán mostrando el conteo total de elementos multimedia, no el del usuario dado - consideraría esto un problema menor).
Pensé en publicarlo de todos modos, podría ser útil... ;)

He permitido la carga de archivos a usuarios con nivel de suscriptor. Intenté usar tu código pero no funciona.

Puedo confirmar la misma observación. Para mí "no funciona" significa que el rol de "colaborador" todavía puede ver todos los elementos multimedia cuando va a subir un jpg. Sin embargo, cuando va a la biblioteca multimedia desde el menú, está vacía. (Mi rol de "colaborador" ya tiene la capacidad adicional para subir archivos y eso sí funciona.)

Así que tu código solo necesita ajustarse para cualquier página que llene la pestaña "Biblioteca de medios" de la ventana de carga. Estoy investigando esto ahora.

Si recuerdo correctamente (y los errores ocurren), no había ningún hook apropiado en su momento cuando escribí esta respuesta, similar a cómo no había hooks disponibles para arreglar el conteo de medios. Han habido unas 3 nuevas versiones de WordPress desde entonces, así que ahora podrían existir soluciones.

Adicionalmente, podría ser solo cuestión de actualizar la verificación condicional $pagenow
, ya que esto apunta a dirigir la consulta específicamente para la página principal de medios, que no es lo mismo que la pestaña de medios en la ventana emergente de carga.

No me importa que los conteos estén incorrectos. Por lo demás, tu código funciona desde el elemento Medios en el menú (WP v3.8.1). Estoy intentando que esto funcione desde la ventana de carga que aparece dentro de la página "Nueva Entrada". Sí, estoy de acuerdo... Estoy tratando de encontrar la página correcta para la verificación de $pagenow
.

Aquí está la solución: http://wordpress.stackexchange.com/a/132319/11092

if( !in_array( $pagenow, array( 'upload.php', 'admin-ajax.php' )
A esta línea en tu código le falta un corchete de cierre )
, Debería haber tres )
, pero tu código solo tiene dos.

Esto parece aplicarse solo a la vista de cuadrícula. ¿Alguna recomendación sobre cómo lograr esto también para la vista de lista?

A partir de WP 3.7 existe una forma mucho mejor mediante el filtro ajax_query_attachments_args
, como se proporciona en la documentación:
add_filter( 'ajax_query_attachments_args', 'show_current_user_attachments' );
function show_current_user_attachments( $query ) {
$user_id = get_current_user_id();
if ( $user_id ) {
$query['author'] = $user_id;
}
return $query;
}

Aquí tienes una solución completa para tanto publicaciones como medios (este código está específicamente para autores, pero puedes modificarlo para cualquier rol de usuario). Esto también corrige el conteo de publicaciones/medios sin necesidad de modificar archivos del núcleo.
// Mostrar solo publicaciones y medios relacionados con el autor conectado
add_action('pre_get_posts', 'query_set_only_author' );
function query_set_only_author( $wp_query ) {
global $current_user;
if( is_admin() && !current_user_can('edit_others_posts') ) {
$wp_query->set( 'author', $current_user->ID );
add_filter('views_edit-post', 'fix_post_counts');
add_filter('views_upload', 'fix_media_counts');
}
}
// Corregir conteos de publicaciones
function fix_post_counts($views) {
global $current_user, $wp_query;
unset($views['mine']);
$types = array(
array( 'status' => NULL ),
array( 'status' => 'publish' ),
array( 'status' => 'draft' ),
array( 'status' => 'pending' ),
array( 'status' => 'trash' )
);
foreach( $types as $type ) {
$query = array(
'author' => $current_user->ID,
'post_type' => 'post',
'post_status' => $type['status']
);
$result = new WP_Query($query);
if( $type['status'] == NULL ):
$class = ($wp_query->query_vars['post_status'] == NULL) ? ' class="current"' : '';
$views['all'] = sprintf(
'<a href="%1$s"%2$s>%4$s <span class="count">(%3$d)</span></a>',
admin_url('edit.php?post_type=post'),
$class,
$result->found_posts,
__('Todos')
);
elseif( $type['status'] == 'publish' ):
$class = ($wp_query->query_vars['post_status'] == 'publish') ? ' class="current"' : '';
$views['publish'] = sprintf(
'<a href="%1$s"%2$s>%4$s <span class="count">(%3$d)</span></a>',
admin_url('edit.php?post_type=post'),
$class,
$result->found_posts,
__('Publicado')
);
elseif( $type['status'] == 'draft' ):
$class = ($wp_query->query_vars['post_status'] == 'draft') ? ' class="current"' : '';
$views['draft'] = sprintf(
'<a href="%1$s"%2$s>%4$s <span class="count">(%3$d)</span></a>',
admin_url('edit.php?post_type=post'),
$class,
$result->found_posts,
__('Borrador')
);
elseif( $type['status'] == 'pending' ):
$class = ($wp_query->query_vars['post_status'] == 'pending') ? ' class="current"' : '';
$views['pending'] = sprintf(
'<a href="%1$s"%2$s>%4$s <span class="count">(%3$d)</span></a>',
admin_url('edit.php?post_type=post'),
$class,
$result->found_posts,
__('Pendiente')
);
elseif( $type['status'] == 'trash' ):
$class = ($wp_query->query_vars['post_status'] == 'trash') ? ' class="current"' : '';
$views['trash'] = sprintf(
'<a href="%1$s"%2$s>%4$s <span class="count">(%3$d)</span></a>',
admin_url('edit.php?post_type=post'),
$class,
$result->found_posts,
__('Papelera')
);
endif;
}
return $views;
}
// Corregir conteos de medios
function fix_media_counts($views) {
global $wpdb, $current_user, $post_mime_types, $avail_post_mime_types;
$views = array();
$count = $wpdb->get_results( "
SELECT post_mime_type, COUNT( * ) AS num_posts
FROM $wpdb->posts
WHERE post_type = 'attachment'
AND post_author = $current_user->ID
AND post_status != 'trash'
GROUP BY post_mime_type
", ARRAY_A );
foreach( $count as $row )
$_num_posts[$row['post_mime_type']] = $row['num_posts'];
$_total_posts = array_sum($_num_posts);
$detached = isset( $_REQUEST['detached'] ) || isset( $_REQUEST['find_detached'] );
if ( !isset( $total_orphans ) )
$total_orphans = $wpdb->get_var("
SELECT COUNT( * )
FROM $wpdb->posts
WHERE post_type = 'attachment'
AND post_author = $current_user->ID
AND post_status != 'trash'
AND post_parent < 1
");
$matches = wp_match_mime_types(array_keys($post_mime_types), array_keys($_num_posts));
foreach ( $matches as $type => $reals )
foreach ( $reals as $real )
$num_posts[$type] = ( isset( $num_posts[$type] ) ) ? $num_posts[$type] + $_num_posts[$real] : $_num_posts[$real];
$class = ( empty($_GET['post_mime_type']) && !$detached && !isset($_GET['status']) ) ? ' class="current"' : '';
$views['all'] = "<a href='upload.php'$class>" . sprintf( __('Todos <span class="count">(%s)</span>', 'archivos subidos'), number_format_i18n( $_total_posts )) . '</a>';
foreach ( $post_mime_types as $mime_type => $label ) {
$class = '';
if ( !wp_match_mime_types($mime_type, $avail_post_mime_types) )
continue;
if ( !empty($_GET['post_mime_type']) && wp_match_mime_types($mime_type, $_GET['post_mime_type']) )
$class = ' class="current"';
if ( !empty( $num_posts[$mime_type] ) )
$views[$mime_type] = "<a href='upload.php?post_mime_type=$mime_type'$class>" . sprintf( translate_nooped_plural( $label[2], $num_posts[$mime_type] ), $num_posts[$mime_type] ) . '</a>';
}
$views['detached'] = '<a href="upload.php?detached=1"' . ( $detached ? ' class="current"' : '' ) . '>' . sprintf( __( 'Sin adjuntar <span class="count">(%s)</span>', 'archivos no adjuntos' ), $total_orphans ) . '</a>';
return $views;
}

gran fragmento de código, pero si no hay elementos en la biblioteca de medios, muestra errores: Warning: array_sum() expects parameter 1 to be array, null given, y Warning: array_keys() expects parameter 1 to be array, null given

Solo tienes que definir $_num_posts como un array en la función fix_media_counts(). $_num_posts = array();

El código en esta respuesta funciona, pero también elimina cualquier campo personalizado creado por el plugin Advanced Custom Fields.

Muy relacionado: http://wordpress.stackexchange.com/questions/178236/correct-post-count-all-published-drafts-pending-trash-for-custom-pos

Esta es una versión modificada de la respuesta aceptada. Dado que la respuesta aceptada solo afecta al elemento del menú Medios en la izquierda, los usuarios aún podían ver toda la biblioteca de medios dentro del cuadro modal al subir una foto a una entrada. Este código ligeramente modificado corrige esa situación. Los usuarios afectados solo verán sus propios elementos de medios desde la pestaña Biblioteca de Medios del cuadro modal que aparece al subir contenido en una entrada.
Este es el código de la respuesta aceptada con un comentario marcando la línea a editar...
add_action('pre_get_posts','users_own_attachments');
function users_own_attachments( $wp_query_obj ) {
global $current_user, $pagenow;
if( !is_a( $current_user, 'WP_User') )
return;
if( 'upload.php' != $pagenow ) // <-- vamos a trabajar en esta línea
return;
if( !current_user_can('delete_pages') )
$wp_query_obj->set('author', $current_user->id );
return;
}
Para que los usuarios solo vean sus propios medios desde el menú Medios Y la pestaña Biblioteca de Medios del modal de subida, reemplaza la línea indicada con esto...
if( ( 'upload.php' != $pagenow ) &&
( ( 'admin-ajax.php' != $pagenow ) || ( $_REQUEST['action'] != 'query-attachments' ) ) )
(saltos de línea y espaciado solo para mejor legibilidad aquí)
Lo siguiente es igual que lo anterior pero también les restringe a ver solo sus propias entradas desde el elemento del menú Entradas.
if( ( 'edit.php' != $pagenow ) &&
( 'upload.php' != $pagenow ) &&
( ( 'admin-ajax.php' != $pagenow ) || ( $_REQUEST['action'] != 'query-attachments' ) ) )
(saltos de línea y espaciado solo para mejor legibilidad aquí)
Notas: como en la respuesta aceptada, los contadores de entradas y medios serán incorrectos. Sin embargo, hay soluciones para esto en algunas otras respuestas en esta página. No las incorporé simplemente porque no las había probado.

Código funcional completo.. El único problema es que obtiene un conteo incorrecto de imágenes en la biblioteca multimedia en la página de Añadir Entrada.
function my_files_only( $wp_query ) {
if ( strpos( $_SERVER[ 'REQUEST_URI' ], '/wp-admin/upload.php' ) !== false ) {
if ( !current_user_can( 'level_5' ) ) {
global $current_user;
$wp_query->set( 'author', $current_user->id );
}
}
else if ( strpos( $_SERVER[ 'REQUEST_URI' ], '/wp-admin/media-upload.php' ) !== false ) {
if ( !current_user_can( 'level_5' ) ) {
global $current_user;
$wp_query->set( 'author', $current_user->id );
}
}
}
add_filter('parse_query', 'my_files_only' );

No deberías usar niveles de usuario, todavía están en WordPress principalmente por compatibilidad con versiones anteriores (anteriores a WP 2.0), no son confiables para determinar capacidades de usuario en WordPress moderno (ya que probablemente desaparecerán del núcleo cuando esa compatibilidad ya no sea necesaria). Usa una capacidad real para determinar los derechos del usuario.

t31os tiene una gran solución allí. Lo único es que el número de todas las publicaciones aún aparece.
Encontré una manera de evitar que aparezca el conteo de números usando jQuery.
Solo agrega esto a tu archivo de funciones.
function jquery_remove_counts()
{
?>
<script type="text/javascript">
jQuery(function(){
jQuery("ul.subsubsub").find("span.count").remove();
});
</script>
<?php
}
add_action('admin_head', 'jquery_remove_counts');
¡Está funcionando para mí!

Resolví mi problema con una solución un poco rudimentaria pero funcional.
1) Instalé el plugin WP Hide Dashboard, para que el usuario solo vea un enlace al formulario de edición de su perfil.
2) En el archivo de plantilla author.php, inserté el código que usé anteriormente.
3) Luego, para usuarios logueados, mostré un enlace directo a la página de subida "wp-admin/media-new.php"
4) El siguiente problema que noté fue que después de subir la foto, los redirigía a upload.php... y podían ver todas las demás imágenes. No encontré un hook en la página media-new.php, así que terminé modificando el core "media-upload.php" para redirigirlos a su página de perfil:
global $current_user;
get_currentuserinfo();
$userredirect = get_bloginfo('home') . "/author/" .$current_user->user_nicename;
Luego reemplacé wp_redirect( admin_url($location) );
con wp_redirect($userredirect);
Un par de problemas, sin embargo. Primero, el usuario logueado todavía puede ir a "upload.php", si sabe que existe. No pueden hacer nada excepto VER los archivos, y el 99% de la gente ni siquiera sabrá de ello, pero aún así no es óptimo. Segundo, también redirige al Admin a la página de perfil después de subir. Estos pueden tener una solución bastante simple verificando los roles de usuario y solo redirigiendo a los Subscriptores.
Si alguien tiene ideas sobre cómo engancharse a la página de Medios sin tocar los archivos core, lo agradecería. ¡Gracias!

<?php
/*
Plugin Name: Gestiona Solo Tus Medios
Version: 0.1
*/
// Gestiona Solo Tus Medios
function mymo_parse_query_useronly( $wp_query ) {
if ( strpos( $_SERVER[ 'REQUEST_URI' ], '/wp-admin/upload.php' ) !== false ) {
if ( !current_user_can( 'level_5' ) ) {
global $current_user;
$wp_query->set( 'author', $current_user->id );
}
}
}
add_filter('parse_query', 'mymo_parse_query_useronly' );
?>
Guarda el código anterior como manage_your_media_only.php, comprímelo en un zip, súbelo como plugin a tu WordPress y actívalo, eso es todo.

Una forma de hacer esto es utilizar el plugin Role Scoper, que también es excelente para gestionar roles y capacidades muy específicas. Puedes bloquear el acceso a las imágenes en la Biblioteca Multimedia para que solo sean accesibles aquellas subidas por cada usuario. Lo he estado utilizando en un proyecto en el que estoy trabajando actualmente y funciona bien.
