Files
thalim-theme/inc/access-control.php

234 lines
8.0 KiB
PHP

<?php
/**
* Restrictions d'accès aux contenus et capacités des contributeurs :
* - liste admin des contributeurs limitée à leurs posts + posts où ils
* figurent en membres/autre_membres (et compteurs cohérents) ;
* - droit d'édition par post pour les membres listés (user_has_cap) ;
* - catégorie « Vie du labo » réservée aux connectés ;
* - redirections login/dashboard des non-admins.
*/
// Restrict Contributors to see only their own posts in admin,
// but also include posts where they appear in membres/autre_membres.
function restrict_contributor_posts( $query ) {
if ( ! is_admin() || ! $query->is_main_query() || current_user_can( 'edit_others_posts' ) ) {
return;
}
global $user_ID, $wpdb;
// Posts where the user is listed as membre or autre_membre.
$membre_ids = array_map( 'intval', (array) $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT post_id FROM {$wpdb->postmeta}
WHERE meta_key IN ('membres', 'autre_membres') AND meta_value = %s",
$user_ID
)
) );
if ( empty( $membre_ids ) ) {
// Fast path: no membre posts, use simple author filter.
$query->set( 'author', $user_ID );
return;
}
// Posts authored by this user.
$authored_ids = array_map( 'intval', (array) $wpdb->get_col(
$wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_author = %d",
$user_ID
)
) );
$all_ids = array_unique( array_merge( $authored_ids, $membre_ids ) );
// post__in with [0] returns nothing when the combined set is empty.
$query->set( 'post__in', empty( $all_ids ) ? [ 0 ] : $all_ids );
}
add_action( 'pre_get_posts', 'restrict_contributor_posts' );
/**
* Let contributors listed as membres/autre_membres on a post edit it.
*
* user_has_cap modifies capabilities only for this single check —
* it does not permanently alter the user's capability set.
*/
function thalim_membres_can_edit_post( $allcaps, $caps, $args, $user ) {
// Editors and above already have edit_others_posts — nothing to do.
if ( ! empty( $allcaps['edit_others_posts'] ) ) {
return $allcaps;
}
if ( empty( $args[0] ) ) {
return $allcaps;
}
$cap = $args[0];
// Meta caps that carry a post ID in $args[2] (e.g. wp-admin/post.php load).
$meta_caps_with_id = [ 'edit_post', 'edit_page' ];
// Primitive caps called during the admin save flow *without* a
// post_id (e.g. wp-admin/includes/post.php:76 checks edit_others_posts
// directly when $post_author !== current user). We infer the post_id from
// the request so we can still authorize membres per-post.
//
// NOTE: publish_posts / publish_pages are intentionally NOT in this list —
// contributors listed in `membres` must be able to edit (incl. published)
// posts of the lab, but only editors/admins should be able to publish.
$primitive_caps_in_save_flow = [
'edit_others_posts',
'edit_others_pages',
'edit_published_posts',
'edit_published_pages',
];
$post_id = 0;
if ( in_array( $cap, $meta_caps_with_id, true ) && ! empty( $args[2] ) ) {
$post_id = (int) $args[2];
} elseif ( in_array( $cap, $primitive_caps_in_save_flow, true ) ) {
$post_id = (int) ( $_POST['post_ID'] ?? $_REQUEST['post'] ?? 0 );
}
if ( ! $post_id ) {
return $allcaps;
}
$user_id = $user->ID;
$membre_ids = array_map(
'intval',
array_merge(
(array) get_post_meta( $post_id, 'membres', false ),
(array) get_post_meta( $post_id, 'autre_membres', false )
)
);
if ( in_array( $user_id, $membre_ids, true ) ) {
// Grant every primitive cap mapped for this check
// (e.g. edit_others_posts, edit_published_posts).
foreach ( $caps as $c ) {
$allcaps[ $c ] = true;
}
}
return $allcaps;
}
add_filter( 'user_has_cap', 'thalim_membres_can_edit_post', 10, 4 );
// Prevent WP_Posts_List_Table from auto-redirecting contributors to the "Mine"
// view. The constructor sets $_GET['author'] = current_user_id() when the user
// lacks edit_others_posts and no other filter is active. Setting all_posts=1
// before the list table is constructed short-circuits that condition.
add_action( 'load-edit.php', function () {
if ( current_user_can( 'edit_others_posts' ) ) {
return;
}
if (
empty( $_REQUEST['post_status'] )
&& empty( $_REQUEST['all_posts'] )
&& empty( $_REQUEST['author'] )
&& empty( $_REQUEST['show_sticky'] )
) {
$_GET['all_posts'] = 1;
$_REQUEST['all_posts'] = 1;
}
} );
// Adjust post-status counts so contributors see only posts they can access
// (posts they authored + posts listed in membres/autre_membres), not all posts.
// The wp_count_posts filter runs even on cached values, so it won't pollute
// the shared cache.
add_filter( 'wp_count_posts', function ( $counts, $type, $perm ) {
if ( ! is_admin() || current_user_can( 'edit_others_posts' ) ) {
return $counts;
}
global $wpdb;
$user_id = get_current_user_id();
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_status, COUNT(*) AS num_posts
FROM {$wpdb->posts}
WHERE post_type = %s
AND ID IN (
SELECT ID FROM {$wpdb->posts}
WHERE post_author = %d AND post_type = %s
UNION
SELECT post_id FROM {$wpdb->postmeta}
WHERE meta_key IN ('membres', 'autre_membres') AND meta_value = %s
)
GROUP BY post_status",
$type,
$user_id,
$type,
(string) $user_id
),
ARRAY_A
);
$new_counts = array_fill_keys( get_post_stati(), 0 );
foreach ( $results as $row ) {
$new_counts[ $row['post_status'] ] = (int) $row['num_posts'];
}
return (object) $new_counts;
}, 10, 3 );
// ── "Vie du labo" — restricted to logged-in users ────
add_action( 'pre_get_posts', function( $query ) {
if ( is_user_logged_in() ) return;
$vie_du_labo = thalim_cat_id( 'vie-du-labo' );
if ( ! $vie_du_labo ) return;
$excluded = $query->get( 'category__not_in' );
if ( ! is_array( $excluded ) ) $excluded = $excluded ? [ $excluded ] : [];
if ( ! in_array( $vie_du_labo, $excluded ) ) {
$excluded[] = $vie_du_labo;
$query->set( 'category__not_in', $excluded );
}
} );
add_action( 'template_redirect', function() {
if ( ! is_user_logged_in() && is_category( thalim_cat_id( 'vie-du-labo' ) ) ) {
wp_safe_redirect( home_url( '/' ) );
exit;
}
} );
add_filter( 'wp_nav_menu_objects', function( $items, $args ) {
if ( is_user_logged_in() ) return $items;
$vie_du_labo = thalim_cat_id( 'vie-du-labo' );
return array_values( array_filter( $items, function( $item ) use ( $vie_du_labo ) {
if ( $item->object === 'category' && (int) $item->object_id === $vie_du_labo ) return false;
if ( strpos( $item->url, 'vie-du-labo' ) !== false ) return false;
return true;
} ) );
}, 10, 2 );
// Non-admins: hide dashboard and tools menu, redirect to posts list
add_action( 'admin_menu', function() {
if ( ! current_user_can( 'manage_options' ) ) {
remove_menu_page( 'tools.php' );
remove_menu_page( 'index.php' );
}
} );
// Redirect non-admins away from dashboard to their posts list
add_action( 'admin_init', function() {
if ( current_user_can( 'manage_options' ) ) return;
global $pagenow;
if ( $pagenow === 'index.php' ) {
wp_safe_redirect( admin_url( 'edit.php' ) );
exit;
}
} );
// After login, send non-admins to posts list instead of dashboard
add_filter( 'login_redirect', function( $redirect_to, $requested, $user ) {
if ( ! is_wp_error( $user ) && ! $user->has_cap( 'manage_options' ) ) {
return admin_url( 'edit.php' );
}
return $redirect_to;
}, 10, 3 );