diff options
Diffstat (limited to 'application/front/controller/admin/ManageShaareController.php')
-rw-r--r-- | application/front/controller/admin/ManageShaareController.php | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/application/front/controller/admin/ManageShaareController.php b/application/front/controller/admin/ManageShaareController.php new file mode 100644 index 00000000..620bbc40 --- /dev/null +++ b/application/front/controller/admin/ManageShaareController.php | |||
@@ -0,0 +1,281 @@ | |||
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\Thumbnailer; | ||
11 | use Slim\Http\Request; | ||
12 | use Slim\Http\Response; | ||
13 | |||
14 | /** | ||
15 | * Class PostBookmarkController | ||
16 | * | ||
17 | * Slim controller used to handle Shaarli create or edit bookmarks. | ||
18 | */ | ||
19 | class ManageShaareController extends ShaarliAdminController | ||
20 | { | ||
21 | /** | ||
22 | * GET /admin/add-shaare - Displays the form used to create a new bookmark from an URL | ||
23 | */ | ||
24 | public function addShaare(Request $request, Response $response): Response | ||
25 | { | ||
26 | $this->assignView( | ||
27 | 'pagetitle', | ||
28 | t('Shaare a new link') .' - '. $this->container->conf->get('general.title', 'Shaarli') | ||
29 | ); | ||
30 | |||
31 | return $response->write($this->render('addlink')); | ||
32 | } | ||
33 | |||
34 | /** | ||
35 | * GET /admin/shaare - Displays the bookmark form for creation. | ||
36 | * Note that if the URL is found in existing bookmarks, then it will be in edit mode. | ||
37 | */ | ||
38 | public function displayCreateForm(Request $request, Response $response): Response | ||
39 | { | ||
40 | $url = cleanup_url($request->getParam('post')); | ||
41 | |||
42 | $linkIsNew = false; | ||
43 | // Check if URL is not already in database (in this case, we will edit the existing link) | ||
44 | $bookmark = $this->container->bookmarkService->findByUrl($url); | ||
45 | if (null === $bookmark) { | ||
46 | $linkIsNew = true; | ||
47 | // Get shaare data if it was provided in URL (e.g.: by the bookmarklet). | ||
48 | $title = $request->getParam('title'); | ||
49 | $description = $request->getParam('description'); | ||
50 | $tags = $request->getParam('tags'); | ||
51 | $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); | ||
52 | |||
53 | // If this is an HTTP(S) link, we try go get the page to extract | ||
54 | // the title (otherwise we will to straight to the edit form.) | ||
55 | if (empty($title) && strpos(get_url_scheme($url) ?: '', 'http') !== false) { | ||
56 | $retrieveDescription = $this->container->conf->get('general.retrieve_description'); | ||
57 | // Short timeout to keep the application responsive | ||
58 | // The callback will fill $charset and $title with data from the downloaded page. | ||
59 | $this->container->httpAccess->getHttpResponse( | ||
60 | $url, | ||
61 | $this->container->conf->get('general.download_timeout', 30), | ||
62 | $this->container->conf->get('general.download_max_size', 4194304), | ||
63 | $this->container->httpAccess->getCurlDownloadCallback( | ||
64 | $charset, | ||
65 | $title, | ||
66 | $description, | ||
67 | $tags, | ||
68 | $retrieveDescription | ||
69 | ) | ||
70 | ); | ||
71 | if (! empty($title) && strtolower($charset) !== 'utf-8') { | ||
72 | $title = mb_convert_encoding($title, 'utf-8', $charset); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | if (empty($url) && empty($title)) { | ||
77 | $title = $this->container->conf->get('general.default_note_title', t('Note: ')); | ||
78 | } | ||
79 | |||
80 | $link = escape([ | ||
81 | 'title' => $title, | ||
82 | 'url' => $url ?? '', | ||
83 | 'description' => $description ?? '', | ||
84 | 'tags' => $tags ?? '', | ||
85 | 'private' => $private, | ||
86 | ]); | ||
87 | } else { | ||
88 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
89 | $link = $formatter->format($bookmark); | ||
90 | } | ||
91 | |||
92 | return $this->displayForm($link, $linkIsNew, $request, $response); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * GET /admin/shaare/{id} - Displays the bookmark form in edition mode. | ||
97 | */ | ||
98 | public function displayEditForm(Request $request, Response $response, array $args): Response | ||
99 | { | ||
100 | $id = $args['id'] ?? ''; | ||
101 | try { | ||
102 | if (false === ctype_digit($id)) { | ||
103 | throw new BookmarkNotFoundException(); | ||
104 | } | ||
105 | $bookmark = $this->container->bookmarkService->get((int) $id); // Read database | ||
106 | } catch (BookmarkNotFoundException $e) { | ||
107 | $this->saveErrorMessage(sprintf( | ||
108 | t('Bookmark with identifier %s could not be found.'), | ||
109 | $id | ||
110 | )); | ||
111 | |||
112 | return $this->redirect($response, '/'); | ||
113 | } | ||
114 | |||
115 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
116 | $link = $formatter->format($bookmark); | ||
117 | |||
118 | return $this->displayForm($link, false, $request, $response); | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * POST /admin/shaare | ||
123 | */ | ||
124 | public function save(Request $request, Response $response): Response | ||
125 | { | ||
126 | $this->checkToken($request); | ||
127 | |||
128 | // lf_id should only be present if the link exists. | ||
129 | $id = $request->getParam('lf_id') ? intval(escape($request->getParam('lf_id'))) : null; | ||
130 | if (null !== $id && true === $this->container->bookmarkService->exists($id)) { | ||
131 | // Edit | ||
132 | $bookmark = $this->container->bookmarkService->get($id); | ||
133 | } else { | ||
134 | // New link | ||
135 | $bookmark = new Bookmark(); | ||
136 | } | ||
137 | |||
138 | $bookmark->setTitle($request->getParam('lf_title')); | ||
139 | $bookmark->setDescription($request->getParam('lf_description')); | ||
140 | $bookmark->setUrl($request->getParam('lf_url'), $this->container->conf->get('security.allowed_protocols', [])); | ||
141 | $bookmark->setPrivate(filter_var($request->getParam('lf_private'), FILTER_VALIDATE_BOOLEAN)); | ||
142 | $bookmark->setTagsString($request->getParam('lf_tags')); | ||
143 | |||
144 | if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE | ||
145 | && false === $bookmark->isNote() | ||
146 | ) { | ||
147 | $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); | ||
148 | } | ||
149 | $this->container->bookmarkService->addOrSet($bookmark, false); | ||
150 | |||
151 | // To preserve backward compatibility with 3rd parties, plugins still use arrays | ||
152 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
153 | $data = $formatter->format($bookmark); | ||
154 | $data = $this->executeHooks('save_link', $data); | ||
155 | |||
156 | $bookmark->fromArray($data); | ||
157 | $this->container->bookmarkService->set($bookmark); | ||
158 | |||
159 | // If we are called from the bookmarklet, we must close the popup: | ||
160 | if ($request->getParam('source') === 'bookmarklet') { | ||
161 | return $response->write('<script>self.close();</script>'); | ||
162 | } | ||
163 | |||
164 | if (!empty($request->getParam('returnurl'))) { | ||
165 | $this->container->environment['HTTP_REFERER'] = escape($request->getParam('returnurl')); | ||
166 | } | ||
167 | |||
168 | return $this->redirectFromReferer( | ||
169 | $request, | ||
170 | $response, | ||
171 | ['add-shaare', 'shaare'], ['addlink', 'post', 'edit_link'], | ||
172 | $bookmark->getShortUrl() | ||
173 | ); | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * GET /admin/shaare/delete | ||
178 | */ | ||
179 | public function deleteBookmark(Request $request, Response $response): Response | ||
180 | { | ||
181 | $this->checkToken($request); | ||
182 | |||
183 | $ids = escape(trim($request->getParam('id') ?? '')); | ||
184 | if (empty($ids) || strpos($ids, ' ') !== false) { | ||
185 | // multiple, space-separated ids provided | ||
186 | $ids = array_values(array_filter(preg_split('/\s+/', $ids), 'ctype_digit')); | ||
187 | } else { | ||
188 | $ids = [$ids]; | ||
189 | } | ||
190 | |||
191 | // assert at least one id is given | ||
192 | if (0 === count($ids)) { | ||
193 | $this->saveErrorMessage(t('Invalid bookmark ID provided.')); | ||
194 | |||
195 | return $this->redirectFromReferer($request, $response, [], ['delete-shaare']); | ||
196 | } | ||
197 | |||
198 | $formatter = $this->container->formatterFactory->getFormatter('raw'); | ||
199 | $count = 0; | ||
200 | foreach ($ids as $id) { | ||
201 | try { | ||
202 | $bookmark = $this->container->bookmarkService->get((int) $id); | ||
203 | } catch (BookmarkNotFoundException $e) { | ||
204 | $this->saveErrorMessage(sprintf( | ||
205 | t('Bookmark with identifier %s could not be found.'), | ||
206 | $id | ||
207 | )); | ||
208 | |||
209 | continue; | ||
210 | } | ||
211 | |||
212 | $data = $formatter->format($bookmark); | ||
213 | $this->container->pluginManager->executeHooks('delete_link', $data); | ||
214 | $this->container->bookmarkService->remove($bookmark, false); | ||
215 | ++ $count; | ||
216 | } | ||
217 | |||
218 | if ($count > 0) { | ||
219 | $this->container->bookmarkService->save(); | ||
220 | } | ||
221 | |||
222 | // If we are called from the bookmarklet, we must close the popup: | ||
223 | if ($request->getParam('source') === 'bookmarklet') { | ||
224 | return $response->write('<script>self.close();</script>'); | ||
225 | } | ||
226 | |||
227 | // Don't redirect to where we were previously because the datastore has changed. | ||
228 | return $this->redirect($response, '/'); | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * Helper function used to display the shaare form whether it's a new or existing bookmark. | ||
233 | * | ||
234 | * @param array $link data used in template, either from parameters or from the data store | ||
235 | */ | ||
236 | protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response | ||
237 | { | ||
238 | $tags = $this->container->bookmarkService->bookmarksCountPerTag(); | ||
239 | if ($this->container->conf->get('formatter') === 'markdown') { | ||
240 | $tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; | ||
241 | } | ||
242 | |||
243 | $data = [ | ||
244 | 'link' => $link, | ||
245 | 'link_is_new' => $isNew, | ||
246 | 'http_referer' => escape($this->container->environment['HTTP_REFERER'] ?? ''), | ||
247 | 'source' => $request->getParam('source') ?? '', | ||
248 | 'tags' => $tags, | ||
249 | 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), | ||
250 | ]; | ||
251 | |||
252 | $data = $this->executeHooks('render_editlink', $data); | ||
253 | |||
254 | foreach ($data as $key => $value) { | ||
255 | $this->assignView($key, $value); | ||
256 | } | ||
257 | |||
258 | $editLabel = false === $isNew ? t('Edit') .' ' : ''; | ||
259 | $this->assignView( | ||
260 | 'pagetitle', | ||
261 | $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') | ||
262 | ); | ||
263 | |||
264 | return $response->write($this->render('editlink')); | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * @param mixed[] $data Variables passed to the template engine | ||
269 | * | ||
270 | * @return mixed[] Template data after active plugins render_picwall hook execution. | ||
271 | */ | ||
272 | protected function executeHooks(string $hook, array $data): array | ||
273 | { | ||
274 | $this->container->pluginManager->executeHooks( | ||
275 | $hook, | ||
276 | $data | ||
277 | ); | ||
278 | |||
279 | return $data; | ||
280 | } | ||
281 | } | ||