]> git.immae.eu Git - github/wallabag/wallabag.git/blob - src/Wallabag/ImportBundle/Import/PocketImport.php
Add CraueConfig for internal settings
[github/wallabag/wallabag.git] / src / Wallabag / ImportBundle / Import / PocketImport.php
1 <?php
2
3 namespace Wallabag\ImportBundle\Import;
4
5 use Psr\Log\LoggerInterface;
6 use Psr\Log\NullLogger;
7 use Doctrine\ORM\EntityManager;
8 use GuzzleHttp\Client;
9 use GuzzleHttp\Exception\RequestException;
10 use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
11 use Wallabag\CoreBundle\Entity\Entry;
12 use Wallabag\CoreBundle\Entity\Tag;
13 use Wallabag\CoreBundle\Helper\ContentProxy;
14 use Craue\ConfigBundle\Util\Config;
15
16 class PocketImport implements ImportInterface
17 {
18 private $user;
19 private $em;
20 private $contentProxy;
21 private $logger;
22 private $client;
23 private $consumerKey;
24 private $skippedEntries = 0;
25 private $importedEntries = 0;
26 protected $accessToken;
27 private $translator;
28
29 public function __construct(TokenStorageInterface $tokenStorage, EntityManager $em, ContentProxy $contentProxy, Config $craueConfig)
30 {
31 $this->user = $tokenStorage->getToken()->getUser();
32 $this->em = $em;
33 $this->contentProxy = $contentProxy;
34 $this->consumerKey = $craueConfig->get('pocket_consumer_key');
35 $this->logger = new NullLogger();
36 }
37
38 public function setLogger(LoggerInterface $logger)
39 {
40 $this->logger = $logger;
41 }
42
43 /**
44 * {@inheritdoc}
45 */
46 public function getName()
47 {
48 return 'Pocket';
49 }
50
51 /**
52 * {@inheritdoc}
53 */
54 public function getUrl()
55 {
56 return 'import_pocket';
57 }
58
59 /**
60 * {@inheritdoc}
61 */
62 public function getDescription()
63 {
64 return 'This importer will import all your Pocket data. Pocket doesn\'t allow us to retrieve content from their service, so the readable content of each article will be re-fetched by wallabag.';
65 }
66
67 /**
68 * Return the oauth url to authenticate the client.
69 *
70 * @param string $redirectUri Redirect url in case of error
71 *
72 * @return string request_token for callback method
73 */
74 public function getRequestToken($redirectUri)
75 {
76 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/request',
77 [
78 'body' => json_encode([
79 'consumer_key' => $this->consumerKey,
80 'redirect_uri' => $redirectUri,
81 ]),
82 ]
83 );
84
85 try {
86 $response = $this->client->send($request);
87 } catch (RequestException $e) {
88 $this->logger->error(sprintf('PocketImport: Failed to request token: %s', $e->getMessage()), ['exception' => $e]);
89
90 return false;
91 }
92
93 return $response->json()['code'];
94 }
95
96 /**
97 * Usually called by the previous callback to authorize the client.
98 * Then it return a token that can be used for next requests.
99 *
100 * @param string $code request_token from getRequestToken
101 *
102 * @return bool
103 */
104 public function authorize($code)
105 {
106 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/oauth/authorize',
107 [
108 'body' => json_encode([
109 'consumer_key' => $this->consumerKey,
110 'code' => $code,
111 ]),
112 ]
113 );
114
115 try {
116 $response = $this->client->send($request);
117 } catch (RequestException $e) {
118 $this->logger->error(sprintf('PocketImport: Failed to authorize client: %s', $e->getMessage()), ['exception' => $e]);
119
120 return false;
121 }
122
123 $this->accessToken = $response->json()['access_token'];
124
125 return true;
126 }
127
128 /**
129 * {@inheritdoc}
130 */
131 public function import()
132 {
133 $request = $this->client->createRequest('POST', 'https://getpocket.com/v3/get',
134 [
135 'body' => json_encode([
136 'consumer_key' => $this->consumerKey,
137 'access_token' => $this->accessToken,
138 'detailType' => 'complete',
139 'state' => 'all',
140 'sort' => 'oldest',
141 ]),
142 ]
143 );
144
145 try {
146 $response = $this->client->send($request);
147 } catch (RequestException $e) {
148 $this->logger->error(sprintf('PocketImport: Failed to import: %s', $e->getMessage()), ['exception' => $e]);
149
150 return false;
151 }
152
153 $entries = $response->json();
154
155 $this->parseEntries($entries['list']);
156
157 return true;
158 }
159
160 /**
161 * {@inheritdoc}
162 */
163 public function getSummary()
164 {
165 return [
166 'skipped' => $this->skippedEntries,
167 'imported' => $this->importedEntries,
168 ];
169 }
170
171 /**
172 * Set the Guzzle client.
173 *
174 * @param Client $client
175 */
176 public function setClient(Client $client)
177 {
178 $this->client = $client;
179 }
180
181 /**
182 * @todo move that in a more global place
183 */
184 private function assignTagsToEntry(Entry $entry, $tags)
185 {
186 foreach ($tags as $tag) {
187 $label = trim($tag['tag']);
188 $tagEntity = $this->em
189 ->getRepository('WallabagCoreBundle:Tag')
190 ->findOneByLabel($label);
191
192 if (is_object($tagEntity)) {
193 $entry->addTag($tagEntity);
194 } else {
195 $newTag = new Tag();
196 $newTag->setLabel($label);
197
198 $entry->addTag($newTag);
199 }
200 $this->em->flush();
201 }
202 }
203
204 /**
205 * @see https://getpocket.com/developer/docs/v3/retrieve
206 *
207 * @param $entries
208 */
209 private function parseEntries($entries)
210 {
211 $i = 1;
212
213 foreach ($entries as $pocketEntry) {
214 $url = isset($pocketEntry['resolved_url']) && $pocketEntry['resolved_url'] != '' ? $pocketEntry['resolved_url'] : $pocketEntry['given_url'];
215
216 $existingEntry = $this->em
217 ->getRepository('WallabagCoreBundle:Entry')
218 ->findByUrlAndUserId($url, $this->user->getId());
219
220 if (false !== $existingEntry) {
221 ++$this->skippedEntries;
222 continue;
223 }
224
225 $entry = new Entry($this->user);
226 $entry = $this->contentProxy->updateEntry($entry, $url);
227
228 // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted
229 if ($pocketEntry['status'] == 1) {
230 $entry->setArchived(true);
231 }
232
233 // 0 or 1 - 1 If the item is favorited
234 if ($pocketEntry['favorite'] == 1) {
235 $entry->setStarred(true);
236 }
237
238 $title = 'Untitled';
239 if (isset($pocketEntry['resolved_title']) && $pocketEntry['resolved_title'] != '') {
240 $title = $pocketEntry['resolved_title'];
241 } elseif (isset($pocketEntry['given_title']) && $pocketEntry['given_title'] != '') {
242 $title = $pocketEntry['given_title'];
243 }
244
245 $entry->setTitle($title);
246
247 // 0, 1, or 2 - 1 if the item has images in it - 2 if the item is an image
248 if (isset($pocketEntry['has_image']) && $pocketEntry['has_image'] > 0 && isset($pocketEntry['images'][1])) {
249 $entry->setPreviewPicture($pocketEntry['images'][1]['src']);
250 }
251
252 if (isset($pocketEntry['tags']) && !empty($pocketEntry['tags'])) {
253 $this->assignTagsToEntry($entry, $pocketEntry['tags']);
254 }
255
256 $this->em->persist($entry);
257 ++$this->importedEntries;
258
259 // flush every 20 entries
260 if (($i % 20) === 0) {
261 $this->em->flush();
262 }
263 ++$i;
264 }
265
266 $this->em->flush();
267 }
268 }