aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php211
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php16
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php182
-rw-r--r--tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php63
-rw-r--r--tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php42
5 files changed, 347 insertions, 167 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index ca460c84..8a206124 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -299,8 +299,8 @@ class EntryRestController extends WallabagRestController
299 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, 299 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
300 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, 300 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
301 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, 301 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
302 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
303 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, 302 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
303 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
304 * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, 304 * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"},
305 * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, 305 * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"},
306 * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, 306 * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"},
@@ -328,7 +328,58 @@ class EntryRestController extends WallabagRestController
328 $entry->setUrl($url); 328 $entry->setUrl($url);
329 } 329 }
330 330
331 $this->upsertEntry($entry, $request); 331 $data = $this->retrieveValueFromRequest($request);
332
333 try {
334 $this->get('wallabag_core.content_proxy')->updateEntry(
335 $entry,
336 $entry->getUrl(),
337 [
338 'title' => !empty($data['title']) ? $data['title'] : $entry->getTitle(),
339 'html' => !empty($data['content']) ? $data['content'] : $entry->getContent(),
340 'url' => $entry->getUrl(),
341 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(),
342 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(),
343 // faking the open graph preview picture
344 'open_graph' => [
345 'og_image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(),
346 ],
347 'authors' => is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(),
348 ]
349 );
350 } catch (\Exception $e) {
351 $this->get('logger')->error('Error while saving an entry', [
352 'exception' => $e,
353 'entry' => $entry,
354 ]);
355 }
356
357 if (null !== $data['isArchived']) {
358 $entry->setArchived((bool) $data['isArchived']);
359 }
360
361 if (null !== $data['isStarred']) {
362 $entry->setStarred((bool) $data['isStarred']);
363 }
364
365 if (!empty($data['tags'])) {
366 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']);
367 }
368
369 if (null !== $data['isPublic']) {
370 if (true === (bool) $data['isPublic'] && null === $entry->getUid()) {
371 $entry->generateUid();
372 } elseif (false === (bool) $data['isPublic']) {
373 $entry->cleanUid();
374 }
375 }
376
377 $em = $this->getDoctrine()->getManager();
378 $em->persist($entry);
379 $em->flush();
380
381 // entry saved, dispatch event about it!
382 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
332 383
333 return $this->sendResponse($entry); 384 return $this->sendResponse($entry);
334 } 385 }
@@ -361,7 +412,78 @@ class EntryRestController extends WallabagRestController
361 $this->validateAuthentication(); 412 $this->validateAuthentication();
362 $this->validateUserAccess($entry->getUser()->getId()); 413 $this->validateUserAccess($entry->getUser()->getId());
363 414
364 $this->upsertEntry($entry, $request, true); 415 $contentProxy = $this->get('wallabag_core.content_proxy');
416
417 $data = $this->retrieveValueFromRequest($request);
418
419 // this is a special case where user want to manually update the entry content
420 // the ContentProxy will only cleanup the html
421 // and also we force to not re-fetch the content in case of error
422 if (!empty($data['content'])) {
423 try {
424 $contentProxy->updateEntry(
425 $entry,
426 $entry->getUrl(),
427 [
428 'html' => $data['content'],
429 ],
430 true
431 );
432 } catch (\Exception $e) {
433 $this->get('logger')->error('Error while saving an entry', [
434 'exception' => $e,
435 'entry' => $entry,
436 ]);
437 }
438 }
439
440 if (!empty($data['title'])) {
441 $entry->setTitle($data['title']);
442 }
443
444 if (!empty($data['language'])) {
445 $contentProxy->updateLanguage($entry, $data['language']);
446 }
447
448 if (!empty($data['authors']) && is_string($data['authors'])) {
449 $entry->setPublishedBy(explode(',', $data['authors']));
450 }
451
452 if (!empty($data['picture'])) {
453 $contentProxy->updatePreviewPicture($entry, $data['picture']);
454 }
455
456 if (!empty($data['publishedAt'])) {
457 $contentProxy->updatePublishedAt($entry, $data['publishedAt']);
458 }
459
460 if (null !== $data['isArchived']) {
461 $entry->setArchived((bool) $data['isArchived']);
462 }
463
464 if (null !== $data['isStarred']) {
465 $entry->setStarred((bool) $data['isStarred']);
466 }
467
468 if (!empty($data['tags'])) {
469 $entry->removeAllTags();
470 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']);
471 }
472
473 if (null !== $data['isPublic']) {
474 if (true === (bool) $data['isPublic'] && null === $entry->getUid()) {
475 $entry->generateUid();
476 } elseif (false === (bool) $data['isPublic']) {
477 $entry->cleanUid();
478 }
479 }
480
481 $em = $this->getDoctrine()->getManager();
482 $em->persist($entry);
483 $em->flush();
484
485 // entry saved, dispatch event about it!
486 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
365 487
366 return $this->sendResponse($entry); 488 return $this->sendResponse($entry);
367 } 489 }
@@ -634,76 +756,27 @@ class EntryRestController extends WallabagRestController
634 } 756 }
635 757
636 /** 758 /**
637 * Update or Insert a new entry. 759 * Retrieve value from the request.
760 * Used for POST & PATCH on a an entry.
638 * 761 *
639 * @param Entry $entry
640 * @param Request $request 762 * @param Request $request
641 * @param bool $disableContentUpdate If we don't want the content to be update by fetching the url (used when patching instead of posting) 763 *
764 * @return array
642 */ 765 */
643 private function upsertEntry(Entry $entry, Request $request, $disableContentUpdate = false) 766 private function retrieveValueFromRequest(Request $request)
644 { 767 {
645 $title = $request->request->get('title'); 768 return [
646 $tags = $request->request->get('tags', []); 769 'title' => $request->request->get('title'),
647 $isArchived = $request->request->get('archive'); 770 'tags' => $request->request->get('tags', []),
648 $isStarred = $request->request->get('starred'); 771 'isArchived' => $request->request->get('archive'),
649 $isPublic = $request->request->get('public'); 772 'isStarred' => $request->request->get('starred'),
650 $content = $request->request->get('content'); 773 'isPublic' => $request->request->get('public'),
651 $language = $request->request->get('language'); 774 'content' => $request->request->get('content'),
652 $picture = $request->request->get('preview_picture'); 775 'language' => $request->request->get('language'),
653 $publishedAt = $request->request->get('published_at'); 776 'picture' => $request->request->get('preview_picture'),
654 $authors = $request->request->get('authors', ''); 777 'publishedAt' => $request->request->get('published_at'),
655 778 'authors' => $request->request->get('authors', ''),
656 try { 779 ];
657 $this->get('wallabag_core.content_proxy')->updateEntry(
658 $entry,
659 $entry->getUrl(),
660 [
661 'title' => !empty($title) ? $title : $entry->getTitle(),
662 'html' => !empty($content) ? $content : $entry->getContent(),
663 'url' => $entry->getUrl(),
664 'language' => !empty($language) ? $language : $entry->getLanguage(),
665 'date' => !empty($publishedAt) ? $publishedAt : $entry->getPublishedAt(),
666 // faking the open graph preview picture
667 'open_graph' => [
668 'og_image' => !empty($picture) ? $picture : $entry->getPreviewPicture(),
669 ],
670 'authors' => is_string($authors) ? explode(',', $authors) : $entry->getPublishedBy(),
671 ],
672 $disableContentUpdate
673 );
674 } catch (\Exception $e) {
675 $this->get('logger')->error('Error while saving an entry', [
676 'exception' => $e,
677 'entry' => $entry,
678 ]);
679 }
680
681 if (null !== $isArchived) {
682 $entry->setArchived((bool) $isArchived);
683 }
684
685 if (null !== $isStarred) {
686 $entry->setStarred((bool) $isStarred);
687 }
688
689 if (!empty($tags)) {
690 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags);
691 }
692
693 if (null !== $isPublic) {
694 if (true === (bool) $isPublic && null === $entry->getUid()) {
695 $entry->generateUid();
696 } elseif (false === (bool) $isPublic) {
697 $entry->cleanUid();
698 }
699 }
700
701 $em = $this->getDoctrine()->getManager();
702 $em->persist($entry);
703 $em->flush();
704
705 // entry saved, dispatch event about it!
706 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
707 } 780 }
708 781
709 /** 782 /**
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index 581e8906..cba72d31 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -593,6 +593,11 @@ class Entry
593 $tag->addEntry($this); 593 $tag->addEntry($this);
594 } 594 }
595 595
596 /**
597 * Remove the given tag from the entry (if the tag is associated).
598 *
599 * @param Tag $tag
600 */
596 public function removeTag(Tag $tag) 601 public function removeTag(Tag $tag)
597 { 602 {
598 if (!$this->tags->contains($tag)) { 603 if (!$this->tags->contains($tag)) {
@@ -604,6 +609,17 @@ class Entry
604 } 609 }
605 610
606 /** 611 /**
612 * Remove all assigned tags from the entry.
613 */
614 public function removeAllTags()
615 {
616 foreach ($this->tags as $tag) {
617 $this->tags->removeElement($tag);
618 $tag->removeEntry($this);
619 }
620 }
621
622 /**
607 * Set previewPicture. 623 * Set previewPicture.
608 * 624 *
609 * @param string $previewPicture 625 * @param string $previewPicture
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index ddecd6f4..656ac6ee 100644
--- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php
+++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
@@ -67,6 +67,76 @@ class ContentProxy
67 } 67 }
68 68
69 /** 69 /**
70 * Use a Symfony validator to ensure the language is well formatted.
71 *
72 * @param Entry $entry
73 * @param string $value Language to validate and save
74 */
75 public function updateLanguage(Entry $entry, $value)
76 {
77 // some lang are defined as fr-FR, es-ES.
78 // replacing - by _ might increase language support
79 $value = str_replace('-', '_', $value);
80
81 $errors = $this->validator->validate(
82 $value,
83 (new LocaleConstraint())
84 );
85
86 if (0 === count($errors)) {
87 $entry->setLanguage($value);
88
89 return;
90 }
91
92 $this->logger->warning('Language validation failed. ' . (string) $errors);
93 }
94
95 /**
96 * Use a Symfony validator to ensure the preview picture is a real url.
97 *
98 * @param Entry $entry
99 * @param string $value URL to validate and save
100 */
101 public function updatePreviewPicture(Entry $entry, $value)
102 {
103 $errors = $this->validator->validate(
104 $value,
105 (new UrlConstraint())
106 );
107
108 if (0 === count($errors)) {
109 $entry->setPreviewPicture($value);
110
111 return;
112 }
113
114 $this->logger->warning('PreviewPicture validation failed. ' . (string) $errors);
115 }
116
117 /**
118 * Update date.
119 *
120 * @param Entry $entry
121 * @param string $value Date to validate and save
122 */
123 public function updatePublishedAt(Entry $entry, $value)
124 {
125 $date = $value;
126
127 // is it a timestamp?
128 if (filter_var($date, FILTER_VALIDATE_INT) !== false) {
129 $date = '@' . $value;
130 }
131
132 try {
133 $entry->setPublishedAt(new \DateTime($date));
134 } catch (\Exception $e) {
135 $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $entry->getUrl(), 'date' => $value]);
136 }
137 }
138
139 /**
70 * Stock entry with fetched or imported content. 140 * Stock entry with fetched or imported content.
71 * Will fall back to OpenGraph data if available. 141 * Will fall back to OpenGraph data if available.
72 * 142 *
@@ -75,9 +145,17 @@ class ContentProxy
75 */ 145 */
76 private function stockEntry(Entry $entry, array $content) 146 private function stockEntry(Entry $entry, array $content)
77 { 147 {
78 $title = $content['title']; 148 $entry->setUrl($content['url']);
79 if (!$title && !empty($content['open_graph']['og_title'])) { 149
80 $title = $content['open_graph']['og_title']; 150 $domainName = parse_url($entry->getUrl(), PHP_URL_HOST);
151 if (false !== $domainName) {
152 $entry->setDomainName($domainName);
153 }
154
155 if (!empty($content['title'])) {
156 $entry->setTitle($content['title']);
157 } elseif (!empty($content['open_graph']['og_title'])) {
158 $entry->setTitle($content['open_graph']['og_title']);
81 } 159 }
82 160
83 $html = $content['html']; 161 $html = $content['html'];
@@ -90,24 +168,11 @@ class ContentProxy
90 } 168 }
91 } 169 }
92 170
93 $entry->setUrl($content['url']);
94 $entry->setTitle($title);
95 $entry->setContent($html); 171 $entry->setContent($html);
96 $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); 172 $entry->setReadingTime(Utils::getReadingTime($html));
97
98 if (!empty($content['date'])) {
99 $date = $content['date'];
100
101 // is it a timestamp?
102 if (filter_var($date, FILTER_VALIDATE_INT) !== false) {
103 $date = '@' . $content['date'];
104 }
105 173
106 try { 174 if (!empty($content['status'])) {
107 $entry->setPublishedAt(new \DateTime($date)); 175 $entry->setHttpStatus($content['status']);
108 } catch (\Exception $e) {
109 $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $content['url'], 'date' => $content['date']]);
110 }
111 } 176 }
112 177
113 if (!empty($content['authors']) && is_array($content['authors'])) { 178 if (!empty($content['authors']) && is_array($content['authors'])) {
@@ -118,30 +183,25 @@ class ContentProxy
118 $entry->setHeaders($content['all_headers']); 183 $entry->setHeaders($content['all_headers']);
119 } 184 }
120 185
121 $this->validateAndSetLanguage( 186 if (!empty($content['date'])) {
122 $entry, 187 $this->updatePublishedAt($entry, $content['date']);
123 isset($content['language']) ? $content['language'] : null 188 }
124 );
125 189
126 $this->validateAndSetPreviewPicture( 190 if (!empty($content['language'])) {
127 $entry, 191 $this->updateLanguage($entry, $content['language']);
128 isset($content['open_graph']['og_image']) ? $content['open_graph']['og_image'] : null 192 }
129 ); 193
194 if (!empty($content['open_graph']['og_image'])) {
195 $this->updatePreviewPicture($entry, $content['open_graph']['og_image']);
196 }
130 197
131 // if content is an image, define it as a preview too 198 // if content is an image, define it as a preview too
132 if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { 199 if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
133 $this->validateAndSetPreviewPicture( 200 $this->updatePreviewPicture($entry, $content['url']);
134 $entry,
135 $content['url']
136 );
137 } 201 }
138 202
139 $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : ''); 203 if (!empty($content['content_type'])) {
140 $entry->setReadingTime(Utils::getReadingTime($html)); 204 $entry->setMimetype($content['content_type']);
141
142 $domainName = parse_url($entry->getUrl(), PHP_URL_HOST);
143 if (false !== $domainName) {
144 $entry->setDomainName($domainName);
145 } 205 }
146 206
147 try { 207 try {
@@ -165,52 +225,4 @@ class ContentProxy
165 { 225 {
166 return !empty($content['title']) && !empty($content['html']) && !empty($content['url']); 226 return !empty($content['title']) && !empty($content['html']) && !empty($content['url']);
167 } 227 }
168
169 /**
170 * Use a Symfony validator to ensure the language is well formatted.
171 *
172 * @param Entry $entry
173 * @param string $value Language to validate
174 */
175 private function validateAndSetLanguage($entry, $value)
176 {
177 // some lang are defined as fr-FR, es-ES.
178 // replacing - by _ might increase language support
179 $value = str_replace('-', '_', $value);
180
181 $errors = $this->validator->validate(
182 $value,
183 (new LocaleConstraint())
184 );
185
186 if (0 === count($errors)) {
187 $entry->setLanguage($value);
188
189 return;
190 }
191
192 $this->logger->warning('Language validation failed. ' . (string) $errors);
193 }
194
195 /**
196 * Use a Symfony validator to ensure the preview picture is a real url.
197 *
198 * @param Entry $entry
199 * @param string $value URL to validate
200 */
201 private function validateAndSetPreviewPicture($entry, $value)
202 {
203 $errors = $this->validator->validate(
204 $value,
205 (new UrlConstraint())
206 );
207
208 if (0 === count($errors)) {
209 $entry->setPreviewPicture($value);
210
211 return;
212 }
213
214 $this->logger->warning('PreviewPicture validation failed. ' . (string) $errors);
215 }
216} 228}
diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
index ae4af4cd..c76be13d 100644
--- a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
+++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
@@ -519,9 +519,6 @@ class EntryRestControllerTest extends WallabagApiTestCase
519 $this->markTestSkipped('No content found in db.'); 519 $this->markTestSkipped('No content found in db.');
520 } 520 }
521 521
522 // hydrate the tags relations
523 $nbTags = count($entry->getTags());
524
525 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [ 522 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [
526 'title' => 'New awesome title', 523 'title' => 'New awesome title',
527 'tags' => 'new tag ' . uniqid(), 524 'tags' => 'new tag ' . uniqid(),
@@ -532,6 +529,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
532 'authors' => 'bob,sponge', 529 'authors' => 'bob,sponge',
533 'content' => 'awesome', 530 'content' => 'awesome',
534 'public' => 0, 531 'public' => 0,
532 'published_at' => 1488833381,
535 ]); 533 ]);
536 534
537 $this->assertSame(200, $this->client->getResponse()->getStatusCode()); 535 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
@@ -541,7 +539,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
541 $this->assertSame($entry->getId(), $content['id']); 539 $this->assertSame($entry->getId(), $content['id']);
542 $this->assertSame($entry->getUrl(), $content['url']); 540 $this->assertSame($entry->getUrl(), $content['url']);
543 $this->assertSame('New awesome title', $content['title']); 541 $this->assertSame('New awesome title', $content['title']);
544 $this->assertGreaterThan($nbTags, count($content['tags'])); 542 $this->assertGreaterThanOrEqual(1, count($content['tags']), 'We force only one tag');
545 $this->assertSame(1, $content['user_id']); 543 $this->assertSame(1, $content['user_id']);
546 $this->assertSame('de_AT', $content['language']); 544 $this->assertSame('de_AT', $content['language']);
547 $this->assertSame('http://preview.io/picture.jpg', $content['preview_picture']); 545 $this->assertSame('http://preview.io/picture.jpg', $content['preview_picture']);
@@ -549,6 +547,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
549 $this->assertContains('bob', $content['published_by']); 547 $this->assertContains('bob', $content['published_by']);
550 $this->assertSame('awesome', $content['content']); 548 $this->assertSame('awesome', $content['content']);
551 $this->assertFalse($content['is_public'], 'Entry is no more shared'); 549 $this->assertFalse($content['is_public'], 'Entry is no more shared');
550 $this->assertContains('2017-03-06', $content['published_at']);
552 } 551 }
553 552
554 public function testPatchEntryWithoutQuotes() 553 public function testPatchEntryWithoutQuotes()
@@ -562,8 +561,8 @@ class EntryRestControllerTest extends WallabagApiTestCase
562 $this->markTestSkipped('No content found in db.'); 561 $this->markTestSkipped('No content found in db.');
563 } 562 }
564 563
565 // hydrate the tags relations 564 $previousContent = $entry->getContent();
566 $nbTags = count($entry->getTags()); 565 $previousLanguage = $entry->getLanguage();
567 566
568 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [ 567 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [
569 'title' => 'New awesome title', 568 'title' => 'New awesome title',
@@ -579,9 +578,10 @@ class EntryRestControllerTest extends WallabagApiTestCase
579 578
580 $this->assertSame($entry->getId(), $content['id']); 579 $this->assertSame($entry->getId(), $content['id']);
581 $this->assertSame($entry->getUrl(), $content['url']); 580 $this->assertSame($entry->getUrl(), $content['url']);
582 $this->assertSame('New awesome title', $content['title']); 581 $this->assertGreaterThanOrEqual(1, count($content['tags']), 'We force only one tag');
583 $this->assertGreaterThan($nbTags, count($content['tags']));
584 $this->assertEmpty($content['published_by'], 'Authors were not saved because of an array instead of a string'); 582 $this->assertEmpty($content['published_by'], 'Authors were not saved because of an array instead of a string');
583 $this->assertSame($previousContent, $content['content'], 'Ensure content has not moved');
584 $this->assertSame($previousLanguage, $content['language'], 'Ensure language has not moved');
585 } 585 }
586 586
587 public function testGetTagsEntry() 587 public function testGetTagsEntry()
@@ -727,6 +727,8 @@ class EntryRestControllerTest extends WallabagApiTestCase
727 $this->markTestSkipped('No content found in db.'); 727 $this->markTestSkipped('No content found in db.');
728 } 728 }
729 729
730 $previousTitle = $entry->getTitle();
731
730 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [ 732 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [
731 'title' => $entry->getTitle() . '++', 733 'title' => $entry->getTitle() . '++',
732 ]); 734 ]);
@@ -736,6 +738,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
736 $content = json_decode($this->client->getResponse()->getContent(), true); 738 $content = json_decode($this->client->getResponse()->getContent(), true);
737 739
738 $this->assertSame(1, $content['is_archived']); 740 $this->assertSame(1, $content['is_archived']);
741 $this->assertSame($previousTitle . '++', $content['title']);
739 } 742 }
740 743
741 public function testSaveIsStarredAfterPatch() 744 public function testSaveIsStarredAfterPatch()
@@ -907,6 +910,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
907 $this->assertCount(4, $tags); 910 $this->assertCount(4, $tags);
908 } 911 }
909 912
913 public function testPostEntriesTagsListActionNoList()
914 {
915 $this->client->request('POST', '/api/entries/tags/lists?list=' . json_encode([]));
916
917 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
918
919 $content = json_decode($this->client->getResponse()->getContent(), true);
920
921 $this->assertEmpty($content);
922 }
923
910 public function testDeleteEntriesTagsListAction() 924 public function testDeleteEntriesTagsListAction()
911 { 925 {
912 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); 926 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
@@ -933,6 +947,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
933 $this->assertCount(0, $entry->getTags()); 947 $this->assertCount(0, $entry->getTags());
934 } 948 }
935 949
950 public function testDeleteEntriesTagsListActionNoList()
951 {
952 $this->client->request('DELETE', '/api/entries/tags/list?list=' . json_encode([]));
953
954 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
955
956 $content = json_decode($this->client->getResponse()->getContent(), true);
957
958 $this->assertEmpty($content);
959 }
960
936 public function testPostEntriesListAction() 961 public function testPostEntriesListAction()
937 { 962 {
938 $list = [ 963 $list = [
@@ -953,6 +978,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
953 $this->assertSame('http://0.0.0.0/entry2', $content[1]['url']); 978 $this->assertSame('http://0.0.0.0/entry2', $content[1]['url']);
954 } 979 }
955 980
981 public function testPostEntriesListActionWithNoUrls()
982 {
983 $this->client->request('POST', '/api/entries/lists?urls=' . json_encode([]));
984
985 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
986
987 $content = json_decode($this->client->getResponse()->getContent(), true);
988
989 $this->assertEmpty($content);
990 }
991
956 public function testDeleteEntriesListAction() 992 public function testDeleteEntriesListAction()
957 { 993 {
958 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); 994 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
@@ -978,6 +1014,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
978 $this->assertSame('http://0.0.0.0/test-entry-not-exist', $content[1]['url']); 1014 $this->assertSame('http://0.0.0.0/test-entry-not-exist', $content[1]['url']);
979 } 1015 }
980 1016
1017 public function testDeleteEntriesListActionWithNoUrls()
1018 {
1019 $this->client->request('DELETE', '/api/entries/list?urls=' . json_encode([]));
1020
1021 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
1022
1023 $content = json_decode($this->client->getResponse()->getContent(), true);
1024
1025 $this->assertEmpty($content);
1026 }
1027
981 public function testLimitBulkAction() 1028 public function testLimitBulkAction()
982 { 1029 {
983 $list = [ 1030 $list = [
diff --git a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
index c63671c4..f94c2137 100644
--- a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
+++ b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
@@ -221,12 +221,9 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
221 ->method('tag'); 221 ->method('tag');
222 222
223 $validator = $this->getValidator(); 223 $validator = $this->getValidator();
224 $validator->expects($this->exactly(2)) 224 $validator->expects($this->once())
225 ->method('validate') 225 ->method('validate')
226 ->will($this->onConsecutiveCalls( 226 ->willReturn(new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]));
227 new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]),
228 new ConstraintViolationList()
229 ));
230 227
231 $graby = $this->getMockBuilder('Graby\Graby') 228 $graby = $this->getMockBuilder('Graby\Graby')
232 ->setMethods(['fetchContent']) 229 ->setMethods(['fetchContent'])
@@ -498,6 +495,41 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
498 $this->assertSame('1.1.1.1', $entry->getDomainName()); 495 $this->assertSame('1.1.1.1', $entry->getDomainName());
499 } 496 }
500 497
498 public function testWithImageAsContent()
499 {
500 $tagger = $this->getTaggerMock();
501 $tagger->expects($this->once())
502 ->method('tag');
503
504 $graby = $this->getMockBuilder('Graby\Graby')
505 ->setMethods(['fetchContent'])
506 ->disableOriginalConstructor()
507 ->getMock();
508
509 $graby->expects($this->any())
510 ->method('fetchContent')
511 ->willReturn([
512 'html' => '<p><img src="http://1.1.1.1/image.jpg" /></p>',
513 'title' => 'this is my title',
514 'url' => 'http://1.1.1.1/image.jpg',
515 'content_type' => 'image/jpeg',
516 'status' => '200',
517 'open_graph' => [],
518 ]);
519
520 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
521 $entry = new Entry(new User());
522 $proxy->updateEntry($entry, 'http://0.0.0.0');
523
524 $this->assertSame('http://1.1.1.1/image.jpg', $entry->getUrl());
525 $this->assertSame('this is my title', $entry->getTitle());
526 $this->assertContains('http://1.1.1.1/image.jpg', $entry->getContent());
527 $this->assertSame('http://1.1.1.1/image.jpg', $entry->getPreviewPicture());
528 $this->assertSame('image/jpeg', $entry->getMimetype());
529 $this->assertSame('200', $entry->getHttpStatus());
530 $this->assertSame('1.1.1.1', $entry->getDomainName());
531 }
532
501 private function getTaggerMock() 533 private function getTaggerMock()
502 { 534 {
503 return $this->getMockBuilder(RuleBasedTagger::class) 535 return $this->getMockBuilder(RuleBasedTagger::class)