Letzte Woche hatte ich euch ja in einem Artikel beschrieben, wie ich in einem Custom Post Type „Portrait“, einem Nutzer ermöglicht habe, ein einzelnes Portrait bearbeiten zu können. Hierzu wurde auch eine eigene Rolle angelegt, mit der man die Berechtigungen besser steuern kann.
Nun gab es aber in diesem Projekt auch die Anforderung, dass ein Nutzer Bilder hochladen kann. Dabei gab es nun aber ein Problem. Wenn ich einem Nutzer erlaube, Bilder über die Mediathek hochzuladen, dann kann er auch die Bilder aller anderen Nutzer sehen. Das ist natürlich sehr unschön, gerade dann, wenn die Mediathek mit der Zeit sehr groß wird.
Nutzern den Upload von Bildern erlauben
Zuerst einmal musste ich natürlich der neuen Nutzerrolle generell das Recht einräumen, Bilder hochzuladen (ihr könnt aber natürlich auch eine bestehende Rolle wie „Abonnent“ verändern):
$portraiteditor = get_role( 'portraiteditor' ); $portraiteditor->add_cap( 'upload_files' );
Nachdem der Nutzer nun dieses Recht bekommen hat, kann er über die Mediathek Bilder hochladen oder aus den bestehenden Bildern auswählen.
Nur die Bilder des Nutzers anzeigen
Im nächsten Schritt müssen wir die Abfrage für die Mediathek so verändern, dass der Nutzer nur seine eigenen Bilder sieht, es sei denn, der Nutzer hat auch das Recht, die Portraits von anderen Nutzern zu bearbeiten (also wie ein Redakteur für Beiträge). Hierzu verwenden wir wiederum einen einfachen Filter:
function portrait_show_curr_user_attachments( $query = array() ) { $user_id = get_current_user_id(); if ( $user_id && ! current_user_can( 'edit_others_portraits' ) ) { $query['author'] = $user_id; } return $query; } add_filter( 'ajax_query_attachments_args', 'portrait_show_curr_user_attachments' );
Mehr ist nicht notwendig, um den Nutzern den Upload vom Bildern (und anderen Dateien) zu erlauben. Der Filter wirkt sich im Übrigen nicht nur auf das Hochladen der Bilder im Beitrag aus, sondern auch auf den Menüpunkt „Medien“ im Backend. Diesen hatte ich aber in dem Projekt ohnehin ausgeblendet.
Bearbeiten und Löschen erlauben
Eigentlich könnten wir ja nun schon fertig sein. Aber leider hat die Lösung einen kleinen Nachteil: der Nutzer kann seine selbst hochgeladenen Bilder werden bearbeiten noch löschen. Gerade die Bearbeitung, um zum Beispiel Bilder zuzuschneiden, war in dem Projekt erforderlich.
Die Lösung hierzu war etwas aufwändiger, denn es gibt leider eine Berechtigung „edit_attachment“ oder „delete_attachment“. Beider wird über die „Meta-Berechtigungen“ für Beiträge „edit_post“ bzw. „delete_post“ gesteuert. Nun wäre es aber eine ganz schlechte Idee, allen Nutzern auch noch das recht zu geben, Beiträge zu erstellen oder zu löschen, wenn man ihnen doch nur das Recht geben möchte, eigene Medien zu Bearbeiten/Löschen.
Nach langer Recherche und ausführlichem Debugging im WordPress Core bin ich dann schließlich zu folgender Lösung für das Projekt gekommen:
function portrait_has_cap_delete_attachments( $allcaps, $cap, $args ) { // Bail out if the capability is not "delete_post" or "edit_post" if ( 'delete_post' != $args[0] && 'edit_post' != $args[0] ) { return $allcaps; } // Bail out for users who can already edit others posts if ( $allcaps[ 'edit_others_portraits' ] ) { return $allcaps; } // Load the post data $post = get_post( $args[2] ); // Bail out if no post was found or it's not an attachment if ( empty( $post ) || 'attachment' != $post->post_type ) { return $allcaps; } // Bail out if the user is not the post author if ( $args[1] != $post->post_author ) { return $allcaps; } // If we haven't bailed out until this point, allow the user to delete/edit $allcaps[ $cap[0] ] = true; return $allcaps; } add_filter( 'user_has_cap', 'cportrait_has_cap_delete_attachments', 10, 3 );
Der Filter „user_has_cap“ wird für jede einzelne Berechtigung aufgerufen, die bei einem Seitenaufruf geprüft werden soll. Daher sollte man hierbei immer sehr früh abbrechen, wenn man feststellt, dass es um eine andere Berechtigung geht. Daher prüfe ich zuerst, ob ein „Post“ bearbeitet oder gelöscht werden soll (Zeile 3), anschließend, ob der Nutzer ohnehin die Rechte für alle Portraits hat (Zeile 8). Erst dann lade ich den Post aus der Datenbank und prüfe, ob es sich um eine Mediendatei handelt (Zeile 16). Ist auch dies noch der Fall, prüfe ich, ob die Mediendatei dem Nutzer gehört (Zeile 21). Sollte einer dieser Tests „fehlschlagen“, dann gebe ich das Berechtigungs-Array unverändert zurück. Sind die aber erfolgreich, dann erweitere ich die Berechtigungen um die „Meta-Berechtigung“ (Zeile 26). Somit kann der Nutzer dann die Mediendatei über die entsprechenden Links und Funktionen erweitern.
Fazit
Ich hoffe ihr konntet auch in diesem Beitrag wieder etwas Neues dazulernen. Das Berechtigungssystem von WordPress kann an manchen Stellen schon mal trickreich sein, aber es gibt meistens eine einfache Lösung. Man muss sie nur finden 🙂
Natürlich habe ich euch wieder ein GIST mit dem Code veröffentlicht. Ich habe dazu den Code von letzter Woche um die neuen Funktionen erweitert, damit ihr das Ganze zusammen testen könnt.
Habt ihr vielleicht auch mal ein schwieriges Problem mit dem Berechtigungssystem von WordPress gehabt und konntet ihr es lösen? Dann würde ich mich sehr über einen Kommentar freuen.