Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions resources/lang/en/permissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
'publish_{collection}_entries_desc' => 'Ability to change from draft to published and vice versa',
'reorder_{collection}_entries' => 'Reorder entries',
'reorder_{collection}_entries_desc' => 'Enables drag and drop reordering',
'view_other_authors_{collection}_entries' => "View other authors' entries",
'edit_other_authors_{collection}_entries' => "Edit other authors' entries",
'publish_other_authors_{collection}_entries' => "Manage publish state of other authors' entries",
'delete_other_authors_{collection}_entries' => "Delete other authors' entries",
Expand Down
8 changes: 5 additions & 3 deletions src/Auth/CorePermissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ protected function registerCollections()
$this->permission('delete {collection} entries'),
$this->permission('publish {collection} entries'),
$this->permission('reorder {collection} entries'),
$this->permission('edit other authors {collection} entries')->children([
$this->permission('publish other authors {collection} entries'),
$this->permission('delete other authors {collection} entries'),
$this->permission('view other authors {collection} entries')->children([
$this->permission('edit other authors {collection} entries')->children([
$this->permission('publish other authors {collection} entries'),
$this->permission('delete other authors {collection} entries'),
]),
]),
]),
])->replacements('collection', function () {
Expand Down
28 changes: 28 additions & 0 deletions src/Entries/ChangeAuthorFieldVisibility.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Statamic\Entries;

use Statamic\Contracts\Entries\Entry;
use Statamic\Events\EntryBlueprintFound;

class ChangeAuthorFieldVisibility
{
public function handle(EntryBlueprintFound $event)
{
if (! $event->entry) {
return;
}

if (! $event->authenticatedUser) {
return;
}

$authorVisibility = match (true) {
$event->authenticatedUser->cant('view-other-authors-entries', [Entry::class, $event->entry->collection()]) => 'hidden',
$event->authenticatedUser->cant('edit-other-authors-entries', [Entry::class, $event->entry->collection(), $event->blueprint]) => 'read_only',
default => 'visible',
};

$event->blueprint->ensureFieldHasConfig('author', ['visibility' => $authorVisibility]);
}
}
9 changes: 8 additions & 1 deletion src/Fieldtypes/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Statamic\Facades\Search;
use Statamic\Facades\Site;
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\Collections\QueriesAuthorEntries;
use Statamic\Http\Resources\CP\Entries\EntriesFieldtypeEntries;
use Statamic\Http\Resources\CP\Entries\EntriesFieldtypeEntry as EntryResource;
use Statamic\Query\OrderedQueryBuilder;
Expand All @@ -31,7 +32,8 @@

class Entries extends Relationship
{
use QueriesFilters;
use QueriesAuthorEntries,
QueriesFilters;

protected $categories = ['relationship'];
protected $keywords = ['entry'];
Expand Down Expand Up @@ -144,6 +146,11 @@ public function getIndexItems($request)
$query->whereIn('blueprint', $blueprints);
}

collect($this->getConfiguredCollections())
->map(fn ($handle) => Collection::findByHandle($handle))
->filter(fn ($collection) => User::current()->cant('view-other-authors-entries', [EntryContract::class, $collection]))
->each(fn ($collection) => $this->queryAuthorEntries($query, $collection));

$this->activeFilterBadges = $this->queryFilters($query, $filters, $this->getSelectionFilterContext());

if ($sort = $this->getSortColumn($request)) {
Expand Down
13 changes: 13 additions & 0 deletions src/Http/Controllers/CP/Collections/CollectionTreeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Statamic\Contracts\Entries\Collection;
use Statamic\Contracts\Entries\Entry as EntryContract;
use Statamic\Facades\Entry;
use Statamic\Facades\Site;
use Statamic\Facades\User;
Expand All @@ -24,6 +25,18 @@ public function index(Request $request, Collection $collection)
'site' => $site,
]);

if (User::current()->cant('view-other-authors-entries', [EntryContract::class, $collection])) {
$entriesFromOtherAuthors = collect($pages)
->map(fn ($page) => Entry::find($page['entry']))
->filter(fn ($entry) => $entry->blueprint()->hasField('author'))
->filter(fn ($entry) => ! $entry->authors()->contains(User::current()->id()))
->map->id();

$pages = collect($pages)
->filter(fn ($page) => ! $entriesFromOtherAuthors->contains($page['entry']))
->values()->all();
}

return ['pages' => $pages];
}

Expand Down
12 changes: 11 additions & 1 deletion src/Http/Controllers/CP/Collections/CollectionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

class CollectionsController extends CpController
{
use QueriesAuthorEntries;

public function index(Request $request)
{
$this->authorize('index', CollectionContract::class, __('You are not authorized to view collections.'));
Expand Down Expand Up @@ -55,10 +57,18 @@ private function collections()
|| User::current()->can('view', $collection)
&& $collection->sites()->contains(Site::selected()->handle());
})->map(function ($collection) {
$entriesCount = $collection->queryEntries()
->where('site', Site::selected())
->when(
User::current()->cant('view-other-authors-entries', [EntryContract::class, $collection]),
fn ($query) => $this->queryAuthorEntries($query, $collection)
)
->count();

return [
'id' => $collection->handle(),
'title' => $collection->title(),
'entries' => $collection->queryEntries()->where('site', Site::selected())->count(),
'entries' => $entriesCount,
'edit_url' => $collection->editUrl(),
'delete_url' => $collection->deleteUrl(),
'entries_url' => cp_route('collections.show', $collection->handle()),
Expand Down
29 changes: 13 additions & 16 deletions src/Http/Controllers/CP/Collections/EntriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
class EntriesController extends CpController
{
use ExtractsFromEntryFields,
QueriesAuthorEntries,
QueriesFilters;

public function index(FilteredRequest $request, $collection)
Expand Down Expand Up @@ -69,21 +70,25 @@ protected function indexQuery($collection)
$query = $collection->queryEntries();

if ($search = request('search')) {
if ($collection->hasSearchIndex()) {
return $collection
->searchIndex()
->ensureExists()
->search($search)
->where('collection', $collection->handle());
}

$query->where('title', 'like', '%'.$search.'%');
}

if ($search && $collection->hasSearchIndex()) {
$query = $collection
->searchIndex()
->ensureExists()
->search($search)
->where('collection', $collection->handle());
}

if (Site::multiEnabled()) {
$query->whereIn('site', Site::authorized()->map->handle()->all());
}

if (User::current()->cant('view-other-authors-entries', [EntryContract::class, $collection])) {
$this->queryAuthorEntries($query, $collection);
}

return $query;
}

Expand All @@ -101,10 +106,6 @@ public function edit(Request $request, $collection, $entry)

$blueprint->setParent($entry);

if (User::current()->cant('edit-other-authors-entries', [EntryContract::class, $collection, $blueprint])) {
$blueprint->ensureFieldHasConfig('author', ['visibility' => 'read_only']);
}

[$values, $meta, $extraValues] = $this->extractFromFields($entry, $blueprint);

if ($hasOrigin = $entry->hasOrigin()) {
Expand Down Expand Up @@ -300,10 +301,6 @@ public function create(Request $request, $collection, $site)
throw new \Exception(__('A valid blueprint is required.'));
}

if (User::current()->cant('edit-other-authors-entries', [EntryContract::class, $collection, $blueprint])) {
$blueprint->ensureFieldHasConfig('author', ['visibility' => 'read_only']);
}

$values = Entry::make()->collection($collection)->values()->all();

if ($collection->hasStructure() && $request->parent) {
Expand Down
40 changes: 40 additions & 0 deletions src/Http/Controllers/CP/Collections/QueriesAuthorEntries.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Statamic\Http\Controllers\CP\Collections;

use Illuminate\Support\Collection as SupportCollection;
use Statamic\Contracts\Entries\Collection;
use Statamic\Contracts\Query\Builder;
use Statamic\Facades\User;
use Statamic\Fields\Blueprint;

trait QueriesAuthorEntries
{
protected function queryAuthorEntries(Builder $query, Collection $collection): void
{
$query
->where(fn ($query) => $query
->whereNotIn('collection', [$collection->handle()]) // Needed for entries fieldtypes configured for multiple collections
->orWhere(fn ($query) => $query
->whereIn('blueprint', $this->blueprintsWithAuthor($collection->entryBlueprints()))
->whereIn('author', [User::current()->id()])
->orWhereJsonContains('author', User::current()->id())
)
->orWhereIn('blueprint', $this->blueprintsWithoutAuthor($collection->entryBlueprints()))
);
}

protected function blueprintsWithAuthor(SupportCollection $blueprints): array
{
return $blueprints
->filter(fn (Blueprint $blueprint) => $blueprint->hasField('author'))
->map->handle()->all();
}

protected function blueprintsWithoutAuthor(SupportCollection $blueprints): array
{
return $blueprints
->filter(fn (Blueprint $blueprint) => ! $blueprint->hasField('author'))
->map->handle()->all();
}
}
6 changes: 6 additions & 0 deletions src/Http/Resources/CP/Entries/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Statamic\Http\Resources\CP\Entries;

use Illuminate\Http\Resources\Json\ResourceCollection;
use Statamic\Contracts\Entries\Entry;
use Statamic\CP\Column;
use Statamic\Facades\User;
use Statamic\Http\Resources\CP\Concerns\HasRequestedColumns;

class Entries extends ResourceCollection
Expand Down Expand Up @@ -42,6 +44,10 @@ private function setColumns()

$columns->put('status', $status);

if (User::current()->cant('view-other-authors-entries', [Entry::class, $this->blueprint->parent()])) {
$columns->get('author')?->listable(false);
}

if ($key = $this->columnPreferenceKey) {
$columns->setPreferred($key);
}
Expand Down
9 changes: 9 additions & 0 deletions src/Policies/EntryPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public function view($user, $entry)
return false;
}

if ($this->hasAnotherAuthor($user, $entry)) {
return $user->hasPermission("view other authors {$entry->collectionHandle()} entries");
}

return $this->edit($user, $entry)
|| $user->hasPermission("view {$entry->collectionHandle()} entries");
}
Expand All @@ -49,6 +53,11 @@ public function edit($user, $entry)
return $user->hasPermission("edit {$entry->collectionHandle()} entries");
}

public function viewOtherAuthorsEntries($user, $collection)
{
return $user->hasPermission("view other authors {$collection->handle()} entries");
}

public function editOtherAuthorsEntries($user, $collection, $blueprint = null)
{
$blueprint = $blueprint ?? $collection->entryBlueprint();
Expand Down
1 change: 1 addition & 0 deletions src/Providers/EventServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class EventServiceProvider extends ServiceProvider
],
\Statamic\Events\EntryBlueprintFound::class => [
\Statamic\Entries\AddSiteColumnToBlueprint::class,
\Statamic\Entries\ChangeAuthorFieldVisibility::class,
],
\Statamic\Events\ResponseCreated::class => [
\Statamic\Listeners\ClearState::class,
Expand Down
43 changes: 43 additions & 0 deletions src/UpdateScripts/AddViewOtherAuthorsEntriesPermissions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Statamic\UpdateScripts;

use Statamic\Facades\Role;

class AddViewOtherAuthorsEntriesPermissions extends UpdateScript
{
public function shouldUpdate($newVersion, $oldVersion)
{
return $this->isUpdatingTo('5.59');
}

public function update()
{
Role::all()->each(fn ($role) => $this->updateRole($role));
}

private function updateRole($role)
{
$this->getMatchingPermissions($role->permissions(), '/^edit other authors (\w+) entries$/')
->filter
->capture
->each(function ($match) use ($role) {
$role->addPermission("view other authors {$match->capture} entries");
});

$role->save();
}

private function getMatchingPermissions($permissions, $regex)
{
return $permissions
->map(function ($permission) use ($regex) {
$found = preg_match($regex, $permission, $matches);

return $found
? (object) ['permission' => $permission, 'capture' => $matches[1] ?? null]
: null;
})
->filter();
}
}
Loading
Loading