Erlauben Sie dem Benutzer, "edit_others_posts" nur zu speichern, nicht zu veröffentlichen

7

Ich möchte ein paar Redakteure hinzuzufügen , die können edit_others_posts , aber ich will nicht , sie in der Lage sein , zu veröffentlichen andere Beiträge, nur speichern die Post durch einen Klick auf den Absenden von Review - Taste.

Wie kann ich das machen?

EDIT: Um dies im Detail zu erklären. Derzeit können wir einem Benutzer nicht erlauben, andere Beiträge zu bearbeiten, indem wir nur den Beitrag speichern. Wenn dies edit_others_postfür den Benutzer aktiviert ist, kann er den Beitrag veröffentlichen.

Workflow, den ich anstrebe:

  1. Redakteure können nur andere ausstehende Beiträge bearbeiten ( hier gelöst ).
  2. Redakteure können den ausstehenden Beitrag speichern, aber nicht veröffentlichen. Daher steht ihnen die Schaltfläche " Zur Überprüfung senden" zur Verfügung (dies ist die Schaltfläche "Beitrag aktualisieren", wenn sich ein Beitrag im ausstehenden Modus befindet).
Christine Cooper
quelle
Sie möchten also verhindern, dass Benutzer einer bestimmten Rolle den Status eines Posts ändern können?
Tom J Nowell
@ TomJNowell Siehe meine aktualisierte Frage mit weiteren Details.
Christine Cooper
Haben Sie die Aktionen zum Umschalten nach dem Status ausprobiert? (Suchkern)
Kaiser
Bisher noch nichts davon gehört. Wie funktioniert es in dieser Hinsicht?
Christine Cooper
Beispielfrage zum Post Transition Hook.
Kaiser

Antworten:

8

Wenn ich das gut verstehe, sollte ein Benutzer, der eine besondere Rolle auf Ihrer Website spielt, Folgendes tun:

  • Sie können eigene Beiträge in allen Status bearbeiten, aber "veröffentlichen" und nicht veröffentlichen. Senden Sie sie einfach zur Überarbeitung
  • Sie können andere Beiträge nur bearbeiten, wenn sie noch ausstehen, aber nicht veröffentlicht werden. Senden Sie sie einfach zur Überarbeitung
  • Sie können niemals andere Beiträge löschen, unabhängig vom Status

Wenn ja, scheint es mir eine Rolle zu sein, die eher einem "Autor" als einem "Herausgeber" ähnelt.

Das ist nur der Unterschied zum Autor

  • Benutzer Ihrer Rolle können veröffentlichte Beiträge nicht bearbeiten, selbst wenn sie der Autor sind
  • Benutzer Ihrer Rolle können andere ausstehende Beiträge bearbeiten, aber nicht veröffentlichen

Der erste Vorschlag, den ich machen kann, ist, eine benutzerdefinierte Rolle zu erstellen, wobei die Rolle "Autor" als Zeichenpunkt verwendet wird, die 3 unerwünschten Kappen entfernt und die benutzerdefinierte hinzugefügt wird, eine einfache Klasse, die dies tut:

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);
  }

}

Fügen wir die Aktion zur Aktivierung / Deaktivierung des Plugins hinzu:

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

Hier gehe ich davon aus, dass sich der vorherige Code in der Haupt-Plugin-Datei befindet.

Die oben festgelegten Funktionen gelten für jeden Beitrag, unabhängig vom Autor oder Beitragsstatus des Beitrags. Jetzt müssen wir dem Benutzer mit unserer benutzerdefinierten Rolle erlauben, andere Beiträge zu bearbeiten, wenn sie ausstehen.

Das erste Problem ist, dass auf dem Postlistenbildschirm ( edit.php), wenn die Funktion edit_others_postsfür den Benutzer nicht aktiviert ist (und für unsere benutzerdefinierte Rolle nicht), Posts von anderen Benutzern nicht in der Liste angezeigt werden, da sie von entfernt wurden Die Abfrage und wenn die Abfrage erfolgt, haben wir keinen Zugriff auf die Post-Daten. Daher müssen wir nur die Funktion zuweisen, unabhängig vom Post-Status, zumindest bis die Abfrage ausgeführt wird.

Das zweite Problem besteht darin, dass edit_others_postswir beim Speichern, bevor wir dem Benutzer mit benutzerdefinierten Rollen die Obergrenze geben, nicht nur überprüfen müssen, ob der aktuelle Status "ausstehend" ist, sondern auch, dass der Benutzer nicht versucht, ihn zu ändern. Dies kann durch Betrachten von Informationen in $_POSTDaten erfolgen. Dies bedeutet, dass wir zwei "Routinen" benötigen, eine, die auf Administrationsbildschirmen ausgeführt wird ( edit.phpund post.phpdie zweite, die während des Speicherns nach dem Speichern ausgeführt wird).

Um unserem benutzerdefinierten Rollenbenutzer die edit_others_postMöglichkeit zu geben, nur ausstehende Beiträge zu verfassen, fügen Sie einen Filter hinzu 'user_has_cap'.

Innerhalb des Filterrückrufs können wir diesen Workflow implementieren:

  1. Überprüfen Sie, ob die Filterfunktion eine der beiden ist, die wir verwalten möchten ( 'edit-post'oder 'edit-others-posts'überprüfen Sie, ob wir uns im Administrator befinden, ob der Benutzer über unsere benutzerdefinierten Funktionen verfügt und kein Editor oder Administrator ist. Wenn alle diese Bedingungen erfüllt sind, können wir dies Weiter, sonst müssen wir nichts tun, dh die ursprünglichen Fähigkeiten zurückgeben
  2. Überprüfen Sie, ob wir speichern oder nicht, und führen Sie zwei verschiedene Routinen aus:
    • Routine beim Speichern
    • Routine, wenn nicht gespeichert wird

Routine beim Speichern:

  1. Überprüfen Sie, ob die aktuelle Aktion Beitrag bearbeiten ist
  2. Rufen Sie die Post-Informationen aus $ _POST-Daten ab und prüfen Sie, ob der Post den richtigen Post-Typ hat und aussteht
  3. Überprüfen Sie, ob der Status "Ausstehend" nur von einem Administrator oder einem "echten" Editor geändert werden kann
  4. Wenn alle vorherigen Prüfungen bestanden wurden, weisen Sie dem Benutzer die 'edit-others-posts'Funktion zu ( 'edit-post'wird automatisch zugeordnet).

Routine beim Nichtsparen:

  1. Überprüfen Sie, ob wir uns in einem der 2 Bildschirme von Interesse befinden. Wenn nicht, tun Sie nichts
  2. unterschiedliches Verhalten je nach Filterfähigkeit:
    • Wenn die Fähigkeit zum Filtern vorhanden ist 'edit-others-posts', haben wir keine Post-Daten. Weisen Sie sie einfach zu, sondern nur, bevor die Hauptabfrage nicht bereits erfolgt und nur auf dem edit.phpBildschirm
    • Wenn die Fähigkeit zum Filtern darin besteht 'edit-post', Post-Daten abzurufen, und wenn der Post aussteht, weisen Sie dem Benutzer die 'edit-others-posts'Obergrenze zu ( 'edit-post'wird automatisch zugeordnet).

Es gibt noch etwas zu tun. Mithilfe des beschriebenen Workflows können Benutzer in benutzerdefinierten Rollen keine Vorschau auf ausstehende Beiträge anzeigen, selbst wenn sie diese bearbeiten können.

Wir können die Funktion erneut filtern, aber es gibt einen einfacheren Weg: Während der Hauptabfrage (unter Verwendung eines der Dutzend von ausgelösten Hooks WP_Query) können wir einfach das $wp_post_statuses['pending']Objekt nehmen und seine publicEigenschaft auf true setzen, wenn der aktuelle Benutzer unsere benutzerdefinierte Rolle hat: Der einzige Effekt ist Ausstehende Beiträge können in der Vorschau angezeigt werden. Wenn wir keine Funktionen ändern, können wir sicher bleiben.

Ok, übersetze einfach Wörter in 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,
    ) );
  }

}

Fügen Sie die beiden relevanten Hooks hinzu: einen zum Filtern 'user_has_cap', einen zum Sicherstellen, dass der Status "Ausstehend" nur von Administratoren oder echten Editoren geändert werden kann, und den letzten Filter 'posts_results', um eine Vorschau zu ermöglichen:

$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 );

Sobald Sie den gesamten Code in einem Plugin haben und ihn aktivieren, müssen Sie nur den Benutzern das benutzerdefinierte Rollen-Plugin zuweisen.


Der gesamte Code ist als Plugin verfügbar, in einer Übersicht hier .

gmazzap
quelle
1
+ gmazzap, dass das Wesentliche nicht mehr verfügbar ist
mawalker
1
@mawalker hat den Gist-Link aktualisiert, auch wenn das kein Code ist, auf den ich heute stolz sein würde ...
gmazzap