Skip to content

Commit 1cfa0c4

Browse files
authored
feat: Add user-group sharing support + activate feature (#3425)
1 parent a23645a commit 1cfa0c4

31 files changed

Lines changed: 583 additions & 166 deletions

app/Actions/Sharing/Propagate.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private function applyUpdate(Album $album): void
4949
// for each descendant, create a new permission if it does not exist.
5050
// or update the existing permission.
5151
$descendants = $album->descendants()->getQuery()->select('id')->pluck('id');
52-
$permissions = $album->access_permissions()->whereNotNull('user_id')->get();
52+
$permissions = $album->access_permissions()->whereNotNull(APC::USER_ID)->orWhereNotNull(APC::USER_GROUP_ID)->get();
5353

5454
// This is super inefficient.
5555
// It would be better to do it in a single query...
@@ -59,6 +59,7 @@ private function applyUpdate(Album $album): void
5959
$perm = AccessPermission::updateOrCreate([
6060
APC::BASE_ALBUM_ID => $descendant,
6161
APC::USER_ID => $permission->user_id,
62+
APC::USER_GROUP_ID => $permission->user_group_id,
6263
], [
6364
APC::GRANTS_FULL_PHOTO_ACCESS => $permission->grants_full_photo_access,
6465
APC::GRANTS_DOWNLOAD => $permission->grants_download,
@@ -106,7 +107,10 @@ private function applyOverwrite(Album $album): void
106107
// 2. applying the new permissions.
107108

108109
DB::table(APC::ACCESS_PERMISSIONS)
109-
->whereNotNull('user_id')
110+
->where(fn ($q) => $q
111+
->whereNotNull(APC::USER_ID)
112+
->orWhereNotNull(APC::USER_GROUP_ID)
113+
)
110114
->whereIn(
111115
'base_album_id',
112116
DB::table('albums')
@@ -122,7 +126,7 @@ private function applyOverwrite(Album $album): void
122126
->where('_rgt', '<', $album->_rgt)
123127
->pluck('id');
124128

125-
$access_permissions = $album->access_permissions()->whereNotNull('user_id')->get();
129+
$access_permissions = $album->access_permissions()->whereNotNull('user_id')->orWhereNotNull('user_group_id')->get();
126130

127131
$new_perm = $access_permissions->reduce(
128132
fn (?array $acc, AccessPermission $permission) => array_merge(
@@ -131,6 +135,7 @@ private function applyOverwrite(Album $album): void
131135
fn ($descendant_id) => [
132136
APC::BASE_ALBUM_ID => $descendant_id,
133137
APC::USER_ID => $permission->user_id,
138+
APC::USER_GROUP_ID => $permission->user_group_id,
134139
APC::GRANTS_FULL_PHOTO_ACCESS => $permission->grants_full_photo_access,
135140
APC::GRANTS_DOWNLOAD => $permission->grants_download,
136141
APC::GRANTS_UPLOAD => $permission->grants_upload,

app/Actions/Sharing/Share.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,21 @@ class Share
1717
* Create an access permission from a resource.
1818
*
1919
* @param AccessPermissionResource $access_permission_resource
20+
* @param int|null $user_id
21+
* @param int|null $user_group_id
2022
* @param string $base_album_id
2123
*
2224
* @return AccessPermission
2325
*/
24-
public function do(AccessPermissionResource $access_permission_resource, int $user_id, string $base_album_id): AccessPermission
25-
{
26+
public function do(
27+
AccessPermissionResource $access_permission_resource,
28+
string $base_album_id,
29+
?int $user_id = null,
30+
?int $user_group_id = null,
31+
): AccessPermission {
2632
$perm = new AccessPermission();
2733
$perm->user_id = $user_id;
34+
$perm->user_group_id = $user_group_id;
2835
$perm->base_album_id = $base_album_id;
2936
$perm->grants_full_photo_access = $access_permission_resource->grants_full_photo_access;
3037
$perm->grants_download = $access_permission_resource->grants_download;
@@ -33,6 +40,7 @@ public function do(AccessPermissionResource $access_permission_resource, int $us
3340
$perm->grants_delete = $access_permission_resource->grants_delete;
3441
$perm->load('user');
3542
$perm->load('album');
43+
$perm->load('user_group');
3644
$perm->save();
3745

3846
return $perm;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/**
4+
* SPDX-License-Identifier: MIT
5+
* Copyright (c) 2017-2018 Tobias Reich
6+
* Copyright (c) 2018-2025 LycheeOrg.
7+
*/
8+
9+
namespace App\Constants;
10+
11+
class UsersUserGroupsConstants
12+
{
13+
// computed table name
14+
public const USERS_USER_GROUPS = 'users_user_groups';
15+
16+
// Id names
17+
public const USER_ID = 'user_id';
18+
public const USER_GROUP_ID = 'user_group_id';
19+
20+
// Attributes name
21+
public const ROLE = 'role';
22+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/**
4+
* SPDX-License-Identifier: MIT
5+
* Copyright (c) 2017-2018 Tobias Reich
6+
* Copyright (c) 2018-2025 LycheeOrg.
7+
*/
8+
9+
namespace App\Contracts\Http\Requests;
10+
11+
interface HasUserGroupIds
12+
{
13+
/**
14+
* @return int[]
15+
*/
16+
public function userGroupIds(): array;
17+
}

app/Contracts/Http/Requests/RequestAttribute.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class RequestAttribute
1414

1515
public const USER_ID_ATTRIBUTE = 'user_id';
1616
public const USER_IDS_ATTRIBUTE = 'user_ids';
17+
public const USER_GROUP_IDS_ATTRIBUTE = 'group_ids';
1718

1819
public const GROUP_ID = 'group_id';
1920
public const NAME_ATTRIBUTE = 'name';

app/Http/Controllers/Gallery/SharingController.php

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,35 @@ class SharingController extends Controller
4646
public function create(AddSharingRequest $request, Share $share): array
4747
{
4848
// delete any already created.
49-
AccessPermission::whereIn('user_id', $request->userIds())
50-
->whereIn('base_album_id', $request->albumIds())
49+
AccessPermission::whereIn(APC::BASE_ALBUM_ID, $request->albumIds())
50+
->where(fn ($q) => $q->whereIn(APC::USER_ID, $request->userIds())
51+
->orWhereIn(APC::USER_GROUP_ID, $request->userGroupIds()))
5152
->delete();
5253

5354
$access_permissions = [];
55+
5456
// Not optimal, but this is barely used, so who cares.
5557
// A better approach would be to do a massive insert in a single SQL query from the cross product.
56-
foreach ($request->userIds() as $user_id) {
57-
foreach ($request->albumIds() as $album_id) {
58-
$access_permissions[] = $share->do($request->permResource(), $user_id, $album_id);
58+
foreach ($request->albumIds() as $album_id) {
59+
foreach ($request->userIds() as $user_id) {
60+
// Create a new sharing permission for each user and album combination.
61+
// This is not optimal, but it is simple and works.
62+
// A better approach would be to do a massive insert in a single SQL query from the cross product.
63+
$access_permissions[] = $share->do(
64+
access_permission_resource: $request->permResource(),
65+
user_id: $user_id,
66+
base_album_id: $album_id
67+
);
68+
}
69+
foreach ($request->userGroupIds() as $user_group_id) {
70+
// Create a new sharing permission for each user group and album combination.
71+
// This is not optimal, but it is simple and works.
72+
// A better approach would be to do a massive insert in a single SQL query from the cross product.
73+
$access_permissions[] = $share->do(
74+
access_permission_resource: $request->permResource(),
75+
user_group_id: $user_group_id,
76+
base_album_id: $album_id
77+
);
5978
}
6079
}
6180

@@ -92,9 +111,10 @@ public function edit(EditSharingRequest $request): AccessPermissionResource
92111
*/
93112
public function list(ListSharingRequest $request): Collection
94113
{
95-
$query = AccessPermission::with(['album', 'user']);
96-
$query = $query->whereNotNull(APC::USER_ID);
114+
$query = AccessPermission::with(['album', 'user', 'user_group']);
97115
$query = $query->where(APC::BASE_ALBUM_ID, '=', $request->album()->id);
116+
$query = $query->where(fn ($q) => $q->whereNotNull(APC::USER_ID)
117+
->orWhereNotNull(APC::USER_GROUP_ID));
98118

99119
return AccessPermissionResource::collect($query->get());
100120
}
@@ -108,12 +128,14 @@ public function list(ListSharingRequest $request): Collection
108128
*/
109129
public function listAll(ListAllSharingRequest $request): Collection
110130
{
111-
$query = AccessPermission::with(['album', 'user']);
131+
$query = AccessPermission::with(['album', 'user', 'user_group']);
112132
$query = $query->when(
113133
!Auth::user()->may_administrate,
114134
fn ($q) => $q->whereIn('base_album_id', BaseAlbumImpl::select('id')
115-
->where('owner_id', '=', Auth::id())));
116-
$query = $query->whereNotNull('user_id');
135+
->where('owner_id', '=', Auth::id()))
136+
);
137+
$query = $query->whereNotNull(APC::USER_ID);
138+
$query = $query->orWhereNotNull(APC::USER_GROUP_ID);
117139
$query = $query->orderBy('base_album_id', 'asc');
118140

119141
return AccessPermissionResource::collect($query->get());
@@ -134,10 +156,12 @@ public function listAlbums(ListAllSharingRequest $request, ListAlbums $list_albu
134156
$owner_id = $user->id;
135157
}
136158

137-
return TargetAlbumResource::collect($list_albums->do(
138-
albums_filtering: resolve(Collection::class),
139-
parent_id: null,
140-
owner_id: $owner_id)
159+
return TargetAlbumResource::collect(
160+
$list_albums->do(
161+
albums_filtering: resolve(Collection::class),
162+
parent_id: null,
163+
owner_id: $owner_id
164+
)
141165
);
142166
}
143167

@@ -173,4 +197,4 @@ public function propagate(PropagateSharingRequest $request, Propagate $propagate
173197
$propagate->update($album);
174198
}
175199
}
176-
}
200+
}

app/Http/Requests/Sharing/AddSharingRequest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,32 @@
1010

1111
use App\Contracts\Http\Requests\HasAccessPermissionResource;
1212
use App\Contracts\Http\Requests\HasAlbumIds;
13+
use App\Contracts\Http\Requests\HasUserGroupIds;
1314
use App\Contracts\Http\Requests\HasUserIds;
1415
use App\Contracts\Http\Requests\RequestAttribute;
1516
use App\Contracts\Models\AbstractAlbum;
1617
use App\Http\Requests\BaseApiRequest;
1718
use App\Http\Requests\Traits\HasAccessPermissionResourceTrait;
1819
use App\Http\Requests\Traits\HasAlbumIdsTrait;
20+
use App\Http\Requests\Traits\HasUserGroupIdsTrait;
1921
use App\Http\Requests\Traits\HasUserIdsTrait;
2022
use App\Http\Resources\Models\AccessPermissionResource;
2123
use App\Policies\AlbumPolicy;
2224
use App\Rules\IntegerIDRule;
2325
use App\Rules\RandomIDRule;
2426
use Illuminate\Support\Facades\Gate;
27+
use Illuminate\Validation\ValidationException;
2528

2629
/**
2730
* Represents a request for setting the shares of specific albums.
2831
*
2932
* Only the owner of the album (or the admin) can set the shares.
3033
*/
31-
class AddSharingRequest extends BaseApiRequest implements HasAlbumIds, HasUserIds, HasAccessPermissionResource
34+
class AddSharingRequest extends BaseApiRequest implements HasAlbumIds, HasUserIds, HasAccessPermissionResource, HasUserGroupIds
3235
{
3336
use HasAlbumIdsTrait;
3437
use HasUserIdsTrait;
38+
use HasUserGroupIdsTrait;
3539
use HasAccessPermissionResourceTrait;
3640

3741
/**
@@ -50,8 +54,10 @@ public function rules(): array
5054
return [
5155
RequestAttribute::ALBUM_IDS_ATTRIBUTE => 'required|array|min:1',
5256
RequestAttribute::ALBUM_IDS_ATTRIBUTE . '.*' => ['required', new RandomIDRule(false)],
53-
RequestAttribute::USER_IDS_ATTRIBUTE => 'required|array|min:1',
57+
RequestAttribute::USER_IDS_ATTRIBUTE => 'present|array',
5458
RequestAttribute::USER_IDS_ATTRIBUTE . '.*' => ['required', new IntegerIDRule(false)],
59+
RequestAttribute::USER_GROUP_IDS_ATTRIBUTE => 'present|array',
60+
RequestAttribute::USER_GROUP_IDS_ATTRIBUTE . '.*' => ['required', new IntegerIDRule(false)],
5561
RequestAttribute::GRANTS_DOWNLOAD_ATTRIBUTE => ['required', 'boolean'],
5662
RequestAttribute::GRANTS_FULL_PHOTO_ACCESS_ATTRIBUTE => ['required', 'boolean'],
5763
RequestAttribute::GRANTS_UPLOAD_ATTRIBUTE => ['required', 'boolean'],
@@ -62,11 +68,20 @@ public function rules(): array
6268

6369
/**
6470
* {@inheritDoc}
71+
*
72+
* @throws ValidationException if no users or groups are specified
6573
*/
6674
protected function processValidatedValues(array $values, array $files): void
6775
{
6876
$this->album_ids = $values[RequestAttribute::ALBUM_IDS_ATTRIBUTE];
6977
$this->user_ids = $values[RequestAttribute::USER_IDS_ATTRIBUTE];
78+
$this->user_group_ids = $values[RequestAttribute::USER_GROUP_IDS_ATTRIBUTE];
79+
80+
if ($this->user_ids === [] && $this->user_group_ids === []) {
81+
// If no users or groups are specified, we do not create a permission.
82+
throw new ValidationException('You must specify at least one user or group to share with.', 422);
83+
}
84+
7085
$this->perm_resource = new AccessPermissionResource(
7186
grants_edit: static::toBoolean($values[RequestAttribute::GRANTS_EDIT_ATTRIBUTE]),
7287
grants_delete: static::toBoolean($values[RequestAttribute::GRANTS_DELETE_ATTRIBUTE]),

app/Http/Requests/Sharing/EditSharingRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ protected function processValidatedValues(array $values, array $files): void
6464
{
6565
/** @var int $id */
6666
$id = $values[RequestAttribute::PERMISSION_ID];
67-
$this->perm = AccessPermission::with(['album', 'user'])->findOrFail($id);
67+
$this->perm = AccessPermission::with(['album', 'user', 'user_group'])->findOrFail($id);
6868

6969
$this->perm_resource = new AccessPermissionResource(
7070
grants_edit: static::toBoolean($values[RequestAttribute::GRANTS_EDIT_ATTRIBUTE]),
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/**
4+
* SPDX-License-Identifier: MIT
5+
* Copyright (c) 2017-2018 Tobias Reich
6+
* Copyright (c) 2018-2025 LycheeOrg.
7+
*/
8+
9+
namespace App\Http\Requests\Traits;
10+
11+
trait HasUserGroupIdsTrait
12+
{
13+
/**
14+
* @var array<int,int>
15+
*/
16+
protected array $user_group_ids = [];
17+
18+
/**
19+
* @return array<int,int>
20+
*/
21+
public function userGroupIds(): array
22+
{
23+
return $this->user_group_ids;
24+
}
25+
}

app/Http/Resources/Models/AccessPermissionResource.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ class AccessPermissionResource extends Data
1818
public function __construct(
1919
public ?int $id = null,
2020
public ?int $user_id = null,
21+
public ?int $user_group_id = null,
2122
public ?string $username = null,
23+
public ?string $user_group_name = null,
2224
public ?string $album_title = null,
2325
public ?string $album_id = null,
2426
public bool $grants_full_photo_access = false,
@@ -33,8 +35,10 @@ public static function fromModel(AccessPermission $access_permission): AccessPer
3335
{
3436
return new AccessPermissionResource(
3537
id: $access_permission->id,
36-
user_id: $access_permission->user_id,
37-
username: $access_permission->user->name,
38+
user_id: $access_permission->user?->id,
39+
username: $access_permission->user?->name,
40+
user_group_id: $access_permission->user_group?->id,
41+
user_group_name: $access_permission->user_group?->name,
3842
album_title: $access_permission->album->title,
3943
album_id: $access_permission->base_album_id,
4044
grants_full_photo_access: $access_permission->grants_full_photo_access,

0 commit comments

Comments
 (0)