diff options
author | Jeremy Benoist <jeremy.benoist@gmail.com> | 2016-11-03 16:41:29 +0100 |
---|---|---|
committer | Jeremy Benoist <jeremy.benoist@gmail.com> | 2016-11-03 16:41:29 +0100 |
commit | 5a619812ca3eb05a82a023ccdaee13501eb8d45f (patch) | |
tree | a1541999a3e13f9bb8b45d3a61320ee61aa4eb3c /src/Wallabag/ApiBundle | |
parent | da4136557963018287cae61226e9006c3c741747 (diff) | |
parent | 84795d015b3c7e1af48a3dda3cb33cf080b66e8f (diff) | |
download | wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.tar.gz wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.tar.zst wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.zip |
Merge remote-tracking branch 'origin/master' into 2.2
Diffstat (limited to 'src/Wallabag/ApiBundle')
4 files changed, 563 insertions, 641 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php new file mode 100644 index 00000000..b3622c62 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -0,0 +1,374 @@ | |||
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\Request; | ||
9 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
11 | use Wallabag\CoreBundle\Entity\Entry; | ||
12 | use Wallabag\CoreBundle\Entity\Tag; | ||
13 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
14 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | ||
15 | |||
16 | class EntryRestController extends WallabagRestController | ||
17 | { | ||
18 | /** | ||
19 | * Check if an entry exist by url. | ||
20 | * | ||
21 | * @ApiDoc( | ||
22 | * parameters={ | ||
23 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | ||
24 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} | ||
25 | * } | ||
26 | * ) | ||
27 | * | ||
28 | * @return JsonResponse | ||
29 | */ | ||
30 | public function getEntriesExistsAction(Request $request) | ||
31 | { | ||
32 | $this->validateAuthentication(); | ||
33 | |||
34 | $urls = $request->query->get('urls', []); | ||
35 | |||
36 | // handle multiple urls first | ||
37 | if (!empty($urls)) { | ||
38 | $results = []; | ||
39 | foreach ($urls as $url) { | ||
40 | $res = $this->getDoctrine() | ||
41 | ->getRepository('WallabagCoreBundle:Entry') | ||
42 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
43 | |||
44 | $results[$url] = false === $res ? false : true; | ||
45 | } | ||
46 | |||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | ||
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | ||
51 | |||
52 | // let's see if it is a simple url? | ||
53 | $url = $request->query->get('url', ''); | ||
54 | |||
55 | if (empty($url)) { | ||
56 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | ||
57 | } | ||
58 | |||
59 | $res = $this->getDoctrine() | ||
60 | ->getRepository('WallabagCoreBundle:Entry') | ||
61 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
62 | |||
63 | $exists = false === $res ? false : true; | ||
64 | |||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
66 | |||
67 | return (new JsonResponse())->setJson($json); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Retrieve all entries. It could be filtered by many options. | ||
72 | * | ||
73 | * @ApiDoc( | ||
74 | * parameters={ | ||
75 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."}, | ||
76 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."}, | ||
77 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, | ||
78 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, | ||
79 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, | ||
80 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | ||
81 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | ||
82 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, | ||
83 | * } | ||
84 | * ) | ||
85 | * | ||
86 | * @return JsonResponse | ||
87 | */ | ||
88 | public function getEntriesAction(Request $request) | ||
89 | { | ||
90 | $this->validateAuthentication(); | ||
91 | |||
92 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | ||
93 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); | ||
94 | $sort = $request->query->get('sort', 'created'); | ||
95 | $order = $request->query->get('order', 'desc'); | ||
96 | $page = (int) $request->query->get('page', 1); | ||
97 | $perPage = (int) $request->query->get('perPage', 30); | ||
98 | $tags = $request->query->get('tags', ''); | ||
99 | $since = $request->query->get('since', 0); | ||
100 | |||
101 | $pager = $this->getDoctrine() | ||
102 | ->getRepository('WallabagCoreBundle:Entry') | ||
103 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | ||
104 | |||
105 | $pager->setCurrentPage($page); | ||
106 | $pager->setMaxPerPage($perPage); | ||
107 | |||
108 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); | ||
109 | $paginatedCollection = $pagerfantaFactory->createRepresentation( | ||
110 | $pager, | ||
111 | new Route( | ||
112 | 'api_get_entries', | ||
113 | [ | ||
114 | 'archive' => $isArchived, | ||
115 | 'starred' => $isStarred, | ||
116 | 'sort' => $sort, | ||
117 | 'order' => $order, | ||
118 | 'page' => $page, | ||
119 | 'perPage' => $perPage, | ||
120 | 'tags' => $tags, | ||
121 | 'since' => $since, | ||
122 | ], | ||
123 | UrlGeneratorInterface::ABSOLUTE_URL | ||
124 | ) | ||
125 | ); | ||
126 | |||
127 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | ||
128 | |||
129 | return (new JsonResponse())->setJson($json); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Retrieve a single entry. | ||
134 | * | ||
135 | * @ApiDoc( | ||
136 | * requirements={ | ||
137 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
138 | * } | ||
139 | * ) | ||
140 | * | ||
141 | * @return JsonResponse | ||
142 | */ | ||
143 | public function getEntryAction(Entry $entry) | ||
144 | { | ||
145 | $this->validateAuthentication(); | ||
146 | $this->validateUserAccess($entry->getUser()->getId()); | ||
147 | |||
148 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
149 | |||
150 | return (new JsonResponse())->setJson($json); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * Create an entry. | ||
155 | * | ||
156 | * @ApiDoc( | ||
157 | * parameters={ | ||
158 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | ||
159 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
160 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
161 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
162 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
163 | * } | ||
164 | * ) | ||
165 | * | ||
166 | * @return JsonResponse | ||
167 | */ | ||
168 | public function postEntriesAction(Request $request) | ||
169 | { | ||
170 | $this->validateAuthentication(); | ||
171 | |||
172 | $url = $request->request->get('url'); | ||
173 | $title = $request->request->get('title'); | ||
174 | $isArchived = $request->request->get('archive'); | ||
175 | $isStarred = $request->request->get('starred'); | ||
176 | |||
177 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
178 | |||
179 | if (false === $entry) { | ||
180 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
181 | new Entry($this->getUser()), | ||
182 | $url | ||
183 | ); | ||
184 | } | ||
185 | |||
186 | if (!is_null($title)) { | ||
187 | $entry->setTitle($title); | ||
188 | } | ||
189 | |||
190 | $tags = $request->request->get('tags', ''); | ||
191 | if (!empty($tags)) { | ||
192 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
193 | } | ||
194 | |||
195 | if (!is_null($isStarred)) { | ||
196 | $entry->setStarred((bool) $isStarred); | ||
197 | } | ||
198 | |||
199 | if (!is_null($isArchived)) { | ||
200 | $entry->setArchived((bool) $isArchived); | ||
201 | } | ||
202 | |||
203 | $em = $this->getDoctrine()->getManager(); | ||
204 | $em->persist($entry); | ||
205 | $em->flush(); | ||
206 | |||
207 | // entry saved, dispatch event about it! | ||
208 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
209 | |||
210 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
211 | |||
212 | return (new JsonResponse())->setJson($json); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Change several properties of an entry. | ||
217 | * | ||
218 | * @ApiDoc( | ||
219 | * requirements={ | ||
220 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
221 | * }, | ||
222 | * parameters={ | ||
223 | * {"name"="title", "dataType"="string", "required"=false}, | ||
224 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
225 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, | ||
226 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, | ||
227 | * } | ||
228 | * ) | ||
229 | * | ||
230 | * @return JsonResponse | ||
231 | */ | ||
232 | public function patchEntriesAction(Entry $entry, Request $request) | ||
233 | { | ||
234 | $this->validateAuthentication(); | ||
235 | $this->validateUserAccess($entry->getUser()->getId()); | ||
236 | |||
237 | $title = $request->request->get('title'); | ||
238 | $isArchived = $request->request->get('archive'); | ||
239 | $isStarred = $request->request->get('starred'); | ||
240 | |||
241 | if (!is_null($title)) { | ||
242 | $entry->setTitle($title); | ||
243 | } | ||
244 | |||
245 | if (!is_null($isArchived)) { | ||
246 | $entry->setArchived((bool) $isArchived); | ||
247 | } | ||
248 | |||
249 | if (!is_null($isStarred)) { | ||
250 | $entry->setStarred((bool) $isStarred); | ||
251 | } | ||
252 | |||
253 | $tags = $request->request->get('tags', ''); | ||
254 | if (!empty($tags)) { | ||
255 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
256 | } | ||
257 | |||
258 | $em = $this->getDoctrine()->getManager(); | ||
259 | $em->flush(); | ||
260 | |||
261 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
262 | |||
263 | return (new JsonResponse())->setJson($json); | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * Delete **permanently** an entry. | ||
268 | * | ||
269 | * @ApiDoc( | ||
270 | * requirements={ | ||
271 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
272 | * } | ||
273 | * ) | ||
274 | * | ||
275 | * @return JsonResponse | ||
276 | */ | ||
277 | public function deleteEntriesAction(Entry $entry) | ||
278 | { | ||
279 | $this->validateAuthentication(); | ||
280 | $this->validateUserAccess($entry->getUser()->getId()); | ||
281 | |||
282 | $em = $this->getDoctrine()->getManager(); | ||
283 | $em->remove($entry); | ||
284 | $em->flush(); | ||
285 | |||
286 | // entry deleted, dispatch event about it! | ||
287 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
288 | |||
289 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
290 | |||
291 | return (new JsonResponse())->setJson($json); | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * Retrieve all tags for an entry. | ||
296 | * | ||
297 | * @ApiDoc( | ||
298 | * requirements={ | ||
299 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
300 | * } | ||
301 | * ) | ||
302 | * | ||
303 | * @return JsonResponse | ||
304 | */ | ||
305 | public function getEntriesTagsAction(Entry $entry) | ||
306 | { | ||
307 | $this->validateAuthentication(); | ||
308 | $this->validateUserAccess($entry->getUser()->getId()); | ||
309 | |||
310 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | ||
311 | |||
312 | return (new JsonResponse())->setJson($json); | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * Add one or more tags to an entry. | ||
317 | * | ||
318 | * @ApiDoc( | ||
319 | * requirements={ | ||
320 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
321 | * }, | ||
322 | * parameters={ | ||
323 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
324 | * } | ||
325 | * ) | ||
326 | * | ||
327 | * @return JsonResponse | ||
328 | */ | ||
329 | public function postEntriesTagsAction(Request $request, Entry $entry) | ||
330 | { | ||
331 | $this->validateAuthentication(); | ||
332 | $this->validateUserAccess($entry->getUser()->getId()); | ||
333 | |||
334 | $tags = $request->request->get('tags', ''); | ||
335 | if (!empty($tags)) { | ||
336 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
337 | } | ||
338 | |||
339 | $em = $this->getDoctrine()->getManager(); | ||
340 | $em->persist($entry); | ||
341 | $em->flush(); | ||
342 | |||
343 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
344 | |||
345 | return (new JsonResponse())->setJson($json); | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * Permanently remove one tag for an entry. | ||
350 | * | ||
351 | * @ApiDoc( | ||
352 | * requirements={ | ||
353 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"}, | ||
354 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
355 | * } | ||
356 | * ) | ||
357 | * | ||
358 | * @return JsonResponse | ||
359 | */ | ||
360 | public function deleteEntriesTagsAction(Entry $entry, Tag $tag) | ||
361 | { | ||
362 | $this->validateAuthentication(); | ||
363 | $this->validateUserAccess($entry->getUser()->getId()); | ||
364 | |||
365 | $entry->removeTag($tag); | ||
366 | $em = $this->getDoctrine()->getManager(); | ||
367 | $em->persist($entry); | ||
368 | $em->flush(); | ||
369 | |||
370 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
371 | |||
372 | return (new JsonResponse())->setJson($json); | ||
373 | } | ||
374 | } | ||
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php new file mode 100644 index 00000000..4e7ddc66 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php | |||
@@ -0,0 +1,171 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
6 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
8 | use Wallabag\CoreBundle\Entity\Entry; | ||
9 | use Wallabag\CoreBundle\Entity\Tag; | ||
10 | |||
11 | class TagRestController extends WallabagRestController | ||
12 | { | ||
13 | /** | ||
14 | * Retrieve all tags. | ||
15 | * | ||
16 | * @ApiDoc() | ||
17 | * | ||
18 | * @return JsonResponse | ||
19 | */ | ||
20 | public function getTagsAction() | ||
21 | { | ||
22 | $this->validateAuthentication(); | ||
23 | |||
24 | $tags = $this->getDoctrine() | ||
25 | ->getRepository('WallabagCoreBundle:Tag') | ||
26 | ->findAllTags($this->getUser()->getId()); | ||
27 | |||
28 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
29 | |||
30 | return (new JsonResponse())->setJson($json); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Permanently remove one tag from **every** entry. | ||
35 | * | ||
36 | * @ApiDoc( | ||
37 | * requirements={ | ||
38 | * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"} | ||
39 | * } | ||
40 | * ) | ||
41 | * | ||
42 | * @return JsonResponse | ||
43 | */ | ||
44 | public function deleteTagLabelAction(Request $request) | ||
45 | { | ||
46 | $this->validateAuthentication(); | ||
47 | $label = $request->request->get('tag', ''); | ||
48 | |||
49 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); | ||
50 | |||
51 | if (empty($tag)) { | ||
52 | throw $this->createNotFoundException('Tag not found'); | ||
53 | } | ||
54 | |||
55 | $this->getDoctrine() | ||
56 | ->getRepository('WallabagCoreBundle:Entry') | ||
57 | ->removeTag($this->getUser()->getId(), $tag); | ||
58 | |||
59 | $this->cleanOrphanTag($tag); | ||
60 | |||
61 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
62 | |||
63 | return (new JsonResponse())->setJson($json); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Permanently remove some tags from **every** entry. | ||
68 | * | ||
69 | * @ApiDoc( | ||
70 | * requirements={ | ||
71 | * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"} | ||
72 | * } | ||
73 | * ) | ||
74 | * | ||
75 | * @return JsonResponse | ||
76 | */ | ||
77 | public function deleteTagsLabelAction(Request $request) | ||
78 | { | ||
79 | $this->validateAuthentication(); | ||
80 | |||
81 | $tagsLabels = $request->request->get('tags', ''); | ||
82 | |||
83 | $tags = []; | ||
84 | |||
85 | foreach (explode(',', $tagsLabels) as $tagLabel) { | ||
86 | $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel); | ||
87 | |||
88 | if (!empty($tagEntity)) { | ||
89 | $tags[] = $tagEntity; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (empty($tags)) { | ||
94 | throw $this->createNotFoundException('Tags not found'); | ||
95 | } | ||
96 | |||
97 | $this->getDoctrine() | ||
98 | ->getRepository('WallabagCoreBundle:Entry') | ||
99 | ->removeTags($this->getUser()->getId(), $tags); | ||
100 | |||
101 | $this->cleanOrphanTag($tags); | ||
102 | |||
103 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
104 | |||
105 | return (new JsonResponse())->setJson($json); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Permanently remove one tag from **every** entry. | ||
110 | * | ||
111 | * @ApiDoc( | ||
112 | * requirements={ | ||
113 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"} | ||
114 | * } | ||
115 | * ) | ||
116 | * | ||
117 | * @return JsonResponse | ||
118 | */ | ||
119 | public function deleteTagAction(Tag $tag) | ||
120 | { | ||
121 | $this->validateAuthentication(); | ||
122 | |||
123 | $this->getDoctrine() | ||
124 | ->getRepository('WallabagCoreBundle:Entry') | ||
125 | ->removeTag($this->getUser()->getId(), $tag); | ||
126 | |||
127 | $this->cleanOrphanTag($tag); | ||
128 | |||
129 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
130 | |||
131 | return (new JsonResponse())->setJson($json); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Retrieve version number. | ||
136 | * | ||
137 | * @ApiDoc() | ||
138 | * | ||
139 | * @return JsonResponse | ||
140 | */ | ||
141 | public function getVersionAction() | ||
142 | { | ||
143 | $version = $this->container->getParameter('wallabag_core.version'); | ||
144 | |||
145 | $json = $this->get('serializer')->serialize($version, 'json'); | ||
146 | |||
147 | return (new JsonResponse())->setJson($json); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Remove orphan tag in case no entries are associated to it. | ||
152 | * | ||
153 | * @param Tag|array $tags | ||
154 | */ | ||
155 | private function cleanOrphanTag($tags) | ||
156 | { | ||
157 | if (!is_array($tags)) { | ||
158 | $tags = [$tags]; | ||
159 | } | ||
160 | |||
161 | $em = $this->getDoctrine()->getManager(); | ||
162 | |||
163 | foreach ($tags as $tag) { | ||
164 | if (count($tag->getEntries()) === 0) { | ||
165 | $em->remove($tag); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | $em->flush(); | ||
170 | } | ||
171 | } | ||
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index 50652b77..544c1ea9 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php | |||
@@ -3,627 +3,11 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use FOS\RestBundle\Controller\FOSRestController; | 5 | use FOS\RestBundle\Controller\FOSRestController; |
6 | use Hateoas\Configuration\Route as HateoasRoute; | ||
7 | use Hateoas\Representation\Factory\PagerfantaFactory; | ||
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
12 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
13 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; | 6 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; |
14 | use Wallabag\CoreBundle\Entity\Entry; | 7 | use Wallabag\CoreBundle\Entity\Entry; |
15 | use Wallabag\CoreBundle\Entity\Tag; | ||
16 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
17 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
18 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | ||
19 | 8 | ||
20 | class WallabagRestController extends FOSRestController | 9 | class WallabagRestController extends FOSRestController |
21 | { | 10 | { |
22 | private function validateAuthentication() | ||
23 | { | ||
24 | if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { | ||
25 | throw new AccessDeniedException(); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * Check if an entry exist by url. | ||
31 | * | ||
32 | * @ApiDoc( | ||
33 | * parameters={ | ||
34 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | ||
35 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} | ||
36 | * } | ||
37 | * ) | ||
38 | * | ||
39 | * @return JsonResponse | ||
40 | */ | ||
41 | public function getEntriesExistsAction(Request $request) | ||
42 | { | ||
43 | $this->validateAuthentication(); | ||
44 | |||
45 | $urls = $request->query->get('urls', []); | ||
46 | |||
47 | // handle multiple urls first | ||
48 | if (!empty($urls)) { | ||
49 | $results = []; | ||
50 | foreach ($urls as $url) { | ||
51 | $res = $this->getDoctrine() | ||
52 | ->getRepository('WallabagCoreBundle:Entry') | ||
53 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
54 | |||
55 | $results[$url] = false === $res ? false : true; | ||
56 | } | ||
57 | |||
58 | $json = $this->get('serializer')->serialize($results, 'json'); | ||
59 | |||
60 | return (new JsonResponse())->setJson($json); | ||
61 | } | ||
62 | |||
63 | // let's see if it is a simple url? | ||
64 | $url = $request->query->get('url', ''); | ||
65 | |||
66 | if (empty($url)) { | ||
67 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | ||
68 | } | ||
69 | |||
70 | $res = $this->getDoctrine() | ||
71 | ->getRepository('WallabagCoreBundle:Entry') | ||
72 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
73 | |||
74 | $exists = false === $res ? false : true; | ||
75 | |||
76 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
77 | |||
78 | return (new JsonResponse())->setJson($json); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Retrieve all entries. It could be filtered by many options. | ||
83 | * | ||
84 | * @ApiDoc( | ||
85 | * parameters={ | ||
86 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."}, | ||
87 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."}, | ||
88 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, | ||
89 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, | ||
90 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, | ||
91 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | ||
92 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | ||
93 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, | ||
94 | * } | ||
95 | * ) | ||
96 | * | ||
97 | * @return JsonResponse | ||
98 | */ | ||
99 | public function getEntriesAction(Request $request) | ||
100 | { | ||
101 | $this->validateAuthentication(); | ||
102 | |||
103 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | ||
104 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); | ||
105 | $sort = $request->query->get('sort', 'created'); | ||
106 | $order = $request->query->get('order', 'desc'); | ||
107 | $page = (int) $request->query->get('page', 1); | ||
108 | $perPage = (int) $request->query->get('perPage', 30); | ||
109 | $tags = $request->query->get('tags', ''); | ||
110 | $since = $request->query->get('since', 0); | ||
111 | |||
112 | $pager = $this->getDoctrine() | ||
113 | ->getRepository('WallabagCoreBundle:Entry') | ||
114 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | ||
115 | |||
116 | $pager->setCurrentPage($page); | ||
117 | $pager->setMaxPerPage($perPage); | ||
118 | |||
119 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); | ||
120 | $paginatedCollection = $pagerfantaFactory->createRepresentation( | ||
121 | $pager, | ||
122 | new HateoasRoute( | ||
123 | 'api_get_entries', | ||
124 | [ | ||
125 | 'archive' => $isArchived, | ||
126 | 'starred' => $isStarred, | ||
127 | 'sort' => $sort, | ||
128 | 'order' => $order, | ||
129 | 'page' => $page, | ||
130 | 'perPage' => $perPage, | ||
131 | 'tags' => $tags, | ||
132 | 'since' => $since, | ||
133 | ], | ||
134 | UrlGeneratorInterface::ABSOLUTE_URL | ||
135 | ) | ||
136 | ); | ||
137 | |||
138 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | ||
139 | |||
140 | return (new JsonResponse())->setJson($json); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Retrieve a single entry. | ||
145 | * | ||
146 | * @ApiDoc( | ||
147 | * requirements={ | ||
148 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
149 | * } | ||
150 | * ) | ||
151 | * | ||
152 | * @return JsonResponse | ||
153 | */ | ||
154 | public function getEntryAction(Entry $entry) | ||
155 | { | ||
156 | $this->validateAuthentication(); | ||
157 | $this->validateUserAccess($entry->getUser()->getId()); | ||
158 | |||
159 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
160 | |||
161 | return (new JsonResponse())->setJson($json); | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * Retrieve a single entry as a predefined format. | ||
166 | * | ||
167 | * @ApiDoc( | ||
168 | * requirements={ | ||
169 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
170 | * } | ||
171 | * ) | ||
172 | * | ||
173 | * @return Response | ||
174 | */ | ||
175 | public function getEntryExportAction(Entry $entry, Request $request) | ||
176 | { | ||
177 | $this->validateAuthentication(); | ||
178 | $this->validateUserAccess($entry->getUser()->getId()); | ||
179 | |||
180 | return $this->get('wallabag_core.helper.entries_export') | ||
181 | ->setEntries($entry) | ||
182 | ->updateTitle('entry') | ||
183 | ->exportAs($request->attributes->get('_format')); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * Create an entry. | ||
188 | * | ||
189 | * @ApiDoc( | ||
190 | * parameters={ | ||
191 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | ||
192 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
193 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
194 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
195 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
196 | * } | ||
197 | * ) | ||
198 | * | ||
199 | * @return JsonResponse | ||
200 | */ | ||
201 | public function postEntriesAction(Request $request) | ||
202 | { | ||
203 | $this->validateAuthentication(); | ||
204 | |||
205 | $url = $request->request->get('url'); | ||
206 | $title = $request->request->get('title'); | ||
207 | $isArchived = $request->request->get('archive'); | ||
208 | $isStarred = $request->request->get('starred'); | ||
209 | |||
210 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
211 | |||
212 | if (false === $entry) { | ||
213 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
214 | new Entry($this->getUser()), | ||
215 | $url | ||
216 | ); | ||
217 | } | ||
218 | |||
219 | if (!is_null($title)) { | ||
220 | $entry->setTitle($title); | ||
221 | } | ||
222 | |||
223 | $tags = $request->request->get('tags', ''); | ||
224 | if (!empty($tags)) { | ||
225 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
226 | } | ||
227 | |||
228 | if (!is_null($isStarred)) { | ||
229 | $entry->setStarred((bool) $isStarred); | ||
230 | } | ||
231 | |||
232 | if (!is_null($isArchived)) { | ||
233 | $entry->setArchived((bool) $isArchived); | ||
234 | } | ||
235 | |||
236 | $em = $this->getDoctrine()->getManager(); | ||
237 | $em->persist($entry); | ||
238 | $em->flush(); | ||
239 | |||
240 | // entry saved, dispatch event about it! | ||
241 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
242 | |||
243 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
244 | |||
245 | return (new JsonResponse())->setJson($json); | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Change several properties of an entry. | ||
250 | * | ||
251 | * @ApiDoc( | ||
252 | * requirements={ | ||
253 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
254 | * }, | ||
255 | * parameters={ | ||
256 | * {"name"="title", "dataType"="string", "required"=false}, | ||
257 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
258 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, | ||
259 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, | ||
260 | * } | ||
261 | * ) | ||
262 | * | ||
263 | * @return JsonResponse | ||
264 | */ | ||
265 | public function patchEntriesAction(Entry $entry, Request $request) | ||
266 | { | ||
267 | $this->validateAuthentication(); | ||
268 | $this->validateUserAccess($entry->getUser()->getId()); | ||
269 | |||
270 | $title = $request->request->get('title'); | ||
271 | $isArchived = $request->request->get('archive'); | ||
272 | $isStarred = $request->request->get('starred'); | ||
273 | |||
274 | if (!is_null($title)) { | ||
275 | $entry->setTitle($title); | ||
276 | } | ||
277 | |||
278 | if (!is_null($isArchived)) { | ||
279 | $entry->setArchived((bool) $isArchived); | ||
280 | } | ||
281 | |||
282 | if (!is_null($isStarred)) { | ||
283 | $entry->setStarred((bool) $isStarred); | ||
284 | } | ||
285 | |||
286 | $tags = $request->request->get('tags', ''); | ||
287 | if (!empty($tags)) { | ||
288 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
289 | } | ||
290 | |||
291 | $em = $this->getDoctrine()->getManager(); | ||
292 | $em->flush(); | ||
293 | |||
294 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
295 | |||
296 | return (new JsonResponse())->setJson($json); | ||
297 | } | ||
298 | |||
299 | /** | ||
300 | * Delete **permanently** an entry. | ||
301 | * | ||
302 | * @ApiDoc( | ||
303 | * requirements={ | ||
304 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
305 | * } | ||
306 | * ) | ||
307 | * | ||
308 | * @return JsonResponse | ||
309 | */ | ||
310 | public function deleteEntriesAction(Entry $entry) | ||
311 | { | ||
312 | $this->validateAuthentication(); | ||
313 | $this->validateUserAccess($entry->getUser()->getId()); | ||
314 | |||
315 | // entry deleted, dispatch event about it! | ||
316 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
317 | |||
318 | $em = $this->getDoctrine()->getManager(); | ||
319 | $em->remove($entry); | ||
320 | $em->flush(); | ||
321 | |||
322 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
323 | |||
324 | return (new JsonResponse())->setJson($json); | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * Retrieve all tags for an entry. | ||
329 | * | ||
330 | * @ApiDoc( | ||
331 | * requirements={ | ||
332 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
333 | * } | ||
334 | * ) | ||
335 | * | ||
336 | * @return JsonResponse | ||
337 | */ | ||
338 | public function getEntriesTagsAction(Entry $entry) | ||
339 | { | ||
340 | $this->validateAuthentication(); | ||
341 | $this->validateUserAccess($entry->getUser()->getId()); | ||
342 | |||
343 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | ||
344 | |||
345 | return (new JsonResponse())->setJson($json); | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * Add one or more tags to an entry. | ||
350 | * | ||
351 | * @ApiDoc( | ||
352 | * requirements={ | ||
353 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
354 | * }, | ||
355 | * parameters={ | ||
356 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
357 | * } | ||
358 | * ) | ||
359 | * | ||
360 | * @return JsonResponse | ||
361 | */ | ||
362 | public function postEntriesTagsAction(Request $request, Entry $entry) | ||
363 | { | ||
364 | $this->validateAuthentication(); | ||
365 | $this->validateUserAccess($entry->getUser()->getId()); | ||
366 | |||
367 | $tags = $request->request->get('tags', ''); | ||
368 | if (!empty($tags)) { | ||
369 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
370 | } | ||
371 | |||
372 | $em = $this->getDoctrine()->getManager(); | ||
373 | $em->persist($entry); | ||
374 | $em->flush(); | ||
375 | |||
376 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
377 | |||
378 | return (new JsonResponse())->setJson($json); | ||
379 | } | ||
380 | |||
381 | /** | ||
382 | * Permanently remove one tag for an entry. | ||
383 | * | ||
384 | * @ApiDoc( | ||
385 | * requirements={ | ||
386 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"}, | ||
387 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
388 | * } | ||
389 | * ) | ||
390 | * | ||
391 | * @return JsonResponse | ||
392 | */ | ||
393 | public function deleteEntriesTagsAction(Entry $entry, Tag $tag) | ||
394 | { | ||
395 | $this->validateAuthentication(); | ||
396 | $this->validateUserAccess($entry->getUser()->getId()); | ||
397 | |||
398 | $entry->removeTag($tag); | ||
399 | $em = $this->getDoctrine()->getManager(); | ||
400 | $em->persist($entry); | ||
401 | $em->flush(); | ||
402 | |||
403 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
404 | |||
405 | return (new JsonResponse())->setJson($json); | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * Retrieve all tags. | ||
410 | * | ||
411 | * @ApiDoc() | ||
412 | * | ||
413 | * @return JsonResponse | ||
414 | */ | ||
415 | public function getTagsAction() | ||
416 | { | ||
417 | $this->validateAuthentication(); | ||
418 | |||
419 | $tags = $this->getDoctrine() | ||
420 | ->getRepository('WallabagCoreBundle:Tag') | ||
421 | ->findAllTags($this->getUser()->getId()); | ||
422 | |||
423 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
424 | |||
425 | return (new JsonResponse())->setJson($json); | ||
426 | } | ||
427 | |||
428 | /** | ||
429 | * Permanently remove one tag from **every** entry. | ||
430 | * | ||
431 | * @ApiDoc( | ||
432 | * requirements={ | ||
433 | * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"} | ||
434 | * } | ||
435 | * ) | ||
436 | * | ||
437 | * @return JsonResponse | ||
438 | */ | ||
439 | public function deleteTagLabelAction(Request $request) | ||
440 | { | ||
441 | $this->validateAuthentication(); | ||
442 | $label = $request->request->get('tag', ''); | ||
443 | |||
444 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); | ||
445 | |||
446 | if (empty($tag)) { | ||
447 | throw $this->createNotFoundException('Tag not found'); | ||
448 | } | ||
449 | |||
450 | $this->getDoctrine() | ||
451 | ->getRepository('WallabagCoreBundle:Entry') | ||
452 | ->removeTag($this->getUser()->getId(), $tag); | ||
453 | |||
454 | $this->cleanOrphanTag($tag); | ||
455 | |||
456 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
457 | |||
458 | return (new JsonResponse())->setJson($json); | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * Permanently remove some tags from **every** entry. | ||
463 | * | ||
464 | * @ApiDoc( | ||
465 | * requirements={ | ||
466 | * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"} | ||
467 | * } | ||
468 | * ) | ||
469 | * | ||
470 | * @return JsonResponse | ||
471 | */ | ||
472 | public function deleteTagsLabelAction(Request $request) | ||
473 | { | ||
474 | $this->validateAuthentication(); | ||
475 | |||
476 | $tagsLabels = $request->request->get('tags', ''); | ||
477 | |||
478 | $tags = []; | ||
479 | |||
480 | foreach (explode(',', $tagsLabels) as $tagLabel) { | ||
481 | $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel); | ||
482 | |||
483 | if (!empty($tagEntity)) { | ||
484 | $tags[] = $tagEntity; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | if (empty($tags)) { | ||
489 | throw $this->createNotFoundException('Tags not found'); | ||
490 | } | ||
491 | |||
492 | $this->getDoctrine() | ||
493 | ->getRepository('WallabagCoreBundle:Entry') | ||
494 | ->removeTags($this->getUser()->getId(), $tags); | ||
495 | |||
496 | $this->cleanOrphanTag($tags); | ||
497 | |||
498 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
499 | |||
500 | return (new JsonResponse())->setJson($json); | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * Permanently remove one tag from **every** entry. | ||
505 | * | ||
506 | * @ApiDoc( | ||
507 | * requirements={ | ||
508 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"} | ||
509 | * } | ||
510 | * ) | ||
511 | * | ||
512 | * @return JsonResponse | ||
513 | */ | ||
514 | public function deleteTagAction(Tag $tag) | ||
515 | { | ||
516 | $this->validateAuthentication(); | ||
517 | |||
518 | $this->getDoctrine() | ||
519 | ->getRepository('WallabagCoreBundle:Entry') | ||
520 | ->removeTag($this->getUser()->getId(), $tag); | ||
521 | |||
522 | $this->cleanOrphanTag($tag); | ||
523 | |||
524 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
525 | |||
526 | return (new JsonResponse())->setJson($json); | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * Retrieve annotations for an entry. | ||
531 | * | ||
532 | * @ApiDoc( | ||
533 | * requirements={ | ||
534 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
535 | * } | ||
536 | * ) | ||
537 | * | ||
538 | * @param Entry $entry | ||
539 | * | ||
540 | * @return JsonResponse | ||
541 | */ | ||
542 | public function getAnnotationsAction(Entry $entry) | ||
543 | { | ||
544 | $this->validateAuthentication(); | ||
545 | |||
546 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:getAnnotations', [ | ||
547 | 'entry' => $entry, | ||
548 | ]); | ||
549 | } | ||
550 | |||
551 | /** | ||
552 | * Creates a new annotation. | ||
553 | * | ||
554 | * @ApiDoc( | ||
555 | * requirements={ | ||
556 | * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"}, | ||
557 | * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"}, | ||
558 | * {"name"="text", "dataType"="string", "required"=true, "description"=""}, | ||
559 | * } | ||
560 | * ) | ||
561 | * | ||
562 | * @param Request $request | ||
563 | * @param Entry $entry | ||
564 | * | ||
565 | * @return JsonResponse | ||
566 | */ | ||
567 | public function postAnnotationAction(Request $request, Entry $entry) | ||
568 | { | ||
569 | $this->validateAuthentication(); | ||
570 | |||
571 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:postAnnotation', [ | ||
572 | 'request' => $request, | ||
573 | 'entry' => $entry, | ||
574 | ]); | ||
575 | } | ||
576 | |||
577 | /** | ||
578 | * Updates an annotation. | ||
579 | * | ||
580 | * @ApiDoc( | ||
581 | * requirements={ | ||
582 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
583 | * } | ||
584 | * ) | ||
585 | * | ||
586 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | ||
587 | * | ||
588 | * @param Annotation $annotation | ||
589 | * @param Request $request | ||
590 | * | ||
591 | * @return JsonResponse | ||
592 | */ | ||
593 | public function putAnnotationAction(Annotation $annotation, Request $request) | ||
594 | { | ||
595 | $this->validateAuthentication(); | ||
596 | |||
597 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:putAnnotation', [ | ||
598 | 'annotation' => $annotation, | ||
599 | 'request' => $request, | ||
600 | ]); | ||
601 | } | ||
602 | |||
603 | /** | ||
604 | * Removes an annotation. | ||
605 | * | ||
606 | * @ApiDoc( | ||
607 | * requirements={ | ||
608 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
609 | * } | ||
610 | * ) | ||
611 | * | ||
612 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | ||
613 | * | ||
614 | * @param Annotation $annotation | ||
615 | * | ||
616 | * @return JsonResponse | ||
617 | */ | ||
618 | public function deleteAnnotationAction(Annotation $annotation) | ||
619 | { | ||
620 | $this->validateAuthentication(); | ||
621 | |||
622 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:deleteAnnotation', [ | ||
623 | 'annotation' => $annotation, | ||
624 | ]); | ||
625 | } | ||
626 | |||
627 | /** | 11 | /** |
628 | * Retrieve version number. | 12 | * Retrieve version number. |
629 | * | 13 | * |
@@ -634,32 +18,15 @@ class WallabagRestController extends FOSRestController | |||
634 | public function getVersionAction() | 18 | public function getVersionAction() |
635 | { | 19 | { |
636 | $version = $this->container->getParameter('wallabag_core.version'); | 20 | $version = $this->container->getParameter('wallabag_core.version'); |
637 | |||
638 | $json = $this->get('serializer')->serialize($version, 'json'); | 21 | $json = $this->get('serializer')->serialize($version, 'json'); |
639 | |||
640 | return (new JsonResponse())->setJson($json); | 22 | return (new JsonResponse())->setJson($json); |
641 | } | 23 | } |
642 | 24 | ||
643 | /** | 25 | protected function validateAuthentication() |
644 | * Remove orphan tag in case no entries are associated to it. | ||
645 | * | ||
646 | * @param Tag|array $tags | ||
647 | */ | ||
648 | private function cleanOrphanTag($tags) | ||
649 | { | 26 | { |
650 | if (!is_array($tags)) { | 27 | if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { |
651 | $tags = [$tags]; | 28 | throw new AccessDeniedException(); |
652 | } | ||
653 | |||
654 | $em = $this->getDoctrine()->getManager(); | ||
655 | |||
656 | foreach ($tags as $tag) { | ||
657 | if (count($tag->getEntries()) === 0) { | ||
658 | $em->remove($tag); | ||
659 | } | ||
660 | } | 29 | } |
661 | |||
662 | $em->flush(); | ||
663 | } | 30 | } |
664 | 31 | ||
665 | /** | 32 | /** |
@@ -668,7 +35,7 @@ class WallabagRestController extends FOSRestController | |||
668 | * | 35 | * |
669 | * @param int $requestUserId User id from the requested source | 36 | * @param int $requestUserId User id from the requested source |
670 | */ | 37 | */ |
671 | private function validateUserAccess($requestUserId) | 38 | protected function validateUserAccess($requestUserId) |
672 | { | 39 | { |
673 | $user = $this->get('security.token_storage')->getToken()->getUser(); | 40 | $user = $this->get('security.token_storage')->getToken()->getUser(); |
674 | if ($requestUserId != $user->getId()) { | 41 | if ($requestUserId != $user->getId()) { |
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index 35f8b2c1..8e1886ac 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml | |||
@@ -1,4 +1,14 @@ | |||
1 | api: | 1 | entry: |
2 | type: rest | 2 | type: rest |
3 | resource: "WallabagApiBundle:WallabagRest" | 3 | resource: "WallabagApiBundle:EntryRest" |
4 | name_prefix: api_ | 4 | name_prefix: api_ |
5 | |||
6 | tag: | ||
7 | type: rest | ||
8 | resource: "WallabagApiBundle:TagRest" | ||
9 | name_prefix: api_ | ||
10 | |||
11 | misc: | ||
12 | type: rest | ||
13 | resource: "WallabagApiBundle:WallabagRest" | ||
14 | name_prefix: api_ | ||