1 | <?php |
||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; |
||
4 | |||
5 | use Hateoas\Configuration\Route; |
||
6 | use Hateoas\Representation\Factory\PagerfantaFactory; |
||
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
||
8 | use Symfony\Component\HttpFoundation\JsonResponse; |
||
9 | use Symfony\Component\HttpFoundation\Request; |
||
10 | use Symfony\Component\HttpFoundation\Response; |
||
11 | use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
||
12 | use Symfony\Component\HttpKernel\Exception\HttpException; |
||
13 | use Wallabag\CoreBundle\Entity\Entry; |
||
14 | use Wallabag\CoreBundle\Entity\Tag; |
||
15 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; |
||
16 | use Wallabag\CoreBundle\Event\EntrySavedEvent; |
||
17 | use Wallabag\CoreBundle\Helper\UrlHasher; |
||
18 | |||
19 | class EntryRestController extends WallabagRestController |
||
20 | { |
||
21 | /** |
||
22 | * Check if an entry exist by url. |
||
23 | * Return ID if entry(ies) exist (and if you give the return_id parameter). |
||
24 | * Otherwise it returns false. |
||
25 | * |
||
26 | * @todo Remove that `return_id` in the next major release |
||
27 | * |
||
28 | * @ApiDoc( |
||
29 | * parameters={ |
||
30 | * {"name"="return_id", "dataType"="string", "required"=false, "format"="1 or 0", "description"="Set 1 if you want to retrieve ID in case entry(ies) exists, 0 by default"}, |
||
31 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="DEPRECATED, use hashed_url instead"}, |
||
32 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="DEPRECATED, use hashed_urls instead"}, |
||
33 | * {"name"="hashed_url", "dataType"="string", "required"=false, "format"="A hashed url", "description"="Hashed url using SHA1 to check if it exists"}, |
||
34 | * {"name"="hashed_urls", "dataType"="string", "required"=false, "format"="An array of hashed urls (?hashed_urls[]=xxx...&hashed_urls[]=xxx...)", "description"="An array of hashed urls using SHA1 to check if they exist"} |
||
35 | * } |
||
36 | * ) |
||
37 | * |
||
38 | * @return JsonResponse |
||
39 | */ |
||
40 | public function getEntriesExistsAction(Request $request) |
||
41 | { |
||
42 | $this->validateAuthentication(); |
||
43 | $repo = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); |
||
44 | |||
45 | $returnId = (null === $request->query->get('return_id')) ? false : (bool) $request->query->get('return_id'); |
||
46 | |||
47 | $hashedUrls = $request->query->get('hashed_urls', []); |
||
48 | $hashedUrl = $request->query->get('hashed_url', ''); |
||
49 | if (!empty($hashedUrl)) { |
||
50 | $hashedUrls[] = $hashedUrl; |
||
51 | } |
||
52 | |||
53 | $urls = $request->query->get('urls', []); |
||
54 | $url = $request->query->get('url', ''); |
||
55 | if (!empty($url)) { |
||
56 | $urls[] = $url; |
||
57 | } |
||
58 | |||
59 | $urlHashMap = []; |
||
60 | foreach ($urls as $urlToHash) { |
||
61 | $urlHash = UrlHasher::hashUrl($urlToHash); |
||
62 | $hashedUrls[] = $urlHash; |
||
63 | $urlHashMap[$urlHash] = $urlToHash; |
||
64 | } |
||
65 | |||
66 | if (empty($hashedUrls)) { |
||
67 | throw $this->createAccessDeniedException('URL is empty?, logged user id: ' . $this->getUser()->getId()); |
||
68 | } |
||
69 | |||
70 | $results = array_fill_keys($hashedUrls, null); |
||
71 | $res = $repo->findByUserIdAndBatchHashedUrls($this->getUser()->getId(), $hashedUrls); |
||
72 | foreach ($res as $e) { |
||
73 | $_hashedUrl = array_keys($hashedUrls, 'blah', true); |
||
0 ignored issues
–
show
Unused Code
introduced
by
Loading history...
|
|||
74 | if ([] !== array_keys($hashedUrls, $e['hashedUrl'], true)) { |
||
75 | $_hashedUrl = $e['hashedUrl']; |
||
76 | } elseif ([] !== array_keys($hashedUrls, $e['hashedGivenUrl'], true)) { |
||
77 | $_hashedUrl = $e['hashedGivenUrl']; |
||
78 | } else { |
||
79 | continue; |
||
80 | } |
||
81 | $results[$_hashedUrl] = $e['id']; |
||
82 | } |
||
83 | |||
84 | if (false === $returnId) { |
||
85 | $results = array_map(function ($v) { |
||
86 | return null !== $v; |
||
87 | }, $results); |
||
88 | } |
||
89 | |||
90 | $results = $this->replaceUrlHashes($results, $urlHashMap); |
||
91 | |||
92 | if (!empty($url) || !empty($hashedUrl)) { |
||
93 | $hu = array_keys($results)[0]; |
||
94 | |||
95 | return $this->sendResponse(['exists' => $results[$hu]]); |
||
96 | } |
||
97 | |||
98 | return $this->sendResponse($results); |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Retrieve all entries. It could be filtered by many options. |
||
103 | * |
||
104 | * @ApiDoc( |
||
105 | * parameters={ |
||
106 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."}, |
||
107 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."}, |
||
108 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated' or 'archived', default 'created'", "description"="sort entries by date."}, |
||
109 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, |
||
110 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, |
||
111 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, |
||
112 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, |
||
113 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, |
||
114 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"}, |
||
115 | * {"name"="detail", "dataType"="string", "required"=false, "format"="metadata or full, metadata by default", "description"="include content field if 'full'. 'full' by default for backward compatibility."}, |
||
116 | * } |
||
117 | * ) |
||
118 | * |
||
119 | * @return JsonResponse |
||
120 | */ |
||
121 | public function getEntriesAction(Request $request) |
||
122 | { |
||
123 | $this->validateAuthentication(); |
||
124 | |||
125 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); |
||
126 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); |
||
127 | $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public'); |
||
128 | $sort = strtolower($request->query->get('sort', 'created')); |
||
129 | $order = strtolower($request->query->get('order', 'desc')); |
||
130 | $page = (int) $request->query->get('page', 1); |
||
131 | $perPage = (int) $request->query->get('perPage', 30); |
||
132 | $tags = \is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', ''); |
||
133 | $since = $request->query->get('since', 0); |
||
134 | $detail = strtolower($request->query->get('detail', 'full')); |
||
135 | |||
136 | try { |
||
137 | /** @var \Pagerfanta\Pagerfanta $pager */ |
||
138 | $pager = $this->get('wallabag_core.entry_repository')->findEntries( |
||
139 | $this->getUser()->getId(), |
||
140 | $isArchived, |
||
141 | $isStarred, |
||
142 | $isPublic, |
||
143 | $sort, |
||
144 | $order, |
||
145 | $since, |
||
146 | $tags, |
||
147 | $detail |
||
148 | ); |
||
149 | } catch (\Exception $e) { |
||
150 | throw new BadRequestHttpException($e->getMessage()); |
||
151 | } |
||
152 | |||
153 | $pager->setMaxPerPage($perPage); |
||
154 | $pager->setCurrentPage($page); |
||
155 | |||
156 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); |
||
157 | $paginatedCollection = $pagerfantaFactory->createRepresentation( |
||
158 | $pager, |
||
159 | new Route( |
||
160 | 'api_get_entries', |
||
161 | [ |
||
162 | 'archive' => $isArchived, |
||
163 | 'starred' => $isStarred, |
||
164 | 'public' => $isPublic, |
||
165 | 'sort' => $sort, |
||
166 | 'order' => $order, |
||
167 | 'page' => $page, |
||
168 | 'perPage' => $perPage, |
||
169 | 'tags' => $tags, |
||
170 | 'since' => $since, |
||
171 | 'detail' => $detail, |
||
172 | ], |
||
173 | true |
||
174 | ) |
||
175 | ); |
||
176 | |||
177 | return $this->sendResponse($paginatedCollection); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Retrieve a single entry. |
||
182 | * |
||
183 | * @ApiDoc( |
||
184 | * requirements={ |
||
185 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
186 | * } |
||
187 | * ) |
||
188 | * |
||
189 | * @return JsonResponse |
||
190 | */ |
||
191 | public function getEntryAction(Entry $entry) |
||
192 | { |
||
193 | $this->validateAuthentication(); |
||
194 | $this->validateUserAccess($entry->getUser()->getId()); |
||
195 | |||
196 | return $this->sendResponse($entry); |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * Retrieve a single entry as a predefined format. |
||
201 | * |
||
202 | * @ApiDoc( |
||
203 | * requirements={ |
||
204 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
205 | * } |
||
206 | * ) |
||
207 | * |
||
208 | * @return Response |
||
209 | */ |
||
210 | public function getEntryExportAction(Entry $entry, Request $request) |
||
211 | { |
||
212 | $this->validateAuthentication(); |
||
213 | $this->validateUserAccess($entry->getUser()->getId()); |
||
214 | |||
215 | return $this->get('wallabag_core.helper.entries_export') |
||
216 | ->setEntries($entry) |
||
217 | ->updateTitle('entry') |
||
218 | ->updateAuthor('entry') |
||
219 | ->exportAs($request->attributes->get('_format')); |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Handles an entries list and delete URL. |
||
224 | * |
||
225 | * @ApiDoc( |
||
226 | * parameters={ |
||
227 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} |
||
228 | * } |
||
229 | * ) |
||
230 | * |
||
231 | * @return JsonResponse |
||
232 | */ |
||
233 | public function deleteEntriesListAction(Request $request) |
||
234 | { |
||
235 | $this->validateAuthentication(); |
||
236 | |||
237 | $urls = json_decode($request->query->get('urls', [])); |
||
238 | |||
239 | if (empty($urls)) { |
||
240 | return $this->sendResponse([]); |
||
241 | } |
||
242 | |||
243 | $results = []; |
||
244 | |||
245 | // handle multiple urls |
||
246 | foreach ($urls as $key => $url) { |
||
247 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
||
248 | $url, |
||
249 | $this->getUser()->getId() |
||
250 | ); |
||
251 | |||
252 | $results[$key]['url'] = $url; |
||
253 | |||
254 | if (false !== $entry) { |
||
255 | // entry deleted, dispatch event about it! |
||
256 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
||
257 | |||
258 | $em = $this->getDoctrine()->getManager(); |
||
259 | $em->remove($entry); |
||
260 | $em->flush(); |
||
261 | } |
||
262 | |||
263 | $results[$key]['entry'] = $entry instanceof Entry ? true : false; |
||
264 | } |
||
265 | |||
266 | return $this->sendResponse($results); |
||
267 | } |
||
268 | |||
269 | /** |
||
270 | * Handles an entries list and create URL. |
||
271 | * |
||
272 | * @ApiDoc( |
||
273 | * parameters={ |
||
274 | * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} |
||
275 | * } |
||
276 | * ) |
||
277 | * |
||
278 | * @throws HttpException When limit is reached |
||
279 | * |
||
280 | * @return JsonResponse |
||
281 | */ |
||
282 | public function postEntriesListAction(Request $request) |
||
283 | { |
||
284 | $this->validateAuthentication(); |
||
285 | |||
286 | $urls = json_decode($request->query->get('urls', [])); |
||
287 | |||
288 | $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); |
||
289 | |||
290 | if (\count($urls) > $limit) { |
||
291 | throw new HttpException(400, 'API limit reached'); |
||
292 | } |
||
293 | |||
294 | $results = []; |
||
295 | if (empty($urls)) { |
||
296 | return $this->sendResponse($results); |
||
297 | } |
||
298 | |||
299 | // handle multiple urls |
||
300 | foreach ($urls as $key => $url) { |
||
301 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
||
302 | $url, |
||
303 | $this->getUser()->getId() |
||
304 | ); |
||
305 | |||
306 | $results[$key]['url'] = $url; |
||
307 | |||
308 | if (false === $entry) { |
||
309 | $entry = new Entry($this->getUser()); |
||
310 | |||
311 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url); |
||
312 | } |
||
313 | |||
314 | $em = $this->getDoctrine()->getManager(); |
||
315 | $em->persist($entry); |
||
316 | $em->flush(); |
||
317 | |||
318 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; |
||
319 | |||
320 | // entry saved, dispatch event about it! |
||
321 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
||
322 | } |
||
323 | |||
324 | return $this->sendResponse($results); |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Create an entry. |
||
329 | * |
||
330 | * If you want to provide the HTML content (which means wallabag won't fetch it from the url), you must provide `content`, `title` & `url` fields **non-empty**. |
||
331 | * Otherwise, content will be fetched as normal from the url and values will be overwritten. |
||
332 | * |
||
333 | * @ApiDoc( |
||
334 | * parameters={ |
||
335 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, |
||
336 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, |
||
337 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
||
338 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, |
||
339 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, |
||
340 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, |
||
341 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, |
||
342 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, |
||
343 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, |
||
344 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, |
||
345 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, |
||
346 | * {"name"="origin_url", "dataType"="string", "required"=false, "format"="http://www.test.com/article.html", "description"="Origin url for the entry (from where you found it)."}, |
||
347 | * } |
||
348 | * ) |
||
349 | * |
||
350 | * @return JsonResponse |
||
351 | */ |
||
352 | public function postEntriesAction(Request $request) |
||
353 | { |
||
354 | $this->validateAuthentication(); |
||
355 | |||
356 | $url = $request->request->get('url'); |
||
357 | |||
358 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
||
359 | $url, |
||
360 | $this->getUser()->getId() |
||
361 | ); |
||
362 | |||
363 | if (false === $entry) { |
||
364 | $entry = new Entry($this->getUser()); |
||
365 | $entry->setUrl($url); |
||
366 | } |
||
367 | |||
368 | $data = $this->retrieveValueFromRequest($request); |
||
369 | |||
370 | try { |
||
371 | $this->get('wallabag_core.content_proxy')->updateEntry( |
||
372 | $entry, |
||
373 | $entry->getUrl(), |
||
374 | [ |
||
375 | 'title' => !empty($data['title']) ? $data['title'] : $entry->getTitle(), |
||
376 | 'html' => !empty($data['content']) ? $data['content'] : $entry->getContent(), |
||
377 | 'url' => $entry->getUrl(), |
||
378 | 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(), |
||
379 | 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(), |
||
380 | // faking the open graph preview picture |
||
381 | 'image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(), |
||
382 | 'authors' => \is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(), |
||
383 | ] |
||
384 | ); |
||
385 | } catch (\Exception $e) { |
||
386 | $this->get('logger')->error('Error while saving an entry', [ |
||
387 | 'exception' => $e, |
||
388 | 'entry' => $entry, |
||
389 | ]); |
||
390 | } |
||
391 | |||
392 | if (null !== $data['isArchived']) { |
||
393 | $entry->updateArchived((bool) $data['isArchived']); |
||
394 | } |
||
395 | |||
396 | if (null !== $data['isStarred']) { |
||
397 | $entry->updateStar((bool) $data['isStarred']); |
||
398 | } |
||
399 | |||
400 | if (!empty($data['tags'])) { |
||
401 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']); |
||
402 | } |
||
403 | |||
404 | if (!empty($data['origin_url'])) { |
||
405 | $entry->setOriginUrl($data['origin_url']); |
||
406 | } |
||
407 | |||
408 | if (null !== $data['isPublic']) { |
||
409 | if (true === (bool) $data['isPublic'] && null === $entry->getUid()) { |
||
410 | $entry->generateUid(); |
||
411 | } elseif (false === (bool) $data['isPublic']) { |
||
412 | $entry->cleanUid(); |
||
413 | } |
||
414 | } |
||
415 | |||
416 | if (empty($entry->getDomainName())) { |
||
417 | $this->get('wallabag_core.content_proxy')->setEntryDomainName($entry); |
||
418 | } |
||
419 | |||
420 | if (empty($entry->getTitle())) { |
||
421 | $this->get('wallabag_core.content_proxy')->setDefaultEntryTitle($entry); |
||
422 | } |
||
423 | |||
424 | $em = $this->getDoctrine()->getManager(); |
||
425 | $em->persist($entry); |
||
426 | $em->flush(); |
||
427 | |||
428 | // entry saved, dispatch event about it! |
||
429 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
||
430 | |||
431 | return $this->sendResponse($entry); |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * Change several properties of an entry. |
||
436 | * |
||
437 | * @ApiDoc( |
||
438 | * requirements={ |
||
439 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
440 | * }, |
||
441 | * parameters={ |
||
442 | * {"name"="title", "dataType"="string", "required"=false}, |
||
443 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
||
444 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, |
||
445 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, |
||
446 | * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, |
||
447 | * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, |
||
448 | * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, |
||
449 | * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, |
||
450 | * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, |
||
451 | * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, |
||
452 | * {"name"="origin_url", "dataType"="string", "required"=false, "format"="http://www.test.com/article.html", "description"="Origin url for the entry (from where you found it)."}, |
||
453 | * } |
||
454 | * ) |
||
455 | * |
||
456 | * @return JsonResponse |
||
457 | */ |
||
458 | public function patchEntriesAction(Entry $entry, Request $request) |
||
459 | { |
||
460 | $this->validateAuthentication(); |
||
461 | $this->validateUserAccess($entry->getUser()->getId()); |
||
462 | |||
463 | $contentProxy = $this->get('wallabag_core.content_proxy'); |
||
464 | |||
465 | $data = $this->retrieveValueFromRequest($request); |
||
466 | |||
467 | // this is a special case where user want to manually update the entry content |
||
468 | // the ContentProxy will only cleanup the html |
||
469 | // and also we force to not re-fetch the content in case of error |
||
470 | if (!empty($data['content'])) { |
||
471 | try { |
||
472 | $contentProxy->updateEntry( |
||
473 | $entry, |
||
474 | $entry->getUrl(), |
||
475 | [ |
||
476 | 'html' => $data['content'], |
||
477 | ], |
||
478 | true |
||
479 | ); |
||
480 | } catch (\Exception $e) { |
||
481 | $this->get('logger')->error('Error while saving an entry', [ |
||
482 | 'exception' => $e, |
||
483 | 'entry' => $entry, |
||
484 | ]); |
||
485 | } |
||
486 | } |
||
487 | |||
488 | if (!empty($data['title'])) { |
||
489 | $entry->setTitle($data['title']); |
||
490 | } |
||
491 | |||
492 | if (!empty($data['language'])) { |
||
493 | $contentProxy->updateLanguage($entry, $data['language']); |
||
494 | } |
||
495 | |||
496 | if (!empty($data['authors']) && \is_string($data['authors'])) { |
||
497 | $entry->setPublishedBy(explode(',', $data['authors'])); |
||
498 | } |
||
499 | |||
500 | if (!empty($data['picture'])) { |
||
501 | $contentProxy->updatePreviewPicture($entry, $data['picture']); |
||
502 | } |
||
503 | |||
504 | if (!empty($data['publishedAt'])) { |
||
505 | $contentProxy->updatePublishedAt($entry, $data['publishedAt']); |
||
506 | } |
||
507 | |||
508 | if (null !== $data['isArchived']) { |
||
509 | $entry->updateArchived((bool) $data['isArchived']); |
||
510 | } |
||
511 | |||
512 | if (null !== $data['isStarred']) { |
||
513 | $entry->updateStar((bool) $data['isStarred']); |
||
514 | } |
||
515 | |||
516 | if (!empty($data['tags'])) { |
||
517 | $entry->removeAllTags(); |
||
518 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']); |
||
519 | } |
||
520 | |||
521 | if (null !== $data['isPublic']) { |
||
522 | if (true === (bool) $data['isPublic'] && null === $entry->getUid()) { |
||
523 | $entry->generateUid(); |
||
524 | } elseif (false === (bool) $data['isPublic']) { |
||
525 | $entry->cleanUid(); |
||
526 | } |
||
527 | } |
||
528 | |||
529 | if (!empty($data['origin_url'])) { |
||
530 | $entry->setOriginUrl($data['origin_url']); |
||
531 | } |
||
532 | |||
533 | if (empty($entry->getDomainName())) { |
||
534 | $this->get('wallabag_core.content_proxy')->setEntryDomainName($entry); |
||
535 | } |
||
536 | |||
537 | if (empty($entry->getTitle())) { |
||
538 | $this->get('wallabag_core.content_proxy')->setDefaultEntryTitle($entry); |
||
539 | } |
||
540 | |||
541 | $em = $this->getDoctrine()->getManager(); |
||
542 | $em->persist($entry); |
||
543 | $em->flush(); |
||
544 | |||
545 | // entry saved, dispatch event about it! |
||
546 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
||
547 | |||
548 | return $this->sendResponse($entry); |
||
549 | } |
||
550 | |||
551 | /** |
||
552 | * Reload an entry. |
||
553 | * An empty response with HTTP Status 304 will be send if we weren't able to update the content (because it hasn't changed or we got an error). |
||
554 | * |
||
555 | * @ApiDoc( |
||
556 | * requirements={ |
||
557 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
558 | * } |
||
559 | * ) |
||
560 | * |
||
561 | * @return JsonResponse |
||
562 | */ |
||
563 | public function patchEntriesReloadAction(Entry $entry) |
||
564 | { |
||
565 | $this->validateAuthentication(); |
||
566 | $this->validateUserAccess($entry->getUser()->getId()); |
||
567 | |||
568 | try { |
||
569 | $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); |
||
570 | } catch (\Exception $e) { |
||
571 | $this->get('logger')->error('Error while saving an entry', [ |
||
572 | 'exception' => $e, |
||
573 | 'entry' => $entry, |
||
574 | ]); |
||
575 | |||
576 | return new JsonResponse([], 304); |
||
577 | } |
||
578 | |||
579 | // if refreshing entry failed, don't save it |
||
580 | if ($this->container->getParameter('wallabag_core.fetching_error_message') === $entry->getContent()) { |
||
581 | return new JsonResponse([], 304); |
||
582 | } |
||
583 | |||
584 | $em = $this->getDoctrine()->getManager(); |
||
585 | $em->persist($entry); |
||
586 | $em->flush(); |
||
587 | |||
588 | // entry saved, dispatch event about it! |
||
589 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); |
||
590 | |||
591 | return $this->sendResponse($entry); |
||
592 | } |
||
593 | |||
594 | /** |
||
595 | * Delete **permanently** an entry. |
||
596 | * |
||
597 | * @ApiDoc( |
||
598 | * requirements={ |
||
599 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
600 | * }, |
||
601 | * parameters={ |
||
602 | * {"name"="expect", "dataType"="string", "required"=false, "format"="id or entry", "description"="Only returns the id instead of the deleted entry's full entity if 'id' is specified. Default to entry"}, |
||
603 | * } |
||
604 | * ) |
||
605 | * |
||
606 | * @return JsonResponse |
||
607 | */ |
||
608 | public function deleteEntriesAction(Entry $entry, Request $request) |
||
609 | { |
||
610 | $expect = $request->query->get('expect', 'entry'); |
||
611 | if (!\in_array($expect, ['id', 'entry'], true)) { |
||
612 | throw new BadRequestHttpException(sprintf("expect: 'id' or 'entry' expected, %s given", $expect)); |
||
613 | } |
||
614 | $this->validateAuthentication(); |
||
615 | $this->validateUserAccess($entry->getUser()->getId()); |
||
616 | |||
617 | $response = $this->sendResponse([ |
||
618 | 'id' => $entry->getId(), |
||
619 | ]); |
||
620 | // We clone $entry to keep id in returned object |
||
621 | if ('entry' === $expect) { |
||
622 | $e = clone $entry; |
||
623 | $response = $this->sendResponse($e); |
||
624 | } |
||
625 | |||
626 | // entry deleted, dispatch event about it! |
||
627 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); |
||
628 | |||
629 | $em = $this->getDoctrine()->getManager(); |
||
630 | $em->remove($entry); |
||
631 | $em->flush(); |
||
632 | |||
633 | return $response; |
||
634 | } |
||
635 | |||
636 | /** |
||
637 | * Retrieve all tags for an entry. |
||
638 | * |
||
639 | * @ApiDoc( |
||
640 | * requirements={ |
||
641 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
642 | * } |
||
643 | * ) |
||
644 | * |
||
645 | * @return JsonResponse |
||
646 | */ |
||
647 | public function getEntriesTagsAction(Entry $entry) |
||
648 | { |
||
649 | $this->validateAuthentication(); |
||
650 | $this->validateUserAccess($entry->getUser()->getId()); |
||
651 | |||
652 | return $this->sendResponse($entry->getTags()); |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * Add one or more tags to an entry. |
||
657 | * |
||
658 | * @ApiDoc( |
||
659 | * requirements={ |
||
660 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
661 | * }, |
||
662 | * parameters={ |
||
663 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, |
||
664 | * } |
||
665 | * ) |
||
666 | * |
||
667 | * @return JsonResponse |
||
668 | */ |
||
669 | public function postEntriesTagsAction(Request $request, Entry $entry) |
||
670 | { |
||
671 | $this->validateAuthentication(); |
||
672 | $this->validateUserAccess($entry->getUser()->getId()); |
||
673 | |||
674 | $tags = $request->request->get('tags', ''); |
||
675 | if (!empty($tags)) { |
||
676 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); |
||
677 | } |
||
678 | |||
679 | $em = $this->getDoctrine()->getManager(); |
||
680 | $em->persist($entry); |
||
681 | $em->flush(); |
||
682 | |||
683 | return $this->sendResponse($entry); |
||
684 | } |
||
685 | |||
686 | /** |
||
687 | * Permanently remove one tag for an entry. |
||
688 | * |
||
689 | * @ApiDoc( |
||
690 | * requirements={ |
||
691 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"}, |
||
692 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} |
||
693 | * } |
||
694 | * ) |
||
695 | * |
||
696 | * @return JsonResponse |
||
697 | */ |
||
698 | public function deleteEntriesTagsAction(Entry $entry, Tag $tag) |
||
699 | { |
||
700 | $this->validateAuthentication(); |
||
701 | $this->validateUserAccess($entry->getUser()->getId()); |
||
702 | |||
703 | $entry->removeTag($tag); |
||
704 | $em = $this->getDoctrine()->getManager(); |
||
705 | $em->persist($entry); |
||
706 | $em->flush(); |
||
707 | |||
708 | return $this->sendResponse($entry); |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * Handles an entries list delete tags from them. |
||
713 | * |
||
714 | * @ApiDoc( |
||
715 | * parameters={ |
||
716 | * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} |
||
717 | * } |
||
718 | * ) |
||
719 | * |
||
720 | * @return JsonResponse |
||
721 | */ |
||
722 | public function deleteEntriesTagsListAction(Request $request) |
||
723 | { |
||
724 | $this->validateAuthentication(); |
||
725 | |||
726 | $list = json_decode($request->query->get('list', [])); |
||
727 | |||
728 | if (empty($list)) { |
||
729 | return $this->sendResponse([]); |
||
730 | } |
||
731 | |||
732 | // handle multiple urls |
||
733 | $results = []; |
||
734 | |||
735 | foreach ($list as $key => $element) { |
||
736 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
||
737 | $element->url, |
||
738 | $this->getUser()->getId() |
||
739 | ); |
||
740 | |||
741 | $results[$key]['url'] = $element->url; |
||
742 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; |
||
743 | |||
744 | $tags = $element->tags; |
||
745 | |||
746 | if (false !== $entry && !(empty($tags))) { |
||
747 | $tags = explode(',', $tags); |
||
748 | foreach ($tags as $label) { |
||
749 | $label = trim($label); |
||
750 | |||
751 | $tag = $this->getDoctrine() |
||
752 | ->getRepository('WallabagCoreBundle:Tag') |
||
753 | ->findOneByLabel($label); |
||
754 | |||
755 | if (false !== $tag) { |
||
756 | $entry->removeTag($tag); |
||
757 | } |
||
758 | } |
||
759 | |||
760 | $em = $this->getDoctrine()->getManager(); |
||
761 | $em->persist($entry); |
||
762 | $em->flush(); |
||
763 | } |
||
764 | } |
||
765 | |||
766 | return $this->sendResponse($results); |
||
767 | } |
||
768 | |||
769 | /** |
||
770 | * Handles an entries list and add tags to them. |
||
771 | * |
||
772 | * @ApiDoc( |
||
773 | * parameters={ |
||
774 | * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} |
||
775 | * } |
||
776 | * ) |
||
777 | * |
||
778 | * @return JsonResponse |
||
779 | */ |
||
780 | public function postEntriesTagsListAction(Request $request) |
||
781 | { |
||
782 | $this->validateAuthentication(); |
||
783 | |||
784 | $list = json_decode($request->query->get('list', [])); |
||
785 | |||
786 | if (empty($list)) { |
||
787 | return $this->sendResponse([]); |
||
788 | } |
||
789 | |||
790 | $results = []; |
||
791 | |||
792 | // handle multiple urls |
||
793 | foreach ($list as $key => $element) { |
||
794 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( |
||
795 | $element->url, |
||
796 | $this->getUser()->getId() |
||
797 | ); |
||
798 | |||
799 | $results[$key]['url'] = $element->url; |
||
800 | $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; |
||
801 | |||
802 | $tags = $element->tags; |
||
803 | |||
804 | if (false !== $entry && !(empty($tags))) { |
||
805 | $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); |
||
806 | |||
807 | $em = $this->getDoctrine()->getManager(); |
||
808 | $em->persist($entry); |
||
809 | $em->flush(); |
||
810 | } |
||
811 | } |
||
812 | |||
813 | return $this->sendResponse($results); |
||
814 | } |
||
815 | |||
816 | /** |
||
817 | * Replace the hashedUrl keys in $results with the unhashed URL from the |
||
818 | * request, as recorded in $urlHashMap. |
||
819 | */ |
||
820 | private function replaceUrlHashes(array $results, array $urlHashMap) |
||
821 | { |
||
822 | $newResults = []; |
||
823 | foreach ($results as $hash => $res) { |
||
824 | if (isset($urlHashMap[$hash])) { |
||
825 | $newResults[$urlHashMap[$hash]] = $res; |
||
826 | } else { |
||
827 | $newResults[$hash] = $res; |
||
828 | } |
||
829 | } |
||
830 | |||
831 | return $newResults; |
||
832 | } |
||
833 | |||
834 | /** |
||
835 | * Retrieve value from the request. |
||
836 | * Used for POST & PATCH on a an entry. |
||
837 | * |
||
838 | * @return array |
||
839 | */ |
||
840 | private function retrieveValueFromRequest(Request $request) |
||
841 | { |
||
842 | return [ |
||
843 | 'title' => $request->request->get('title'), |
||
844 | 'tags' => $request->request->get('tags', []), |
||
845 | 'isArchived' => $request->request->get('archive'), |
||
846 | 'isStarred' => $request->request->get('starred'), |
||
847 | 'isPublic' => $request->request->get('public'), |
||
848 | 'content' => $request->request->get('content'), |
||
849 | 'language' => $request->request->get('language'), |
||
850 | 'picture' => $request->request->get('preview_picture'), |
||
851 | 'publishedAt' => $request->request->get('published_at'), |
||
852 | 'authors' => $request->request->get('authors', ''), |
||
853 | 'origin_url' => $request->request->get('origin_url', ''), |
||
854 | ]; |
||
855 | } |
||
856 | } |
||
857 |