it-swarm.com.de

Benutzer darf "edit_others_posts" nur speichern, nicht veröffentlichen

Ich möchte ein paar Editoren hinzufügen, die edit_others_posts können , aber ich möchte nicht, dass sie veröffentlichen können andere Beiträge, nur speichern Sie den Beitrag, indem Sie auf die Schaltfläche Zur Überprüfung einreichen klicken.

Wie kann ich das machen?

EDIT: Um dies im Detail zu erklären. Gegenwärtig können wir einem Benutzer nicht erlauben, andere Beiträge zu bearbeiten, indem er nur den Beitrag speichert. Wenn edit_others_post fü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 , die hier gelöst wurden ).
  2. Redakteure können den ausstehenden Beitrag speichern , aber nicht veröffentlichen. Daher steht ihnen die Schaltfläche Zur Überprüfung einreichen zur Verfügung (dies ist die Schaltfläche "Beitrag aktualisieren", wenn sich ein Beitrag im ausstehenden Modus befindet).
7

Wenn ich es richtig verstehe, sollte der Benutzer, der eine besondere Rolle auf Ihrer Website spielt,:

  • Sie können eigene Posts in allen Status bearbeiten, aber nicht veröffentlichen. Senden Sie sie einfach zur Überarbeitung
  • Sie können andere Beiträge nur bearbeiten, wenn sie noch ausstehen, aber nicht veröffentlichen. Senden Sie sie einfach zur Überarbeitung
  • Niemals in der Lage sein, andere Beiträge zu löschen, egal welchen Status

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

Das ist der einzige 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 'author' als Strting Point verwendet wird, die 3 unerwünschten Caps zu entfernen und die benutzerdefinierte hinzuzufügen, eine einfache Klasse, die das macht:

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 der vorherige Code in der Haupt-Plugin-Datei enthalten ist.

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

Das erste Problem, auf das wir stoßen, ist, dass auf dem Bildschirm der Beitragsliste (edit.php), wenn die Funktion edit_others_posts für den Benutzer nicht aktiviert ist (und für unsere benutzerdefinierte Rolle nicht), Beiträge anderer Benutzer nicht in der Liste angezeigt werden, da sie entfernt werden Außerhalb der Abfrage und wenn die Abfrage ausgeführt wird, 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 beim Speichern, bevor dem Benutzer mit benutzerdefinierten Rollen die Obergrenze für edit_others_posts gegeben wird, nicht nur überprüft werden muss, ob der aktuelle Status "ausstehend" ist, sondern auch, ob der Benutzer versucht, ihn zu ändern. Dies kann durch Betrachten von Informationen in $_POST Daten erfolgen. Dies bedeutet, dass wir zwei "Routinen" benötigen, eine, die auf den Admin-Bildschirmen (edit.php und post.php) ausgeführt wird, die zweite, die beim Speichern nach dem Speichern ausgeführt wird.

Die Möglichkeit, unserem benutzerdefinierten Rollenbenutzer die Funktion edit_others_post nur für ausstehende Posts zu erteilen, besteht darin, 'user_has_cap' einen Filter hinzuzufügen.

Innerhalb des Filter-Callbacks können wir diesen Workflow implementieren:

  1. überprüfen Sie, ob es sich bei der Filterfunktion um eine der 2 Funktionen handelt, die wir verwalten möchten ('edit-post' oder 'edit-others-posts'). Überprüfen Sie, ob der Benutzer über unsere benutzerdefinierten Funktionen verfügt und kein Editor oder Administrator ist. Wenn alle diese Bedingungen erfüllt sind wir können weitermachen, sonst müssen wir nichts tun, dh ursprüngliche Fähigkeiten zurückgeben
  2. überprüfe ob wir speichern oder nicht und führe 2 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 den $ _POST-Daten ab. Überprüfen Sie, ob der Post den richtigen Post-Typ hat und noch aussteht
  3. stellen Sie sicher, dass der Status "Ausstehend" nur von einem Administrator oder einem "echten" Redakteur geändert werden kann
  4. wenn alle vorherigen Prüfungen bestanden wurden, weisen Sie dem Benutzer die Funktion 'edit-others-posts' zu ('edit-post' wird automatisch zugeordnet).

Routine, wenn nicht gespeichert wird:

  1. Stellen Sie sicher, dass wir in einem der beiden Bildschirme von Interesse sind. Wenn nicht, tun Sie nichts
  2. unterschiedliches Verhalten je nach Filterfähigkeit:
    • wenn die Fähigkeit zum Filtern 'edit-others-posts' ist, haben wir keine Post-Daten. Ordnen Sie diese also nur zu, bevor die Hauptabfrage noch nicht erfolgt und nur auf dem edit.php-Bildschirm
    • wenn die Fähigkeit zum Filtern 'edit-post' ist, werden Post-Daten abgerufen, und wenn der Post aussteht, wird dem Benutzer die 'edit-others-posts'-Obergrenze zugewiesen ('edit-post' wird automatisch zugeordnet).

Es gibt noch etwas zu tun. Bei Verwendung der beschriebenen benutzerdefinierten Workflow-Rolle können Benutzer keine Vorschau anderer ausstehender Beiträge anzeigen, selbst wenn sie diese bearbeiten können.

Wir können die Fähigkeit wieder filtern, aber es gibt einen einfacheren Weg: Während der Hauptabfrage (unter Verwendung eines der Dutzenden von Hooks, die von WP_Query ausgelöst wurden) können wir einfach das $wp_post_statuses['pending'] -Objekt nehmen und seine public -Eigenschaft auf true setzen, wenn der aktuelle Benutzer unsere hat Benutzerdefinierte Rolle: Der einzige Effekt ist, dass ausstehende Posts in der Vorschau angezeigt werden können. Wenn wir keine Funktion ändern, können wir sicher bleiben.

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

}

Und fügen Sie die 2 relevanten Haken hinzu: einen zum Filtern von 'user_has_cap', einen, um sicherzustellen, dass der Status "Ausstehend" nur von Administratoren oder echten Redakteuren geändert werden kann, und den letzten zum Filtern von '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 aktivieren, müssen Sie nur den Benutzern zuweisen, die das benutzerdefinierte Rollen-Plugin erstellt.


Alle Codes als Plugin verfügbar, in einer Gist hier .

8
gmazzap