]> git.immae.eu Git - github/wallabag/wallabag.git/blob - src/Wallabag/ImportBundle/Import/PocketImport.php
Moved Pocket token to user config
[github/wallabag/wallabag.git] / src / Wallabag / ImportBundle / Import / PocketImport.php
1 <?php
2
3 namespace Wallabag\ImportBundle\Import;
4
5 use Psr\Log\NullLogger;
6 use Doctrine\ORM\EntityManager;
7 use GuzzleHttp\Client;
8 use GuzzleHttp\Exception\RequestException;
9 use Wallabag\CoreBundle\Entity\Entry;
10 use Wallabag\CoreBundle\Helper\ContentProxy;
11 use Craue\ConfigBundle\Util\Config;
12
13 class PocketImport extends AbstractImport
14 {
15 private $client;
16 private $accessToken;
17
18 const NB_ELEMENTS = 5000;
19
20 public function __construct(EntityManager $em, ContentProxy $contentProxy)
21 {
22 $this->em = $em;
23 $this->contentProxy = $contentProxy;
24 $this->logger = new NullLogger();
25 }
26
27 /**
28 * Only used for test purpose.
29 *
30 * @return string
31 */
32 public function getAccessToken()
33 {
34 return $this->accessToken;
35 }
36
37 /**
38 * {@inheritdoc}
39 */
40 public function getName()
41 {
42 return 'Pocket';
43 }
44
45 /**
46 * {@inheritdoc}
47 */
48 public function getUrl()
49 {
50 return 'import_pocket';
51 }
52
53 /**
54 * {@inheritdoc}
55 */
56 public function getDescription()
57 {
58 return 'import.pocket.description';
59 }
60
61 /**
62 * Return the oauth url to authenticate the client.
63 *
64 * @param string $redirectUri Redirect url in case of error
65 *
66 * @return string|false request_token for callback method
67 */
68 public function getRequestToken($redirectUri)
69 {
70 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/request',
71 [
72 'body' => json_encode([
73 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
74 'redirect_uri' => $redirectUri,
75 ]),
76 ]
77 );
78
79 try {
80 $response = $this->client->send($request);
81 } catch (RequestException $e) {
82 $this->logger->error(sprintf('PocketImport: Failed to request token: %s', $e->getMessage()), ['exception' => $e]);
83
84 return false;
85 }
86
87 return $response->json()['code'];
88 }
89
90 /**
91 * Usually called by the previous callback to authorize the client.
92 * Then it return a token that can be used for next requests.
93 *
94 * @param string $code request_token from getRequestToken
95 *
96 * @return bool
97 */
98 public function authorize($code)
99 {
100 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/authorize',
101 [
102 'body' => json_encode([
103 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
104 'code' => $code,
105 ]),
106 ]
107 );
108
109 try {
110 $response = $this->client->send($request);
111 } catch (RequestException $e) {
112 $this->logger->error(sprintf('PocketImport: Failed to authorize client: %s', $e->getMessage()), ['exception' => $e]);
113
114 return false;
115 }
116
117 $this->accessToken = $response->json()['access_token'];
118
119 return true;
120 }
121
122 /**
123 * {@inheritdoc}
124 */
125 public function import($offset = 0)
126 {
127 static $run = 0;
128
129 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/get',
130 [
131 'body' => json_encode([
132 'consumer_key' => $this->user->getConfig()->getPocketConsumerKey(),
133 'access_token' => $this->accessToken,
134 'detailType' => 'complete',
135 'state' => 'all',
136 'sort' => 'newest',
137 'count' => self::NB_ELEMENTS,
138 'offset' => $offset,
139 ]),
140 ]
141 );
142
143 try {
144 $response = $this->client->send($request);
145 } catch (RequestException $e) {
146 $this->logger->error(sprintf('PocketImport: Failed to import: %s', $e->getMessage()), ['exception' => $e]);
147
148 return false;
149 }
150
151 $entries = $response->json();
152
153 if ($this->producer) {
154 $this->parseEntriesForProducer($entries['list']);
155 } else {
156 $this->parseEntries($entries['list']);
157 }
158
159 // if we retrieve exactly the amount of items requested it means we can get more
160 // re-call import and offset item by the amount previous received:
161 // - first call get 5k offset 0
162 // - second call get 5k offset 5k
163 // - and so on
164 if (count($entries['list']) === self::NB_ELEMENTS) {
165 ++$run;
166
167 return $this->import(self::NB_ELEMENTS * $run);
168 }
169
170 return true;
171 }
172
173 /**
174 * Set the Guzzle client.
175 *
176 * @param Client $client
177 */
178 public function setClient(Client $client)
179 {
180 $this->client = $client;
181 }
182
183 /**
184 * {@inheritdoc}
185 *
186 * @see https://getpocket.com/developer/docs/v3/retrieve
187 */
188 public function parseEntry(array $importedEntry)
189 {
190 $url = isset($importedEntry['resolved_url']) && $importedEntry['resolved_url'] != '' ? $importedEntry['resolved_url'] : $importedEntry['given_url'];
191
192 $existingEntry = $this->em
193 ->getRepository('WallabagCoreBundle:Entry')
194 ->findByUrlAndUserId($url, $this->user->getId());
195
196 if (false !== $existingEntry) {
197 ++$this->skippedEntries;
198
199 return;
200 }
201
202 $entry = new Entry($this->user);
203 $entry = $this->fetchContent($entry, $url);
204
205 // jump to next entry in case of problem while getting content
206 if (false === $entry) {
207 ++$this->skippedEntries;
208
209 return;
210 }
211
212 // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted
213 if ($importedEntry['status'] == 1 || $this->markAsRead) {
214 $entry->setArchived(true);
215 }
216
217 // 0 or 1 - 1 If the item is starred
218 if ($importedEntry['favorite'] == 1) {
219 $entry->setStarred(true);
220 }
221
222 $title = 'Untitled';
223 if (isset($importedEntry['resolved_title']) && $importedEntry['resolved_title'] != '') {
224 $title = $importedEntry['resolved_title'];
225 } elseif (isset($importedEntry['given_title']) && $importedEntry['given_title'] != '') {
226 $title = $importedEntry['given_title'];
227 }
228
229 $entry->setTitle($title);
230 $entry->setUrl($url);
231
232 // 0, 1, or 2 - 1 if the item has images in it - 2 if the item is an image
233 if (isset($importedEntry['has_image']) && $importedEntry['has_image'] > 0 && isset($importedEntry['images'][1])) {
234 $entry->setPreviewPicture($importedEntry['images'][1]['src']);
235 }
236
237 if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) {
238 $this->contentProxy->assignTagsToEntry(
239 $entry,
240 array_keys($importedEntry['tags'])
241 );
242 }
243
244 if (!empty($importedEntry['time_added'])) {
245 $entry->setCreatedAt((new \DateTime())->setTimestamp($importedEntry['time_added']));
246 }
247
248 $this->em->persist($entry);
249 ++$this->importedEntries;
250
251 return $entry;
252 }
253
254 /**
255 * {@inheritdoc}
256 */
257 protected function setEntryAsRead(array $importedEntry)
258 {
259 $importedEntry['status'] = '1';
260
261 return $importedEntry;
262 }
263 }