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