Skip to content

Commit 754dd99

Browse files
chore: Refactor forms able to select collections
1 parent d5affbb commit 754dd99

File tree

7 files changed

+291
-528
lines changed

7 files changed

+291
-528
lines changed

src/controllers/Links.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,9 @@ public function new(Request $request): Response
162162
]);
163163
$user = $this->requireCurrentUser(redirect_after_login: $from);
164164

165+
$default_collection_ids = [];
165166
if ($default_collection_id) {
166-
$default_collection_ids = [$default_collection_id];
167-
} else {
168-
$bookmarks = $user->bookmarks();
169-
$default_collection_ids = [$bookmarks->id];
167+
$default_collection_ids[] = $default_collection_id;
170168
}
171169

172170
$link = new models\Link($default_url, $user->id);
@@ -226,15 +224,17 @@ public function create(Request $request): Response
226224
}
227225

228226
$link_collections = $form->selectedCollections();
229-
230227
foreach ($form->newCollections() as $collection) {
231228
$collection->save();
232-
233229
$link_collections[] = $collection;
234230
}
235231

236232
$link->addCollections($link_collections);
237233

234+
if ($form->read_later) {
235+
$user->markAsReadLater($link);
236+
}
237+
238238
return Response::redirect('link', [
239239
'id' => $link->id,
240240
]);
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<?php
2+
3+
namespace App\forms\links;
4+
5+
use App\auth;
6+
use App\models;
7+
use App\utils;
8+
use Minz\Form;
9+
use Minz\Validable;
10+
11+
/**
12+
* @author Marien Fressinaud <dev@marienfressinaud.fr>
13+
* @license http://www.gnu.org/licenses/agpl-3.0.en.html AGPL
14+
*/
15+
trait CollectionsSelector
16+
{
17+
/** @var string[] */
18+
#[Form\Field(bind: false)]
19+
public array $collection_ids = [];
20+
21+
/** @var string[] */
22+
#[Form\Field(bind: false, transform: '\App\utils\ArrayHelper::trim')]
23+
public array $new_collection_names = [];
24+
25+
public int $collection_name_max_length = models\Collection::NAME_MAX_LENGTH;
26+
27+
/** @var ?array<string, models\Collection[]> */
28+
private ?array $cache_collection_values = null;
29+
30+
public function user(): models\User
31+
{
32+
$user = auth\CurrentUser::get();
33+
34+
if (!$user) {
35+
throw new \LogicException('User must be connected.');
36+
}
37+
38+
return $user;
39+
}
40+
41+
/**
42+
* Return the collections to display in the select field of the form.
43+
*
44+
* Collections are grouped by their groups if any. Non-grouped collections
45+
* can be found with the empty string key.
46+
*
47+
* @return array<string, models\Collection[]>
48+
*/
49+
public function collectionsValues(): array
50+
{
51+
if ($this->cache_collection_values !== null) {
52+
return $this->cache_collection_values;
53+
}
54+
55+
$user = $this->user();
56+
57+
$collection_values = [];
58+
59+
$groups = models\Group::listBy(['user_id' => $user->id]);
60+
$groups = utils\Sorter::localeSort($groups, 'name');
61+
62+
$collections = $user->collections();
63+
$collections = utils\Sorter::localeSort($collections, 'name');
64+
$groups_to_collections = utils\Grouper::groupBy($collections, 'group_id');
65+
66+
$shared_collections = $user->sharedCollections(options: [
67+
'access_type' => 'write',
68+
]);
69+
$shared_collections = utils\Sorter::localeSort($shared_collections, 'name');
70+
71+
// Add the collections with no groups first.
72+
$collection_values[''] = $groups_to_collections[''] ?? [];
73+
74+
// Add the collections with groups then
75+
foreach ($groups as $group) {
76+
if (isset($groups_to_collections[$group->id])) {
77+
$collection_values[$group->name] = $groups_to_collections[$group->id];
78+
}
79+
}
80+
81+
// Finally, add the shared collections like it was a distinct group.
82+
if ($shared_collections) {
83+
$share_group_name = _('Shared with me');
84+
$collection_values[$share_group_name] = $shared_collections;
85+
}
86+
87+
$this->cache_collection_values = $collection_values;
88+
89+
return $collection_values;
90+
}
91+
92+
/**
93+
* Find a collection by its id from the collections values.
94+
*/
95+
public function findCollection(string $collection_id): ?models\Collection
96+
{
97+
$collection_values = $this->collectionsValues();
98+
99+
foreach ($collection_values as $collections) {
100+
foreach ($collections as $collection) {
101+
if ($collection->id === $collection_id) {
102+
return $collection;
103+
}
104+
}
105+
}
106+
107+
return null;
108+
}
109+
110+
/**
111+
* Return whether the given collection is selected or not.
112+
*/
113+
public function isCollectionSelected(models\Collection $collection): bool
114+
{
115+
return in_array($collection->id, $this->collection_ids);
116+
}
117+
118+
/**
119+
* Return the list of the selected collections from the collection_ids
120+
* attribute.
121+
*
122+
* @throw new \RuntimeException
123+
* Raised if a selected collection_id doesn't match an existing collection.
124+
*
125+
* @return models\Collection[]
126+
*/
127+
public function selectedCollections(): array
128+
{
129+
$selected_collections = [];
130+
131+
foreach ($this->collection_ids as $collection_id) {
132+
$collection = $this->findCollection($collection_id);
133+
134+
if (!$collection) {
135+
throw new \RuntimeException("Collection {$collection_id} does not exist.'");
136+
}
137+
138+
$selected_collections[] = $collection;
139+
}
140+
141+
return $selected_collections;
142+
}
143+
144+
/**
145+
* Return the list of new collections (to create) from the
146+
* new_collection_names attribute.
147+
*
148+
* @return models\Collection[]
149+
*/
150+
public function newCollections(): array
151+
{
152+
$user = auth\CurrentUser::get();
153+
154+
if (!$user) {
155+
throw new \LogicException('User must be connected.');
156+
}
157+
158+
$collections = [];
159+
foreach ($this->new_collection_names as $name) {
160+
$collections[] = models\Collection::init(
161+
$user->id,
162+
$name,
163+
description: '',
164+
is_public: false,
165+
);
166+
}
167+
168+
return $collections;
169+
}
170+
171+
/**
172+
* @return models\Collection[]
173+
*/
174+
public function collectionsByOthers(): array
175+
{
176+
$user = $this->user();
177+
178+
$collections_by_others = models\Collection::listWritableContainingNotOwnedLinkWithUrl(
179+
$user->id,
180+
$this->model()->url_hash,
181+
);
182+
183+
return utils\Sorter::localeSort($collections_by_others, 'name');
184+
}
185+
186+
/**
187+
* Check that the selected collections are part of the collections values.
188+
*/
189+
#[Validable\Check]
190+
public function checkSelectedCollections(): void
191+
{
192+
try {
193+
$this->selectedCollections();
194+
} catch (\RuntimeException $e) {
195+
$this->addError(
196+
'collection_ids',
197+
'invalidCollection',
198+
_('One of the associated collection doesn’t exist.'),
199+
);
200+
}
201+
}
202+
203+
/**
204+
* Check that the new collections are valid (basically, that the names are
205+
* filled and not too long).
206+
*/
207+
#[Validable\Check]
208+
public function checkNewCollections(): void
209+
{
210+
foreach ($this->newCollections() as $collection) {
211+
if (!$collection->validate()) {
212+
$errors = $collection->errors();
213+
$error = implode(' ', $errors);
214+
$this->addError('new_collection_names', 'invalidNewCollection', $error);
215+
216+
// Stop after getting one error as the others are probably
217+
// similar.
218+
break;
219+
}
220+
}
221+
}
222+
}

0 commit comments

Comments
 (0)