2016-10-28 12:46:30 +00:00
< ? php
namespace Wallabag\ApiBundle\Controller ;
use Hateoas\Configuration\Route ;
use Hateoas\Representation\Factory\PagerfantaFactory ;
use Nelmio\ApiDocBundle\Annotation\ApiDoc ;
use Symfony\Component\HttpFoundation\JsonResponse ;
2017-07-01 07:52:38 +00:00
use Symfony\Component\HttpFoundation\Request ;
2017-07-29 20:51:50 +00:00
use Symfony\Component\HttpFoundation\Response ;
2019-01-14 16:01:21 +00:00
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException ;
2017-07-01 07:52:38 +00:00
use Symfony\Component\HttpKernel\Exception\HttpException ;
2016-10-28 12:46:30 +00:00
use Wallabag\CoreBundle\Entity\Entry ;
use Wallabag\CoreBundle\Entity\Tag ;
2016-11-03 15:41:29 +00:00
use Wallabag\CoreBundle\Event\EntryDeletedEvent ;
2017-07-01 07:52:38 +00:00
use Wallabag\CoreBundle\Event\EntrySavedEvent ;
2019-05-10 12:07:55 +00:00
use Wallabag\CoreBundle\Helper\UrlHasher ;
2016-10-28 12:46:30 +00:00
class EntryRestController extends WallabagRestController
{
/**
* Check if an entry exist by url .
2017-06-23 06:30:23 +00:00
* Return ID if entry ( ies ) exist ( and if you give the return_id parameter ) .
* Otherwise it returns false .
2016-10-28 12:46:30 +00:00
*
2017-06-28 06:15:06 +00:00
* @ todo Remove that `return_id` in the next major release
*
2016-10-28 12:46:30 +00:00
* @ ApiDoc (
* parameters = {
2017-06-23 06:30:23 +00:00
* { " 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 " },
2019-04-01 09:50:33 +00:00
* { " name " = " url " , " dataType " = " string " , " required " = true , " format " = " An url " , " description " = " DEPRECATED, use hashed_url instead " },
* { " name " = " urls " , " dataType " = " string " , " required " = false , " format " = " An array of urls (?urls[]=http...&urls[]=http...) " , " description " = " DEPRECATED, use hashed_urls instead " },
2019-04-02 20:59:50 +00:00
* { " name " = " hashed_url " , " dataType " = " string " , " required " = false , " format " = " A hashed url " , " description " = " Hashed url using SHA1 to check if it exists " },
* { " 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 " }
2016-10-28 12:46:30 +00:00
* }
* )
*
* @ return JsonResponse
*/
public function getEntriesExistsAction ( Request $request )
{
$this -> validateAuthentication ();
2019-04-01 09:50:33 +00:00
$repo = $this -> getDoctrine () -> getRepository ( 'WallabagCoreBundle:Entry' );
2016-10-28 12:46:30 +00:00
2017-06-28 07:18:22 +00:00
$returnId = ( null === $request -> query -> get ( 'return_id' )) ? false : ( bool ) $request -> query -> get ( 'return_id' );
2016-10-28 12:46:30 +00:00
2019-04-01 09:50:33 +00:00
$hashedUrls = $request -> query -> get ( 'hashed_urls' , []);
2019-05-02 11:19:20 +00:00
$hashedUrl = $request -> query -> get ( 'hashed_url' , '' );
if ( ! empty ( $hashedUrl )) {
$hashedUrls [] = $hashedUrl ;
}
2017-05-28 12:53:04 +00:00
2019-05-02 11:19:20 +00:00
$urls = $request -> query -> get ( 'urls' , []);
$url = $request -> query -> get ( 'url' , '' );
if ( ! empty ( $url )) {
$urls [] = $url ;
}
2016-10-28 12:46:30 +00:00
2019-05-02 11:19:20 +00:00
$urlHashMap = [];
2019-05-10 12:07:55 +00:00
foreach ( $urls as $urlToHash ) {
$urlHash = UrlHasher :: hashUrl ( $urlToHash );
2019-05-02 11:19:20 +00:00
$hashedUrls [] = $urlHash ;
$urlHashMap [ $urlHash ] = $urlToHash ;
2016-10-28 12:46:30 +00:00
}
2019-05-02 11:19:20 +00:00
if ( empty ( $hashedUrls )) {
throw $this -> createAccessDeniedException ( 'URL is empty?, logged user id: ' . $this -> getUser () -> getId ());
}
2019-04-01 09:50:33 +00:00
2021-04-28 22:19:37 +00:00
$results = array_fill_keys ( $hashedUrls , null );
$res = $repo -> findByUserIdAndBatchHashedUrls ( $this -> getUser () -> getId (), $hashedUrls );
foreach ( $res as $e ) {
$_hashedUrl = array_keys ( $hashedUrls , 'blah' , true );
if ([] !== array_keys ( $hashedUrls , $e [ 'hashedUrl' ], true )) {
$_hashedUrl = $e [ 'hashedUrl' ];
} elseif ([] !== array_keys ( $hashedUrls , $e [ 'hashedGivenUrl' ], true )) {
$_hashedUrl = $e [ 'hashedGivenUrl' ];
} else {
continue ;
}
$results [ $_hashedUrl ] = $e [ 'id' ];
}
2019-04-01 09:50:33 +00:00
2021-04-28 22:19:37 +00:00
if ( false === $returnId ) {
$results = array_map ( function ( $v ) {
return null !== $v ;
}, $results );
2019-04-01 09:50:33 +00:00
}
2019-05-02 11:19:20 +00:00
$results = $this -> replaceUrlHashes ( $results , $urlHashMap );
2017-05-28 12:53:04 +00:00
2019-05-02 11:19:20 +00:00
if ( ! empty ( $url ) || ! empty ( $hashedUrl )) {
$hu = array_keys ( $results )[ 0 ];
2016-10-28 12:46:30 +00:00
2019-05-02 11:19:20 +00:00
return $this -> sendResponse ([ 'exists' => $results [ $hu ]]);
2016-10-28 12:46:30 +00:00
}
2019-05-10 12:07:55 +00:00
return $this -> sendResponse ( $results );
2016-10-28 12:46:30 +00:00
}
/**
* Retrieve all entries . It could be filtered by many options .
*
* @ ApiDoc (
* parameters = {
* { " name " = " archive " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0, all entries by default " , " description " = " filter by archived status. " },
* { " name " = " starred " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0, all entries by default " , " description " = " filter by starred status. " },
2018-04-26 05:20:52 +00:00
* { " name " = " sort " , " dataType " = " string " , " required " = false , " format " = " 'created' or 'updated' or 'archived', default 'created' " , " description " = " sort entries by date. " },
2016-10-28 12:46:30 +00:00
* { " name " = " order " , " dataType " = " string " , " required " = false , " format " = " 'asc' or 'desc', default 'desc' " , " description " = " order of sort. " },
* { " name " = " page " , " dataType " = " integer " , " required " = false , " format " = " default '1' " , " description " = " what page you want. " },
* { " name " = " perPage " , " dataType " = " integer " , " required " = false , " format " = " default'30' " , " description " = " results per page. " },
* { " name " = " tags " , " dataType " = " string " , " required " = false , " format " = " api,rest " , " description " = " a list of tags url encoded. Will returns entries that matches ALL tags. " },
* { " name " = " since " , " dataType " = " integer " , " required " = false , " format " = " default '0' " , " description " = " The timestamp since when you want entries updated. " },
2017-06-10 13:31:57 +00:00
* { " name " = " public " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0, all entries by default " , " description " = " filter by entries with a public link " },
2019-05-11 22:00:00 +00:00
* { " 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. " },
2016-10-28 12:46:30 +00:00
* }
* )
*
* @ return JsonResponse
*/
public function getEntriesAction ( Request $request )
{
$this -> validateAuthentication ();
$isArchived = ( null === $request -> query -> get ( 'archive' )) ? null : ( bool ) $request -> query -> get ( 'archive' );
$isStarred = ( null === $request -> query -> get ( 'starred' )) ? null : ( bool ) $request -> query -> get ( 'starred' );
2017-06-10 13:31:57 +00:00
$isPublic = ( null === $request -> query -> get ( 'public' )) ? null : ( bool ) $request -> query -> get ( 'public' );
2019-01-14 16:01:21 +00:00
$sort = strtolower ( $request -> query -> get ( 'sort' , 'created' ));
$order = strtolower ( $request -> query -> get ( 'order' , 'desc' ));
2016-10-28 12:46:30 +00:00
$page = ( int ) $request -> query -> get ( 'page' , 1 );
$perPage = ( int ) $request -> query -> get ( 'perPage' , 30 );
2018-09-05 12:25:32 +00:00
$tags = \is_array ( $request -> query -> get ( 'tags' )) ? '' : ( string ) $request -> query -> get ( 'tags' , '' );
2016-10-28 12:46:30 +00:00
$since = $request -> query -> get ( 'since' , 0 );
2019-05-11 22:00:00 +00:00
$detail = strtolower ( $request -> query -> get ( 'detail' , 'full' ));
2016-10-28 12:46:30 +00:00
2019-01-14 16:01:21 +00:00
try {
/** @var \Pagerfanta\Pagerfanta $pager */
$pager = $this -> get ( 'wallabag_core.entry_repository' ) -> findEntries (
$this -> getUser () -> getId (),
$isArchived ,
$isStarred ,
$isPublic ,
$sort ,
$order ,
$since ,
2019-05-11 22:00:00 +00:00
$tags ,
$detail
2019-01-14 16:01:21 +00:00
);
} catch ( \Exception $e ) {
throw new BadRequestHttpException ( $e -> getMessage ());
}
2016-10-28 12:46:30 +00:00
$pager -> setMaxPerPage ( $perPage );
2017-05-08 14:27:16 +00:00
$pager -> setCurrentPage ( $page );
2016-10-28 12:46:30 +00:00
$pagerfantaFactory = new PagerfantaFactory ( 'page' , 'perPage' );
$paginatedCollection = $pagerfantaFactory -> createRepresentation (
$pager ,
new Route (
'api_get_entries' ,
[
'archive' => $isArchived ,
'starred' => $isStarred ,
2017-06-10 13:31:57 +00:00
'public' => $isPublic ,
2016-10-28 12:46:30 +00:00
'sort' => $sort ,
'order' => $order ,
'page' => $page ,
'perPage' => $perPage ,
'tags' => $tags ,
'since' => $since ,
2019-05-11 22:00:00 +00:00
'detail' => $detail ,
2016-10-28 12:46:30 +00:00
],
2018-10-24 18:29:33 +00:00
true
2016-10-28 12:46:30 +00:00
)
);
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $paginatedCollection );
2016-10-28 12:46:30 +00:00
}
/**
* Retrieve a single entry .
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* }
* )
*
* @ return JsonResponse
*/
public function getEntryAction ( Entry $entry )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry );
2016-10-28 12:46:30 +00:00
}
2016-11-03 16:29:16 +00:00
/**
* Retrieve a single entry as a predefined format .
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* }
* )
*
* @ return Response
*/
public function getEntryExportAction ( Entry $entry , Request $request )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
return $this -> get ( 'wallabag_core.helper.entries_export' )
-> setEntries ( $entry )
-> updateTitle ( 'entry' )
2017-07-08 15:55:58 +00:00
-> updateAuthor ( 'entry' )
2016-11-03 16:29:16 +00:00
-> exportAs ( $request -> attributes -> get ( '_format' ));
}
2017-04-24 08:22:57 +00:00
/**
2017-04-24 09:31:00 +00:00
* Handles an entries list and delete URL .
2017-04-24 08:22:57 +00:00
*
* @ ApiDoc (
* parameters = {
2017-04-24 09:31:00 +00:00
* { " name " = " urls " , " dataType " = " string " , " required " = true , " format " = " A JSON array of urls [ { 'url': 'http://...'}, { 'url': 'http://...'}] " , " description " = " Urls (as an array) to delete. " }
2017-04-24 08:22:57 +00:00
* }
* )
*
* @ return JsonResponse
*/
2017-04-24 09:31:00 +00:00
public function deleteEntriesListAction ( Request $request )
2017-04-24 08:22:57 +00:00
{
$this -> validateAuthentication ();
2017-04-24 09:31:00 +00:00
$urls = json_decode ( $request -> query -> get ( 'urls' , []));
2017-05-05 10:05:50 +00:00
if ( empty ( $urls )) {
return $this -> sendResponse ([]);
}
2017-04-24 08:22:57 +00:00
$results = [];
// handle multiple urls
2017-05-05 10:05:50 +00:00
foreach ( $urls as $key => $url ) {
$entry = $this -> get ( 'wallabag_core.entry_repository' ) -> findByUrlAndUserId (
$url ,
$this -> getUser () -> getId ()
);
2017-04-24 09:31:00 +00:00
2017-05-05 10:05:50 +00:00
$results [ $key ][ 'url' ] = $url ;
2017-04-24 09:31:00 +00:00
2017-05-05 10:05:50 +00:00
if ( false !== $entry ) {
2020-04-06 14:11:05 +00:00
// entry deleted, dispatch event about it!
$this -> get ( 'event_dispatcher' ) -> dispatch ( EntryDeletedEvent :: NAME , new EntryDeletedEvent ( $entry ));
2017-05-05 10:05:50 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> remove ( $entry );
$em -> flush ();
2017-04-24 09:31:00 +00:00
}
2017-04-24 08:22:57 +00:00
2017-05-05 10:05:50 +00:00
$results [ $key ][ 'entry' ] = $entry instanceof Entry ? true : false ;
}
2017-04-24 08:22:57 +00:00
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $results );
2017-04-24 09:31:00 +00:00
}
2017-04-24 08:22:57 +00:00
2017-04-24 09:31:00 +00:00
/**
* Handles an entries list and create URL .
*
* @ ApiDoc (
* parameters = {
* { " name " = " urls " , " dataType " = " string " , " required " = true , " format " = " A JSON array of urls [ { 'url': 'http://...'}, { 'url': 'http://...'}] " , " description " = " Urls (as an array) to create. " }
* }
* )
*
2017-05-05 10:05:50 +00:00
* @ throws HttpException When limit is reached
2017-07-01 07:52:38 +00:00
*
* @ return JsonResponse
2017-04-24 09:31:00 +00:00
*/
public function postEntriesListAction ( Request $request )
{
$this -> validateAuthentication ();
2017-04-24 08:22:57 +00:00
2017-04-24 09:31:00 +00:00
$urls = json_decode ( $request -> query -> get ( 'urls' , []));
2017-04-24 08:22:57 +00:00
2017-05-01 07:21:59 +00:00
$limit = $this -> container -> getParameter ( 'wallabag_core.api_limit_mass_actions' );
2018-09-05 12:25:32 +00:00
if ( \count ( $urls ) > $limit ) {
2017-05-05 10:05:50 +00:00
throw new HttpException ( 400 , 'API limit reached' );
2017-05-01 07:21:59 +00:00
}
2017-05-30 15:48:24 +00:00
$results = [];
if ( empty ( $urls )) {
return $this -> sendResponse ( $results );
}
2017-04-24 09:31:00 +00:00
// handle multiple urls
2017-05-30 15:48:24 +00:00
foreach ( $urls as $key => $url ) {
$entry = $this -> get ( 'wallabag_core.entry_repository' ) -> findByUrlAndUserId (
$url ,
$this -> getUser () -> getId ()
);
2017-04-24 08:22:57 +00:00
2017-05-30 15:48:24 +00:00
$results [ $key ][ 'url' ] = $url ;
2017-04-24 08:22:57 +00:00
2017-05-30 15:48:24 +00:00
if ( false === $entry ) {
$entry = new Entry ( $this -> getUser ());
2017-04-24 09:31:00 +00:00
2017-05-30 15:48:24 +00:00
$this -> get ( 'wallabag_core.content_proxy' ) -> updateEntry ( $entry , $url );
}
2017-04-24 09:31:00 +00:00
2017-05-30 15:48:24 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
2017-04-24 09:31:00 +00:00
2017-05-30 15:48:24 +00:00
$results [ $key ][ 'entry' ] = $entry instanceof Entry ? $entry -> getId () : false ;
// entry saved, dispatch event about it!
$this -> get ( 'event_dispatcher' ) -> dispatch ( EntrySavedEvent :: NAME , new EntrySavedEvent ( $entry ));
2017-04-24 08:22:57 +00:00
}
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $results );
2017-04-24 08:22:57 +00:00
}
2016-10-28 12:46:30 +00:00
/**
* Create an entry .
*
2017-05-24 14:02:49 +00:00
* 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 **.
* Otherwise , content will be fetched as normal from the url and values will be overwritten .
*
2016-10-28 12:46:30 +00:00
* @ ApiDoc (
* parameters = {
* { " name " = " url " , " dataType " = " string " , " required " = true , " format " = " http://www.test.com/article.html " , " description " = " Url for the entry. " },
* { " name " = " title " , " dataType " = " string " , " required " = false , " description " = " Optional, we'll get the title from the page. " },
* { " name " = " tags " , " dataType " = " string " , " required " = false , " format " = " tag1,tag2,tag3 " , " description " = " a comma-separated list of tags. " },
* { " name " = " archive " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0 " , " description " = " entry already archived " },
2017-06-30 14:54:26 +00:00
* { " name " = " starred " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0 " , " description " = " entry already starred " },
2017-05-11 06:14:29 +00:00
* { " name " = " content " , " dataType " = " string " , " required " = false , " description " = " Content of the entry " },
* { " name " = " language " , " dataType " = " string " , " required " = false , " description " = " Language of the entry " },
* { " name " = " preview_picture " , " dataType " = " string " , " required " = false , " description " = " Preview picture of the entry " },
2017-05-24 14:44:03 +00:00
* { " name " = " published_at " , " dataType " = " datetime|integer " , " format " = " YYYY-MM-DDTHH:II:SS+TZ or a timestamp " , " required " = false , " description " = " Published date of the entry " },
2017-05-11 18:10:22 +00:00
* { " name " = " authors " , " dataType " = " string " , " format " = " Name Firstname,author2,author3 " , " required " = false , " description " = " Authors of the entry " },
2017-06-10 13:31:57 +00:00
* { " name " = " public " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0 " , " description " = " will generate a public link for the entry " },
2017-11-06 20:02:48 +00:00
* { " 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). " },
2016-10-28 12:46:30 +00:00
* }
* )
*
* @ return JsonResponse
*/
public function postEntriesAction ( Request $request )
{
$this -> validateAuthentication ();
$url = $request -> request -> get ( 'url' );
2017-06-07 13:07:55 +00:00
$entry = $this -> get ( 'wallabag_core.entry_repository' ) -> findByUrlAndUserId (
$url ,
$this -> getUser () -> getId ()
);
2016-10-28 12:46:30 +00:00
if ( false === $entry ) {
2017-05-08 10:35:02 +00:00
$entry = new Entry ( $this -> getUser ());
2017-05-11 06:14:29 +00:00
$entry -> setUrl ( $url );
2016-10-28 12:46:30 +00:00
}
2017-06-30 14:54:26 +00:00
$data = $this -> retrieveValueFromRequest ( $request );
try {
$this -> get ( 'wallabag_core.content_proxy' ) -> updateEntry (
$entry ,
$entry -> getUrl (),
[
'title' => ! empty ( $data [ 'title' ]) ? $data [ 'title' ] : $entry -> getTitle (),
'html' => ! empty ( $data [ 'content' ]) ? $data [ 'content' ] : $entry -> getContent (),
'url' => $entry -> getUrl (),
'language' => ! empty ( $data [ 'language' ]) ? $data [ 'language' ] : $entry -> getLanguage (),
'date' => ! empty ( $data [ 'publishedAt' ]) ? $data [ 'publishedAt' ] : $entry -> getPublishedAt (),
// faking the open graph preview picture
2017-11-11 19:04:15 +00:00
'image' => ! empty ( $data [ 'picture' ]) ? $data [ 'picture' ] : $entry -> getPreviewPicture (),
2018-09-05 12:25:32 +00:00
'authors' => \is_string ( $data [ 'authors' ]) ? explode ( ',' , $data [ 'authors' ]) : $entry -> getPublishedBy (),
2017-06-30 14:54:26 +00:00
]
);
} catch ( \Exception $e ) {
$this -> get ( 'logger' ) -> error ( 'Error while saving an entry' , [
'exception' => $e ,
'entry' => $entry ,
]);
}
2017-07-03 11:56:39 +00:00
if ( null !== $data [ 'isArchived' ]) {
2018-04-11 09:42:52 +00:00
$entry -> updateArchived (( bool ) $data [ 'isArchived' ]);
2017-06-30 14:54:26 +00:00
}
2017-07-03 11:56:39 +00:00
if ( null !== $data [ 'isStarred' ]) {
2017-08-23 21:06:40 +00:00
$entry -> updateStar (( bool ) $data [ 'isStarred' ]);
2017-06-30 14:54:26 +00:00
}
if ( ! empty ( $data [ 'tags' ])) {
$this -> get ( 'wallabag_core.tags_assigner' ) -> assignTagsToEntry ( $entry , $data [ 'tags' ]);
}
2017-09-04 21:39:08 +00:00
if ( ! empty ( $data [ 'origin_url' ])) {
$entry -> setOriginUrl ( $data [ 'origin_url' ]);
}
2017-07-03 11:56:39 +00:00
if ( null !== $data [ 'isPublic' ]) {
2017-06-30 14:54:26 +00:00
if ( true === ( bool ) $data [ 'isPublic' ] && null === $entry -> getUid ()) {
$entry -> generateUid ();
} elseif ( false === ( bool ) $data [ 'isPublic' ]) {
$entry -> cleanUid ();
}
}
2017-11-26 22:20:23 +00:00
if ( empty ( $entry -> getDomainName ())) {
$this -> get ( 'wallabag_core.content_proxy' ) -> setEntryDomainName ( $entry );
}
if ( empty ( $entry -> getTitle ())) {
$this -> get ( 'wallabag_core.content_proxy' ) -> setDefaultEntryTitle ( $entry );
}
2017-06-30 14:54:26 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
// entry saved, dispatch event about it!
$this -> get ( 'event_dispatcher' ) -> dispatch ( EntrySavedEvent :: NAME , new EntrySavedEvent ( $entry ));
2016-11-03 15:41:29 +00:00
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry );
2016-10-28 12:46:30 +00:00
}
/**
* Change several properties of an entry .
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* },
* parameters = {
* { " name " = " title " , " dataType " = " string " , " required " = false },
* { " name " = " tags " , " dataType " = " string " , " required " = false , " format " = " tag1,tag2,tag3 " , " description " = " a comma-separated list of tags. " },
* { " name " = " archive " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0 " , " description " = " archived the entry. " },
* { " name " = " starred " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0 " , " description " = " starred the entry. " },
2017-06-02 18:52:49 +00:00
* { " name " = " content " , " dataType " = " string " , " required " = false , " description " = " Content of the entry " },
* { " name " = " language " , " dataType " = " string " , " required " = false , " description " = " Language of the entry " },
* { " name " = " preview_picture " , " dataType " = " string " , " required " = false , " description " = " Preview picture of the entry " },
* { " name " = " published_at " , " dataType " = " datetime|integer " , " format " = " YYYY-MM-DDTHH:II:SS+TZ or a timestamp " , " required " = false , " description " = " Published date of the entry " },
* { " name " = " authors " , " dataType " = " string " , " format " = " Name Firstname,author2,author3 " , " required " = false , " description " = " Authors of the entry " },
2017-06-10 13:31:57 +00:00
* { " name " = " public " , " dataType " = " integer " , " required " = false , " format " = " 1 or 0 " , " description " = " will generate a public link for the entry " },
2017-11-06 20:02:48 +00:00
* { " 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). " },
2016-10-28 12:46:30 +00:00
* }
* )
*
* @ return JsonResponse
*/
public function patchEntriesAction ( Entry $entry , Request $request )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
2017-06-30 14:54:26 +00:00
$contentProxy = $this -> get ( 'wallabag_core.content_proxy' );
$data = $this -> retrieveValueFromRequest ( $request );
// this is a special case where user want to manually update the entry content
// the ContentProxy will only cleanup the html
// and also we force to not re-fetch the content in case of error
if ( ! empty ( $data [ 'content' ])) {
try {
$contentProxy -> updateEntry (
$entry ,
$entry -> getUrl (),
[
'html' => $data [ 'content' ],
],
true
);
} catch ( \Exception $e ) {
$this -> get ( 'logger' ) -> error ( 'Error while saving an entry' , [
'exception' => $e ,
'entry' => $entry ,
]);
}
}
if ( ! empty ( $data [ 'title' ])) {
$entry -> setTitle ( $data [ 'title' ]);
}
if ( ! empty ( $data [ 'language' ])) {
$contentProxy -> updateLanguage ( $entry , $data [ 'language' ]);
}
2018-09-05 12:25:32 +00:00
if ( ! empty ( $data [ 'authors' ]) && \is_string ( $data [ 'authors' ])) {
2017-06-30 14:54:26 +00:00
$entry -> setPublishedBy ( explode ( ',' , $data [ 'authors' ]));
}
if ( ! empty ( $data [ 'picture' ])) {
$contentProxy -> updatePreviewPicture ( $entry , $data [ 'picture' ]);
}
if ( ! empty ( $data [ 'publishedAt' ])) {
$contentProxy -> updatePublishedAt ( $entry , $data [ 'publishedAt' ]);
}
2017-07-03 11:56:39 +00:00
if ( null !== $data [ 'isArchived' ]) {
2018-04-11 09:42:52 +00:00
$entry -> updateArchived (( bool ) $data [ 'isArchived' ]);
2017-06-30 14:54:26 +00:00
}
2017-07-03 11:56:39 +00:00
if ( null !== $data [ 'isStarred' ]) {
2017-08-23 21:06:40 +00:00
$entry -> updateStar (( bool ) $data [ 'isStarred' ]);
2017-06-30 14:54:26 +00:00
}
if ( ! empty ( $data [ 'tags' ])) {
$entry -> removeAllTags ();
$this -> get ( 'wallabag_core.tags_assigner' ) -> assignTagsToEntry ( $entry , $data [ 'tags' ]);
}
2017-07-03 11:56:39 +00:00
if ( null !== $data [ 'isPublic' ]) {
2017-06-30 14:54:26 +00:00
if ( true === ( bool ) $data [ 'isPublic' ] && null === $entry -> getUid ()) {
$entry -> generateUid ();
} elseif ( false === ( bool ) $data [ 'isPublic' ]) {
$entry -> cleanUid ();
}
}
2017-09-04 21:39:08 +00:00
if ( ! empty ( $data [ 'origin_url' ])) {
$entry -> setOriginUrl ( $data [ 'origin_url' ]);
}
2017-11-26 22:20:23 +00:00
if ( empty ( $entry -> getDomainName ())) {
$this -> get ( 'wallabag_core.content_proxy' ) -> setEntryDomainName ( $entry );
}
if ( empty ( $entry -> getTitle ())) {
$this -> get ( 'wallabag_core.content_proxy' ) -> setDefaultEntryTitle ( $entry );
}
2017-06-30 14:54:26 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
// entry saved, dispatch event about it!
$this -> get ( 'event_dispatcher' ) -> dispatch ( EntrySavedEvent :: NAME , new EntrySavedEvent ( $entry ));
2016-10-28 12:46:30 +00:00
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry );
2016-10-28 12:46:30 +00:00
}
2016-11-20 12:08:41 +00:00
/**
* Reload an entry .
2016-11-22 09:45:17 +00:00
* 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 ) .
2016-11-20 12:08:41 +00:00
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* }
* )
*
* @ return JsonResponse
*/
public function patchEntriesReloadAction ( Entry $entry )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
try {
2016-12-07 03:17:44 +00:00
$this -> get ( 'wallabag_core.content_proxy' ) -> updateEntry ( $entry , $entry -> getUrl ());
2016-11-20 12:08:41 +00:00
} catch ( \Exception $e ) {
$this -> get ( 'logger' ) -> error ( 'Error while saving an entry' , [
'exception' => $e ,
'entry' => $entry ,
]);
2016-11-22 09:45:17 +00:00
return new JsonResponse ([], 304 );
2016-11-20 12:08:41 +00:00
}
// if refreshing entry failed, don't save it
2019-05-29 10:00:23 +00:00
if ( $this -> container -> getParameter ( 'wallabag_core.fetching_error_message' ) === $entry -> getContent ()) {
2016-11-22 09:45:17 +00:00
return new JsonResponse ([], 304 );
2016-11-20 12:08:41 +00:00
}
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
// entry saved, dispatch event about it!
$this -> get ( 'event_dispatcher' ) -> dispatch ( EntrySavedEvent :: NAME , new EntrySavedEvent ( $entry ));
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry );
2016-11-20 12:08:41 +00:00
}
2016-10-28 12:46:30 +00:00
/**
* Delete ** permanently ** an entry .
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
2019-02-17 14:30:42 +00:00
* },
* parameters = {
* { " 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 " },
2016-10-28 12:46:30 +00:00
* }
* )
*
* @ return JsonResponse
*/
2019-02-17 14:30:42 +00:00
public function deleteEntriesAction ( Entry $entry , Request $request )
2016-10-28 12:46:30 +00:00
{
2019-02-17 14:30:42 +00:00
$expect = $request -> query -> get ( 'expect' , 'entry' );
if ( ! \in_array ( $expect , [ 'id' , 'entry' ], true )) {
throw new BadRequestHttpException ( sprintf ( " expect: 'id' or 'entry' expected, %s given " , $expect ));
}
2016-10-28 12:46:30 +00:00
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
2019-02-17 14:30:42 +00:00
$response = $this -> sendResponse ([
'id' => $entry -> getId (),
]);
// We clone $entry to keep id in returned object
if ( 'entry' === $expect ) {
$e = clone $entry ;
$response = $this -> sendResponse ( $e );
}
2017-12-17 20:05:25 +00:00
2020-04-06 14:11:05 +00:00
// entry deleted, dispatch event about it!
$this -> get ( 'event_dispatcher' ) -> dispatch ( EntryDeletedEvent :: NAME , new EntryDeletedEvent ( $entry ));
2016-10-28 12:46:30 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> remove ( $entry );
$em -> flush ();
2019-02-17 14:30:42 +00:00
return $response ;
2016-10-28 12:46:30 +00:00
}
/**
* Retrieve all tags for an entry .
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* }
* )
*
* @ return JsonResponse
*/
public function getEntriesTagsAction ( Entry $entry )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry -> getTags ());
2016-10-28 12:46:30 +00:00
}
/**
* Add one or more tags to an entry .
*
* @ ApiDoc (
* requirements = {
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* },
* parameters = {
* { " name " = " tags " , " dataType " = " string " , " required " = false , " format " = " tag1,tag2,tag3 " , " description " = " a comma-separated list of tags. " },
* }
* )
*
* @ return JsonResponse
*/
public function postEntriesTagsAction ( Request $request , Entry $entry )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
$tags = $request -> request -> get ( 'tags' , '' );
if ( ! empty ( $tags )) {
2017-05-27 20:08:14 +00:00
$this -> get ( 'wallabag_core.tags_assigner' ) -> assignTagsToEntry ( $entry , $tags );
2016-10-28 12:46:30 +00:00
}
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry );
2016-10-28 12:46:30 +00:00
}
/**
* Permanently remove one tag for an entry .
*
* @ ApiDoc (
* requirements = {
* { " name " = " tag " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The tag ID " },
* { " name " = " entry " , " dataType " = " integer " , " requirement " = " \ w+ " , " description " = " The entry ID " }
* }
* )
*
* @ return JsonResponse
*/
public function deleteEntriesTagsAction ( Entry $entry , Tag $tag )
{
$this -> validateAuthentication ();
$this -> validateUserAccess ( $entry -> getUser () -> getId ());
$entry -> removeTag ( $tag );
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $entry );
2016-10-28 12:46:30 +00:00
}
2017-04-24 09:12:41 +00:00
/**
2017-04-24 10:24:17 +00:00
* Handles an entries list delete tags from them .
2017-04-24 09:12:41 +00:00
*
* @ ApiDoc (
* parameters = {
2017-04-24 10:24:17 +00:00
* { " 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. " }
2017-04-24 09:12:41 +00:00
* }
* )
*
* @ return JsonResponse
*/
2017-04-24 10:24:17 +00:00
public function deleteEntriesTagsListAction ( Request $request )
2017-04-24 09:12:41 +00:00
{
$this -> validateAuthentication ();
$list = json_decode ( $request -> query -> get ( 'list' , []));
2017-05-05 10:05:50 +00:00
if ( empty ( $list )) {
return $this -> sendResponse ([]);
}
2017-04-24 09:12:41 +00:00
// handle multiple urls
2017-05-05 10:05:50 +00:00
$results = [];
2017-04-24 09:12:41 +00:00
2017-05-05 10:05:50 +00:00
foreach ( $list as $key => $element ) {
$entry = $this -> get ( 'wallabag_core.entry_repository' ) -> findByUrlAndUserId (
$element -> url ,
$this -> getUser () -> getId ()
);
2017-04-24 09:12:41 +00:00
2017-05-05 10:05:50 +00:00
$results [ $key ][ 'url' ] = $element -> url ;
$results [ $key ][ 'entry' ] = $entry instanceof Entry ? $entry -> getId () : false ;
2017-04-24 09:12:41 +00:00
2017-05-05 10:05:50 +00:00
$tags = $element -> tags ;
2017-04-24 10:24:17 +00:00
2017-05-05 10:05:50 +00:00
if ( false !== $entry && ! ( empty ( $tags ))) {
$tags = explode ( ',' , $tags );
foreach ( $tags as $label ) {
$label = trim ( $label );
2017-04-24 10:24:17 +00:00
2017-05-05 10:05:50 +00:00
$tag = $this -> getDoctrine ()
-> getRepository ( 'WallabagCoreBundle:Tag' )
-> findOneByLabel ( $label );
2017-04-24 09:12:41 +00:00
2017-05-05 10:05:50 +00:00
if ( false !== $tag ) {
$entry -> removeTag ( $tag );
}
2017-04-24 09:12:41 +00:00
}
2017-05-05 10:05:50 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
2017-04-24 09:12:41 +00:00
}
}
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $results );
2017-04-24 09:12:41 +00:00
}
2017-04-24 10:24:17 +00:00
/**
* Handles an entries list and add tags to them .
*
* @ ApiDoc (
* parameters = {
* { " 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. " }
* }
* )
*
* @ return JsonResponse
*/
public function postEntriesTagsListAction ( Request $request )
{
$this -> validateAuthentication ();
$list = json_decode ( $request -> query -> get ( 'list' , []));
2017-05-05 10:05:50 +00:00
if ( empty ( $list )) {
return $this -> sendResponse ([]);
}
2017-04-24 10:24:17 +00:00
$results = [];
// handle multiple urls
2017-05-05 10:05:50 +00:00
foreach ( $list as $key => $element ) {
$entry = $this -> get ( 'wallabag_core.entry_repository' ) -> findByUrlAndUserId (
$element -> url ,
$this -> getUser () -> getId ()
);
2017-04-24 10:24:17 +00:00
2017-05-05 10:05:50 +00:00
$results [ $key ][ 'url' ] = $element -> url ;
$results [ $key ][ 'entry' ] = $entry instanceof Entry ? $entry -> getId () : false ;
2017-04-24 10:24:17 +00:00
2017-05-05 10:05:50 +00:00
$tags = $element -> tags ;
2017-04-24 10:24:17 +00:00
2017-05-05 10:05:50 +00:00
if ( false !== $entry && ! ( empty ( $tags ))) {
2017-05-27 20:08:14 +00:00
$this -> get ( 'wallabag_core.tags_assigner' ) -> assignTagsToEntry ( $entry , $tags );
2017-04-24 10:24:17 +00:00
2017-05-05 10:05:50 +00:00
$em = $this -> getDoctrine () -> getManager ();
$em -> persist ( $entry );
$em -> flush ();
2017-04-24 10:24:17 +00:00
}
}
2017-05-05 10:05:50 +00:00
return $this -> sendResponse ( $results );
}
2019-05-10 12:07:55 +00:00
/**
* Replace the hashedUrl keys in $results with the unhashed URL from the
* request , as recorded in $urlHashMap .
*/
private function replaceUrlHashes ( array $results , array $urlHashMap )
{
$newResults = [];
foreach ( $results as $hash => $res ) {
if ( isset ( $urlHashMap [ $hash ])) {
$newResults [ $urlHashMap [ $hash ]] = $res ;
} else {
$newResults [ $hash ] = $res ;
}
}
return $newResults ;
}
2017-06-07 13:07:55 +00:00
/**
2017-06-30 14:54:26 +00:00
* Retrieve value from the request .
* Used for POST & PATCH on a an entry .
2017-06-07 13:07:55 +00:00
*
2017-06-30 14:54:26 +00:00
* @ return array
2017-06-07 13:07:55 +00:00
*/
2017-06-30 14:54:26 +00:00
private function retrieveValueFromRequest ( Request $request )
2017-06-07 13:07:55 +00:00
{
2017-06-30 14:54:26 +00:00
return [
'title' => $request -> request -> get ( 'title' ),
'tags' => $request -> request -> get ( 'tags' , []),
'isArchived' => $request -> request -> get ( 'archive' ),
'isStarred' => $request -> request -> get ( 'starred' ),
'isPublic' => $request -> request -> get ( 'public' ),
'content' => $request -> request -> get ( 'content' ),
'language' => $request -> request -> get ( 'language' ),
'picture' => $request -> request -> get ( 'preview_picture' ),
'publishedAt' => $request -> request -> get ( 'published_at' ),
'authors' => $request -> request -> get ( 'authors' , '' ),
2017-09-04 21:39:08 +00:00
'origin_url' => $request -> request -> get ( 'origin_url' , '' ),
2017-06-30 14:54:26 +00:00
];
2017-06-07 13:07:55 +00:00
}
2016-10-28 12:46:30 +00:00
}