web-dev-qa-db-fra.com

Autoriser l'utilisateur à "edit_others_posts" pour enregistrer uniquement, pas publier

Je veux ajouter quelques éditeurs que canedit_others_posts, mais je ne veux pas qu’ils soient capables de publierautres publications, uniquement enregistrerla publication en cliquant sur le bouton Soumettre pour Bouton de révision.

Comment puis-je faire ceci?

EDIT: Pour expliquer cela en détail. Actuellement, nous ne pouvons pas autoriser un utilisateur à éditer d'autres publications par seulementenregistrer la publication. Si edit_others_post est activé pour l'utilisateur, il peut alors publierla publication.).

Le flux de travail que je vise:

  1. Les rédacteurs ne peuvent éditer que les autres messages en attente ( résolu ici ).
  2. Les rédacteurs peuvent enregistrer le message en attente maisne pas le publier. Ainsi, le bouton Soumettre pour révision sera disponible pour eux (qui est le bouton "Mettre à jour le message" lorsqu'un message est en mode attente).
7
Christine Cooper

Si je comprends bien, les utilisateurs ayant un rôle particulier sur votre site doivent:

  • Pouvoir éditer ses propres publications dans tous les statuts sauf 'publier' et ne pas pouvoir les publier, il suffit d'envoyer pour révision
  • Être capable de modifier les autres publications uniquement en attente, mais ne pas pouvoir les publier, il suffit d'envoyer pour révision
  • Ne jamais être capable de supprimer d'autres messages, quel que soit le statut

Si tel est le cas, il me semble que ce rôle ressemble plus à un "auteur" qu'à un "éditeur".

Seules les différences avec l'auteur est que

  • l'utilisateur de votre rôle ne peut pas éditer les publications publiées, même s'il en est l'auteur
  • l'utilisateur de votre rôle peut éditer d'autres publications en attente mais ne les publiant pas

La première suggestion que je puisse vous donner est de créer un rôle personnalisé, en utilisant le rôle 'author' comme point de départ, en supprimant les 3 majuscules non désirées et en ajoutant celui personnalisé, une classe simple qui le fait:

class CustomEditorRole {

  private static $role = 'authorplus';
  private $role_label;

  function __construct() {
    // here we need a real, loaded, text domain
    $this->role_label = __( 'Author +', 'yout-txt-dmn' );
  }

  function addRole() {
    global $wp_roles;
    if ( ! $wp_roles instanceof WP_Roles ) {
      $wp_roles = new WP_Roles;
    }
    $author = get_role( 'author' ); 
    $caps = $author->capabilities; // start with author capabilities
    $caps['publish_posts'] = FALSE;
    $caps['edit_published_posts'] = FALSE;
    $caps['delete_published_posts'] = FALSE;
    $caps['edit_others_pending_posts'] = TRUE; // custom cap
    // create new role with custom caps
    add_role( self::$role, $this->role_label, $caps );
  }

  function removeRole() {
    global $wp_roles;
    if ( ! $wp_roles instanceof WP_Roles ) {
      $wp_roles = new WP_Roles;
    }
    remove_role(self::$role);
  }

}

Ajoutons l'action sur l'activation/désactivation du plugin:

register_activation_hook( __FILE__, array( new CustomEditorRole, 'addRole' ) );
register_deactivation_hook( __FILE__, array( new CustomEditorRole, 'removeRole' ) );

Ici, je suppose que le code précédent est dans le fichier du plugin principal.

Les fonctionnalités que nous avons définies ci-dessus sont valides pour chaque publication, quel que soit le statut de l'auteur ou de la publication de la publication. Nous devons maintenant permettre à un utilisateur doté de notre rôle personnalisé de modifier les autres publications en attente.

Le premier problème que nous rencontrons est que sur l'écran de liste de publication (edit.php), si la fonctionnalité edit_others_posts n'est pas activée pour l'utilisateur (et pour notre rôle personnalisé, elle ne l'est pas), les publications d'autres utilisateurs ne sont pas affichées dans la liste, car supprimées. À partir de la requête et lorsque la requête se produit, nous n’avons pas accès aux données des publications. Nous devons donc attribuer la capacité, l’état de la publication, au moins jusqu’à ce que la requête soit exécutée.

Le deuxième problème est que, lors de la sauvegarde, avant de donner à l'utilisateur doté de rôles personnalisés la limite edit_others_posts, nous devons vérifier non seulement que l'état actuel est "en attente", mais également que l'utilisateur n'essaie pas de le modifier. Cela peut être fait en regardant les informations dans les données $_POST. Cela signifie que nous avons besoin de 2 "routines", une qui s'exécute sur les écrans d'administration (edit.php et post.php) et la seconde qui s'exécute pendant la post-sauvegarde.

Pour donner à notre utilisateur de rôle personnalisé la capacité edit_others_post uniquement pour les publications en attente, ajoutez un filtre à 'user_has_cap'.

Dans le rappel du filtre, nous pouvons implémenter ce workflow:

  1. vérifie si la capacité de filtrage est l'une des 2 que nous souhaitons gérer ('edit-post' ou 'edit-others-posts', vérifions si nous sommes en tant qu'administrateur, vérifie si l'utilisateur dispose de notre fonctionnalité personnalisée et qu'il ne s'agit pas d'un éditeur ni d'un administrateur. Si toutes ces conditions sont vraies nous pouvons continuer, sinon nous ne devons rien faire, c'est-à-dire rendre les capacités d'origine
  2. vérifions si nous économisons ou non, et exécutons 2 routines différentes:
    • Routine lors de la sauvegarde
    • Routine quand on n'enregistre pas

Routine lors de la sauvegarde:

  1. vérifier que l'action en cours est edit post
  2. obtenir les informations de publication à partir des données $ _POST, vérifier si la publication a le type de publication approprié et est en attente
  3. vérifier que le statut en attente ne peut être modifié que par un administrateur ou un "vrai" éditeur
  4. si toutes les vérifications précédentes réussissent, attribuez à l'utilisateur la capacité 'edit-others-posts' ('edit-post' sera automatiquement mappé)

Routine lorsque vous ne sauvegardez pas:

  1. Vérifiez que nous sommes sur l’un des 2 écrans d’intérêt, sinon ne faites rien
  2. comportement différent selon la capacité à filtrer:
    • lorsque la capacité de filtrage est 'edit-others-posts', nous n’avons pas de données de publication. Il suffit donc de l’affecter, mais seulement avant que la requête principale ne se produise pas et uniquement sur l’écran edit.php.
    • lorsque la capacité de filtrage est 'edit-post', obtenir les données de publication et si celle-ci est en attente, attribuer à l'utilisateur le cap 'edit-others-posts' ('edit-post' sera automatiquement mappé)

Il y a une dernière chose à faire. À l'aide du rôle personnalisé décrit dans le flux de travail, les utilisateurs ne pourront pas prévisualiser les autres publications en attente, même s'ils sont en mesure de les modifier.

Nous pouvons filtrer à nouveau la capacité, mais il existe un moyen plus simple: lors d'une requête principale (en utilisant l'un des douze hooks déclenchés par WP_Query), nous pouvons simplement prendre l'objet $wp_post_statuses['pending'] object et définir sa propriété public à true lorsque l'utilisateur actuel a notre rôle personnalisé: le seul effet est que les publications en attente sont prévisualisées et que, si nous ne modifions aucune fonctionnalité, nous pouvons rester en sécurité.

Ok, il suffit de traduire les mots dans le code:

class CustomEditorCaps {

  function manageCaps( $allcaps, $caps, $args, $user ) {    
    if ( ! $this->shouldManage( $args[0], $user ) ) {
      return $allcaps;
    }
    // Are we saving?
    $action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING );
    $method = strtoupper(filter_var($_SERVER['REQUEST_METHOD'], FILTER_SANITIZE_STRING ));
    if ( $method !== 'POST' ) { // not saving
      global $pagenow;
      // we are interested only on post list and  post edit screens
      if (
        is_admin()
        && in_array( $pagenow, array( 'post.php', 'post-new.php', 'edit.php' ), TRUE
      ) ) {
        $screen_id = $pagenow === 'edit.php' ? 'edit-post' : 'post';
        $allcaps = $this->maybeAllow( $args, $allcaps, $user, $screen_id );
      }
    } elseif ( $action === 'editpost' ) { // saving and right action
      $allcaps = $this->maybeAllowOnSave( $args, $allcaps, $user  );
    }
    return $allcaps; // always return: it's a filter
  }

  function lockPendingStatus( $data, $postarr ) {
    if (
       isset( $postarr['ID'] )
       && ! empty($postarr['ID'])
       && $data['post_type'] === 'post' // 'post' post type
       && $data['post_status'] !== 'pending' // a non pending status
       && ! current_user_can( 'delete_others_posts' ) // current user is not an admin
    ) {
       $orig = get_post_status( $postarr['ID'] ); 
       if ( $orig === 'pending' ) { // hey post was pending!
          $data['post_status'] = 'pending'; // let's restore pending status
       }
    }
    return $data; // always return: it's a filter
  }

  function allowPreview( $posts, $query ) {
    if ( is_admin()
      || ! $query->is_main_query()
      || empty( $posts )
      || ! $query->is_single
      || $posts[0]->post_type !== 'post'
    ) {
      return $posts; // return first argument: it's a filter
    }
    $status = get_post_status( $posts[0] );
    $post_status_obj = get_post_status_object( $status );
    if (
      ! $post_status_obj->public
      && $status === 'pending'
      && current_user_can('edit_others_pending_posts')
    ) {
      // post is pending and our user has our special role
      // allow preview
      global $wp_post_statuses;
      $wp_post_statuses[$status]->public = TRUE;
    }
    return $posts; // return first argument: it's a filter
  }

  private function maybeAllow( $args, $allcaps, $user, $screen ) {
    if ( $args[0] === 'edit_others_posts' ) {
      // if filtering 'edit_others_posts' we have no access to single post data
      // allow cap only on post list screen and before querying posts
      $allcaps['edit_others_posts'] = ! did_action('pre_get_posts')
        && $screen === 'edit-post';
      return $allcaps;
    }
    $post = get_post( $args[2] );
    if (  $post->post_status === 'pending' ) {
      $allcaps['edit_others_posts'] = TRUE;
    }
    return $allcaps; // always return: it's a filter
  }

  private function maybeAllowOnSave( $args, $allcaps, $user ) {
    $data = $this->getPostedData();
    if ( $data['post_type'] !== 'post' || (int) $data['post_ID'] <= 0 ) {
      return $allcaps;
    }
    $post = get_post( $data['post_ID'] );
    if (
      $post->post_status === 'pending'
      && $data['original_post_status'] === 'pending'
      && ( empty( $data['post_status'] ) || $data['post_status'] === 'pending' )
    ) {
      // if post is pending and will stay pending allow editing
      $allcaps['edit_others_posts'] = true;
    }
    return $allcaps;
  }

  private function shouldManage( $cap, $user ) {
    return is_admin() // not affect frontend
      && in_array( $cap, array( 'edit_others_posts', 'edit_post' ), TRUE )
      && ! $user->has_cap( 'delete_others_posts' ) // real editor or more
      && $user->has_cap( 'edit_others_pending_posts' ) // our role
      && ! defined( 'DOING_AJAX' ); // does not affect ajax
  }

  private function getPostedData() {
    return filter_input_array( INPUT_POST, array(
      'post_type'            => FILTER_SANITIZE_STRING,
      'post_ID'              => FILTER_SANITIZE_NUMBER_INT,
      'original_post_status' => FILTER_SANITIZE_STRING,
      'post_status'          => FILTER_SANITIZE_STRING,
    ) );
  }

}

Et ajoutez les 2 points d’accrochage appropriés: un pour le filtrage 'user_has_cap', un pour s’assurer que l’état en attente ne peut être modifié que par des administrateurs ou des éditeurs réels et le dernier filtre 'posts_results' pour autoriser la prévisualisation:

$cap_manager = new CustomEditorCaps;
add_filter( 'user_has_cap', array( $cap_manager, 'manageCaps' ), PHP_INT_MAX, 4 );
add_filter( 'posts_results', array( $cap_manager, 'allowPreview' ), 10, 2 );
add_filter( 'wp_insert_post_data', array( $cap_manager, 'lockPendingStatus' ), 10, 2 );

Une fois que vous avez tout ce code dans un plugin et que vous l'activez, vous n'avez plus qu'à affecter aux utilisateurs le rôle personnalisé créé par le plugin.


Tout le code est disponible sous forme de plugin, dans un fichier Gist here .

8
gmazzap