]>
Commit | Line | Data |
---|---|---|
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\BookmarkFormatter; | |
10 | use Shaarli\Formatter\BookmarkMarkdownFormatter; | |
11 | use Shaarli\Render\TemplatePage; | |
12 | use Shaarli\Thumbnailer; | |
13 | use Slim\Http\Request; | |
14 | use Slim\Http\Response; | |
15 | ||
16 | class ShaarePublishController extends ShaarliAdminController | |
17 | { | |
18 | /** | |
19 | * @var BookmarkFormatter[] Statically cached instances of formatters | |
20 | */ | |
21 | protected $formatters = []; | |
22 | ||
23 | /** | |
24 | * @var array Statically cached bookmark's tags counts | |
25 | */ | |
26 | protected $tags; | |
27 | ||
28 | /** | |
29 | * GET /admin/shaare - Displays the bookmark form for creation. | |
30 | * Note that if the URL is found in existing bookmarks, then it will be in edit mode. | |
31 | */ | |
32 | public function displayCreateForm(Request $request, Response $response): Response | |
33 | { | |
34 | $url = cleanup_url($request->getParam('post')); | |
35 | $link = $this->buildLinkDataFromUrl($request, $url); | |
36 | ||
37 | return $this->displayForm($link, $link['linkIsNew'], $request, $response); | |
38 | } | |
39 | ||
40 | /** | |
41 | * POST /admin/shaare-batch - Displays multiple creation/edit forms from bulk add in add-link page. | |
42 | */ | |
43 | public function displayCreateBatchForms(Request $request, Response $response): Response | |
44 | { | |
45 | $urls = array_map('cleanup_url', explode(PHP_EOL, $request->getParam('urls'))); | |
46 | ||
47 | $links = []; | |
48 | foreach ($urls as $url) { | |
49 | if (empty($url)) { | |
50 | continue; | |
51 | } | |
52 | $link = $this->buildLinkDataFromUrl($request, $url); | |
53 | $data = $this->buildFormData($link, $link['linkIsNew'], $request); | |
54 | $data['token'] = $this->container->sessionManager->generateToken(); | |
55 | $data['source'] = 'batch'; | |
56 | ||
57 | $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); | |
58 | ||
59 | $links[] = $data; | |
60 | } | |
61 | ||
62 | $this->assignView('links', $links); | |
63 | $this->assignView('batch_mode', true); | |
64 | $this->assignView('async_metadata', $this->container->conf->get('general.enable_async_metadata', true)); | |
65 | ||
66 | return $response->write($this->render(TemplatePage::EDIT_LINK_BATCH)); | |
67 | } | |
68 | ||
69 | /** | |
70 | * GET /admin/shaare/{id} - Displays the bookmark form in edition mode. | |
71 | */ | |
72 | public function displayEditForm(Request $request, Response $response, array $args): Response | |
73 | { | |
74 | $id = $args['id'] ?? ''; | |
75 | try { | |
76 | if (false === ctype_digit($id)) { | |
77 | throw new BookmarkNotFoundException(); | |
78 | } | |
79 | $bookmark = $this->container->bookmarkService->get((int) $id); // Read database | |
80 | } catch (BookmarkNotFoundException $e) { | |
81 | $this->saveErrorMessage(sprintf( | |
82 | t('Bookmark with identifier %s could not be found.'), | |
83 | $id | |
84 | )); | |
85 | ||
86 | return $this->redirect($response, '/'); | |
87 | } | |
88 | ||
89 | $formatter = $this->getFormatter('raw'); | |
90 | $link = $formatter->format($bookmark); | |
91 | ||
92 | return $this->displayForm($link, false, $request, $response); | |
93 | } | |
94 | ||
95 | /** | |
96 | * POST /admin/shaare | |
97 | */ | |
98 | public function save(Request $request, Response $response): Response | |
99 | { | |
100 | $this->checkToken($request); | |
101 | ||
102 | // lf_id should only be present if the link exists. | |
103 | $id = $request->getParam('lf_id') !== null ? intval(escape($request->getParam('lf_id'))) : null; | |
104 | if (null !== $id && true === $this->container->bookmarkService->exists($id)) { | |
105 | // Edit | |
106 | $bookmark = $this->container->bookmarkService->get($id); | |
107 | } else { | |
108 | // New link | |
109 | $bookmark = new Bookmark(); | |
110 | } | |
111 | ||
112 | $bookmark->setTitle($request->getParam('lf_title')); | |
113 | $bookmark->setDescription($request->getParam('lf_description')); | |
114 | $bookmark->setUrl($request->getParam('lf_url'), $this->container->conf->get('security.allowed_protocols', [])); | |
115 | $bookmark->setPrivate(filter_var($request->getParam('lf_private'), FILTER_VALIDATE_BOOLEAN)); | |
116 | $bookmark->setTagsString($request->getParam('lf_tags')); | |
117 | ||
118 | if ($this->container->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE | |
119 | && true !== $this->container->conf->get('general.enable_async_metadata', true) | |
120 | && $bookmark->shouldUpdateThumbnail() | |
121 | ) { | |
122 | $bookmark->setThumbnail($this->container->thumbnailer->get($bookmark->getUrl())); | |
123 | } | |
124 | $this->container->bookmarkService->addOrSet($bookmark, false); | |
125 | ||
126 | // To preserve backward compatibility with 3rd parties, plugins still use arrays | |
127 | $formatter = $this->getFormatter('raw'); | |
128 | $data = $formatter->format($bookmark); | |
129 | $this->executePageHooks('save_link', $data); | |
130 | ||
131 | $bookmark->fromArray($data); | |
132 | $this->container->bookmarkService->set($bookmark); | |
133 | ||
134 | // If we are called from the bookmarklet, we must close the popup: | |
135 | if ($request->getParam('source') === 'bookmarklet') { | |
136 | return $response->write('<script>self.close();</script>'); | |
137 | } elseif ($request->getParam('source') === 'batch') { | |
138 | return $response; | |
139 | } | |
140 | ||
141 | if (!empty($request->getParam('returnurl'))) { | |
142 | $this->container->environment['HTTP_REFERER'] = $request->getParam('returnurl'); | |
143 | } | |
144 | ||
145 | return $this->redirectFromReferer( | |
146 | $request, | |
147 | $response, | |
148 | ['/admin/add-shaare', '/admin/shaare'], ['addlink', 'post', 'edit_link'], | |
149 | $bookmark->getShortUrl() | |
150 | ); | |
151 | } | |
152 | ||
153 | /** | |
154 | * Helper function used to display the shaare form whether it's a new or existing bookmark. | |
155 | * | |
156 | * @param array $link data used in template, either from parameters or from the data store | |
157 | */ | |
158 | protected function displayForm(array $link, bool $isNew, Request $request, Response $response): Response | |
159 | { | |
160 | $data = $this->buildFormData($link, $isNew, $request); | |
161 | ||
162 | $this->executePageHooks('render_editlink', $data, TemplatePage::EDIT_LINK); | |
163 | ||
164 | foreach ($data as $key => $value) { | |
165 | $this->assignView($key, $value); | |
166 | } | |
167 | ||
168 | $editLabel = false === $isNew ? t('Edit') .' ' : ''; | |
169 | $this->assignView( | |
170 | 'pagetitle', | |
171 | $editLabel . t('Shaare') .' - '. $this->container->conf->get('general.title', 'Shaarli') | |
172 | ); | |
173 | ||
174 | return $response->write($this->render(TemplatePage::EDIT_LINK)); | |
175 | } | |
176 | ||
177 | protected function buildLinkDataFromUrl(Request $request, string $url): array | |
178 | { | |
179 | // Check if URL is not already in database (in this case, we will edit the existing link) | |
180 | $bookmark = $this->container->bookmarkService->findByUrl($url); | |
181 | if (null === $bookmark) { | |
182 | // Get shaare data if it was provided in URL (e.g.: by the bookmarklet). | |
183 | $title = $request->getParam('title'); | |
184 | $description = $request->getParam('description'); | |
185 | $tags = $request->getParam('tags'); | |
186 | if ($request->getParam('private') !== null) { | |
187 | $private = filter_var($request->getParam('private'), FILTER_VALIDATE_BOOLEAN); | |
188 | } else { | |
189 | $private = $this->container->conf->get('privacy.default_private_links', false); | |
190 | } | |
191 | ||
192 | // If this is an HTTP(S) link, we try go get the page to extract | |
193 | // the title (otherwise we will to straight to the edit form.) | |
194 | if (true !== $this->container->conf->get('general.enable_async_metadata', true) | |
195 | && empty($title) | |
196 | && strpos(get_url_scheme($url) ?: '', 'http') !== false | |
197 | ) { | |
198 | $metadata = $this->container->metadataRetriever->retrieve($url); | |
199 | } | |
200 | ||
201 | if (empty($url)) { | |
202 | $metadata['title'] = $this->container->conf->get('general.default_note_title', t('Note: ')); | |
203 | } | |
204 | ||
205 | return [ | |
206 | 'title' => $title ?? $metadata['title'] ?? '', | |
207 | 'url' => $url ?? '', | |
208 | 'description' => $description ?? $metadata['description'] ?? '', | |
209 | 'tags' => $tags ?? $metadata['tags'] ?? '', | |
210 | 'private' => $private, | |
211 | 'linkIsNew' => true, | |
212 | ]; | |
213 | } | |
214 | ||
215 | $formatter = $this->getFormatter('raw'); | |
216 | $link = $formatter->format($bookmark); | |
217 | $link['linkIsNew'] = false; | |
218 | ||
219 | return $link; | |
220 | } | |
221 | ||
222 | protected function buildFormData(array $link, bool $isNew, Request $request): array | |
223 | { | |
224 | return escape([ | |
225 | 'link' => $link, | |
226 | 'link_is_new' => $isNew, | |
227 | 'http_referer' => $this->container->environment['HTTP_REFERER'] ?? '', | |
228 | 'source' => $request->getParam('source') ?? '', | |
229 | 'tags' => $this->getTags(), | |
230 | 'default_private_links' => $this->container->conf->get('privacy.default_private_links', false), | |
231 | 'async_metadata' => $this->container->conf->get('general.enable_async_metadata', true), | |
232 | 'retrieve_description' => $this->container->conf->get('general.retrieve_description', false), | |
233 | ]); | |
234 | } | |
235 | ||
236 | /** | |
237 | * Memoize formatterFactory->getFormatter() calls. | |
238 | */ | |
239 | protected function getFormatter(string $type): BookmarkFormatter | |
240 | { | |
241 | if (!array_key_exists($type, $this->formatters) || $this->formatters[$type] === null) { | |
242 | $this->formatters[$type] = $this->container->formatterFactory->getFormatter($type); | |
243 | } | |
244 | ||
245 | return $this->formatters[$type]; | |
246 | } | |
247 | ||
248 | /** | |
249 | * Memoize bookmarkService->bookmarksCountPerTag() calls. | |
250 | */ | |
251 | protected function getTags(): array | |
252 | { | |
253 | if ($this->tags === null) { | |
254 | $this->tags = $this->container->bookmarkService->bookmarksCountPerTag(); | |
255 | ||
256 | if ($this->container->conf->get('formatter') === 'markdown') { | |
257 | $this->tags[BookmarkMarkdownFormatter::NO_MD_TAG] = 1; | |
258 | } | |
259 | } | |
260 | ||
261 | return $this->tags; | |
262 | } | |
263 | } |