diff options
author | ArthurHoaro <arthur@hoa.ro> | 2020-10-10 17:40:26 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-10-27 20:11:30 +0100 |
commit | 5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1 (patch) | |
tree | 2236e571035332a63f87a09222f2278b93f63515 /application/front/controller/admin/ShaarePublishController.php | |
parent | b8e5a253ab5521ce2be6c0d3e04e0101527df3c1 (diff) | |
download | Shaarli-5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1.tar.gz Shaarli-5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1.tar.zst Shaarli-5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1.zip |
Feature: bulk creation of bookmarks
This changes creates a new form in addlink page allowing to create
multiple bookmarks at once more easily. It focuses on re-using as much
existing code and template component as possible.
These changes includes:
- a new form in addlink (hidden behind a button by default),
containing a text area for URL, and tags/private status to apply to
created links
- this form displays a new template called editlink.batch, itself
including editlink template multiple times
- User interation in this new templates are handle by a new JS script
(shaare-batch.js) making AJAX requests, and therefore does not need page
reloading
- ManageShaareController has been split into 3 distinct controllers:
+ ShaareAdd: displays addlink template
+ ShaareManage: various operation applied on existing shaares
(change visibility, pin, deletion, etc.)
+ ShaarePublish: handles creation/edit forms and saving Shaare's
form
- Updated translations
Fixes #137
Diffstat (limited to 'application/front/controller/admin/ShaarePublishController.php')
-rw-r--r-- | application/front/controller/admin/ShaarePublishController.php | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/application/front/controller/admin/ShaarePublishController.php b/application/front/controller/admin/ShaarePublishController.php new file mode 100644 index 00000000..608f79cf --- /dev/null +++ b/application/front/controller/admin/ShaarePublishController.php | |||
@@ -0,0 +1,222 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Shaarli\Front\Controller\Admin; | ||
6 | |||
7 | use Shaarli\Bookmark\Bookmark; | ||
8 | use Shaarli\Bookmark\Exception\BookmarkNotFoundException; | ||
9 | use Shaarli\Formatter\BookmarkMarkdownFormatter; | ||
10 | use Shaarli\Render\TemplatePage; | ||
11 | use Shaarli\Thumbnailer; | ||
12 | use Slim\Http\Request; | ||
13 | use Slim\Http\Response; | ||
14 | |||
15 | class ShaarePublishController extends ShaarliAdminController | ||
16 | { | ||
17 | /** | ||
18 | * GET /admin/shaare - Displays the bookmark form for creation. | ||
19 | * Note that if the URL is found in existing bookmarks, then it will be in edit mode. | ||
20 | */ | ||
21 | public function displayCreateForm(Request $request, Response $response): Response | ||
22 | { | ||
23 | $url = cleanup_url($request->getParam('post')); | ||
24 | $link = $this->buildLinkDataFromUrl($request, $url); | ||
25 | |||
26 | return $this->displayForm($link, $link['linkIsNew'], $request, $response); | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * POST /admin/shaare-batch - Displays multiple creation/edit forms from bulk add in add-link page. | ||
31 | */ | ||
32 | public function displayCreateBatchForms(Request $request, Response $response): Response | ||
33 | { | ||
34 | $urls = array_map('cleanup_url', explode(PHP_EOL, $request->getParam('urls'))); | ||
35 | |||
36 | $links = []; | ||
37 | foreach ($urls as $url) { | ||
38 | $link = $this->buildLinkDataFromUrl($request, $url); | ||
39 | $data = $this->buildFormData($link, $link['linkIsNew'], $request); | ||
40 | $data['token'] = $this->container->sessionManager->generateToken(); | ||
41 | $data['source'] = 'batch'; | ||
42 | |||
43 | $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); | ||
44 | |||
45 | $links[] = $data; | ||
46 | } | ||
47 | |||
48 | $this->assignView('links', $links); | ||
49 | $this->assignView('batch_mode', true); | ||
50 | $this->assignView('async_metadata', $this->container->conf->get('general.enable_async_metadata', true)); | ||
51 | |||
52 | return $response->write($this->render(TemplatePage::EDIT_LINK_BATCH)); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * GET /admin/shaare/{id} - Displays the bookmark form in edition mode. | ||
57 | */ | ||
58 | public function displayEditForm(Request $request, Response $response, array $args): Response | ||
59 | { | ||
60 | $id = $args['id'] ?? ''; | ||
61 | try { | ||
62 | if (false === ctype_digit($id)) { | ||
63 | throw new BookmarkNotFoundException(); | ||
64 | } | ||
65 | $bookmark = $this->container->bookmarkService->get((int) $id); // Read database | ||
66 | } catch (BookmarkNotFoundException $e) { | ||
67 | $this->saveErrorMessage(sprintf( | ||
68 | t('Bookmark with identifier %s could not be found.'), | ||
69 | $id | ||
70 | )); | ||
71 | |||
72 | return $this->redirect($response, '/'); | ||
73 | } | ||
74 | |||
75 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
76 | $link = $formatter->format($bookmark); | ||
77 | |||
78 | return $this->displayForm($link, false, $request, $response); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * POST /admin/shaare | ||
83 | */ | ||
84 | public function save(Request $request, Response $response): Response | ||
85 | { | ||
86 | $this->checkToken($request); | ||
87 | |||
88 | // lf_id should only be present if the link exists. | ||
89 | $id = $request->getParam('lf_id') !== null ? intval(escape($request->getParam('lf_id'))) : null; | ||
90 | if (null !== $id && true === $this->container->bookmarkService->exists($id)) { | ||
91 | // Edit | ||
92 | $bookmark = $this->container->bookmarkService->get($id); | ||
93 | } else { | ||
94 | // New link | ||
95 | $bookmark = new Bookmark(); | ||
96 | } | ||
97 | |||
98 | $bookmark->setTitle($request->getParam('lf_title')); | ||
99 | $bookmark->setDescription($request->getParam('lf_description')); | ||
100 | $bookmark->setUrl($request->getParam('lf_url'), $this->container->conf->get('security.allowed_protocols', [])); | ||
101 | $bookmark->setPrivate(filter_var($request->getParam('lf_private'), FILTER_VALIDATE_BOOLEAN)); | ||
102 | $bookmark->setTagsString($request->getParam('lf_tags')); | ||
103 | |||
104 | if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE | ||
105 | && true !== $this->container->conf->get('general.enable_async_metadata', true) | ||
106 | && $bookmark->shouldUpdateThumbnail() | ||
107 | ) { | ||
108 | $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); | ||
109 | } | ||
110 | $this->container->bookmarkService->addOrSet($bookmark, false); | ||
111 | |||
112 | // To preserve backward compatibility with 3rd parties, plugins still use arrays | ||
113 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
114 | $data = $formatter->format($bookmark); | ||
115 | $this->executePageHooks('save_link', $data); | ||
116 | |||
117 | $bookmark->fromArray($data); | ||
118 | $this->container->bookmarkService->set($bookmark); | ||
119 | |||
120 | // If we are called from the bookmarklet, we must close the popup: | ||
121 | if ($request->getParam('source') === 'bookmarklet') { | ||
122 | return $response->write('<script>self.close();</script>'); | ||
123 | } elseif ($request->getParam('source') === 'batch') { | ||
124 | return $response; | ||
125 | } | ||
126 | |||
127 | if (!empty($request->getParam('returnurl'))) { | ||
128 | $this->container->environment['HTTP_REFERER'] = escape($request->getParam('returnurl')); | ||
129 | } | ||
130 | |||
131 | return $this->redirectFromReferer( | ||
132 | $request, | ||
133 | $response, | ||
134 | ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'], | ||
135 | $bookmark->getShortUrl() | ||
136 | ); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Helper function used to display the shaare form whether it's a new or existing bookmark. | ||
141 | * | ||
142 | * @param array $link data used in template, either from parameters or from the data store | ||
143 | */ | ||
144 | protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response | ||
145 | { | ||
146 | $data = $this->buildFormData($link, $isNew, $request); | ||
147 | |||
148 | $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); | ||
149 | |||
150 | foreach ($data as $key => $value) { | ||
151 | $this->assignView($key, $value); | ||
152 | } | ||
153 | |||
154 | $editLabel = false === $isNew ? t('Edit') .' ' : ''; | ||
155 | $this->assignView( | ||
156 | 'pagetitle', | ||
157 | $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') | ||
158 | ); | ||
159 | |||
160 | return $response->write($this->render(TemplatePage::EDIT_LINK)); | ||
161 | } | ||
162 | |||
163 | protected function buildLinkDataFromUrl(Request $request, string $url): array | ||
164 | { | ||
165 | // Check if URL is not already in database (in this case, we will edit the existing link) | ||
166 | $bookmark = $this->container->bookmarkService->findByUrl($url); | ||
167 | if (null === $bookmark) { | ||
168 | // Get shaare data if it was provided in URL (e.g.: by the bookmarklet). | ||
169 | $title = $request->getParam('title'); | ||
170 | $description = $request->getParam('description'); | ||
171 | $tags = $request->getParam('tags'); | ||
172 | $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); | ||
173 | |||
174 | // If this is an HTTP(S) link, we try go get the page to extract | ||
175 | // the title (otherwise we will to straight to the edit form.) | ||
176 | if (true !== $this->container->conf->get('general.enable_async_metadata', true) | ||
177 | && empty($title) | ||
178 | && strpos(get_url_scheme($url) ?: '', 'http') !== false | ||
179 | ) { | ||
180 | $metadata = $this->container->metadataRetriever->retrieve($url); | ||
181 | } | ||
182 | |||
183 | if (empty($url)) { | ||
184 | $metadata['title'] = $this->container->conf->get('general.default_note_title', t('Note: ')); | ||
185 | } | ||
186 | |||
187 | return [ | ||
188 | 'title' => $title ?? $metadata['title'] ?? '', | ||
189 | 'url' => $url ?? '', | ||
190 | 'description' => $description ?? $metadata['description'] ?? '', | ||
191 | 'tags' => $tags ?? $metadata['tags'] ?? '', | ||
192 | 'private' => $private, | ||
193 | 'linkIsNew' => true, | ||
194 | ]; | ||
195 | } | ||
196 | |||
197 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
198 | $link = $formatter->format($bookmark); | ||
199 | $link['linkIsNew'] = false; | ||
200 | |||
201 | return $link; | ||
202 | } | ||
203 | |||
204 | protected function buildFormData(array $link, bool $isNew, Request $request): array | ||
205 | { | ||
206 | $tags = $this->container->bookmarkService->bookmarksCountPerTag(); | ||
207 | if ($this->container->conf->get('formatter') === 'markdown') { | ||
208 | $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; | ||
209 | } | ||
210 | |||
211 | return escape([ | ||
212 | 'link' => $link, | ||
213 | 'link_is_new' => $isNew, | ||
214 | 'http_referer' => $this->container->environment['HTTP_REFERER'] ?? '', | ||
215 | 'source' => $request->getParam('source') ?? '', | ||
216 | 'tags' => $tags, | ||
217 | 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), | ||
218 | 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true), | ||
219 | 'retrieve_description' => $this->container->conf->get('general.retrieve_description', false), | ||
220 | ]); | ||
221 | } | ||
222 | } | ||