]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | class GoogleReaderImport extends Plugin { | |
3 | ||
4 | ||
5 | private $link; | |
6 | private $host; | |
7 | ||
8 | function about() { | |
9 | return array(1.0, | |
10 | "Import starred/shared items from Google Reader takeout", | |
11 | "fox", | |
12 | false, | |
13 | ""); | |
14 | } | |
15 | ||
16 | function init($host) { | |
17 | $this->link = $host->get_link(); | |
18 | $this->host = $host; | |
19 | ||
20 | $host->add_command("greader-import", | |
21 | "import data in Google Reader JSON format", | |
22 | $this, ":", "FILE"); | |
23 | ||
24 | $host->add_hook($host::HOOK_PREFS_TAB, $this); | |
25 | } | |
26 | ||
27 | function greader_import($args) { | |
28 | $file = $args['greader_import']; | |
29 | ||
30 | if (!file_exists($file)) { | |
31 | _debug("file not found: $file"); | |
32 | return; | |
33 | } | |
34 | ||
35 | _debug("please enter your username:"); | |
36 | ||
37 | $username = db_escape_string($this->link, trim(read_stdin())); | |
38 | ||
39 | _debug("looking up user: $username..."); | |
40 | ||
41 | $result = db_query($this->link, "SELECT id FROM ttrss_users | |
42 | WHERE login = '$username'"); | |
43 | ||
44 | if (db_num_rows($result) == 0) { | |
45 | _debug("user not found."); | |
46 | return; | |
47 | } | |
48 | ||
49 | $owner_uid = db_fetch_result($result, 0, "id"); | |
50 | ||
51 | _debug("processing: $file (owner_uid: $owner_uid)"); | |
52 | ||
53 | $this->import($file, $owner_uid); | |
54 | } | |
55 | ||
56 | function get_prefs_js() { | |
57 | return file_get_contents(dirname(__FILE__) . "/init.js"); | |
58 | } | |
59 | ||
60 | function import($file = false, $owner_uid = 0) { | |
61 | ||
62 | purge_orphans($this->link); | |
63 | ||
64 | if (!$file) { | |
65 | header("Content-Type: text/html"); | |
66 | ||
67 | $owner_uid = $_SESSION["uid"]; | |
68 | ||
69 | if (is_file($_FILES['starred_file']['tmp_name'])) { | |
70 | $doc = json_decode(file_get_contents($_FILES['starred_file']['tmp_name']), true); | |
71 | } else { | |
72 | print_error(__('No file uploaded.')); | |
73 | return; | |
74 | } | |
75 | } else { | |
76 | $doc = json_decode(file_get_contents($file), true); | |
77 | } | |
78 | ||
79 | if ($file) { | |
80 | $sql_set_marked = strtolower(basename($file)) == 'starred.json' ? 'true' : 'false'; | |
81 | _debug("will set articles as starred: $sql_set_marked"); | |
82 | ||
83 | } else { | |
84 | $sql_set_marked = strtolower($_FILES['starred_file']['name']) == 'starred.json' ? 'true' : 'false'; | |
85 | } | |
86 | ||
87 | if ($doc) { | |
88 | if (isset($doc['items'])) { | |
89 | $processed = 0; | |
90 | ||
91 | foreach ($doc['items'] as $item) { | |
92 | // print_r($item); | |
93 | ||
94 | $guid = db_escape_string($this->link, mb_substr($item['id'], 0, 250)); | |
95 | $title = db_escape_string($this->link, $item['title']); | |
96 | $updated = date('Y-m-d h:i:s', $item['updated']); | |
97 | $link = ''; | |
98 | $content = ''; | |
99 | $author = db_escape_string($this->link, $item['author']); | |
100 | $tags = array(); | |
101 | $orig_feed_data = array(); | |
102 | ||
103 | if (is_array($item['alternate'])) { | |
104 | foreach ($item['alternate'] as $alt) { | |
105 | if (isset($alt['type']) && $alt['type'] == 'text/html') { | |
106 | $link = db_escape_string($this->link, $alt['href']); | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | if (is_array($item['content'])) { | |
112 | $content = db_escape_string($this->link, | |
113 | $item['content']['content'], false); | |
114 | } | |
115 | ||
116 | if (is_array($item['categories'])) { | |
117 | foreach ($item['categories'] as $cat) { | |
118 | if (strstr($cat, "com.google/") === FALSE) { | |
119 | array_push($tags, sanitize_tag($cat)); | |
120 | } | |
121 | } | |
122 | } | |
123 | ||
124 | if (is_array($item['origin'])) { | |
125 | if (strpos($item['origin']['streamId'], 'feed/') === 0) { | |
126 | ||
127 | $orig_feed_data['feed_url'] = db_escape_string($this->link, | |
128 | preg_replace("/^feed\//", | |
129 | "", $item['origin']['streamId'])); | |
130 | ||
131 | $orig_feed_data['title'] = db_escape_string($this->link, | |
132 | $item['origin']['title']); | |
133 | ||
134 | $orig_feed_data['site_url'] = db_escape_string($this->link, | |
135 | $item['origin']['htmlUrl']); | |
136 | } | |
137 | } | |
138 | ||
139 | $processed++; | |
140 | ||
141 | $imported += (int) $this->create_article($owner_uid, $guid, $title, | |
142 | $updated, $link, $content, $author, $sql_set_marked, $tags, | |
143 | $orig_feed_data); | |
144 | ||
145 | if ($file && $processed % 25 == 0) { | |
146 | _debug("processed $processed articles..."); | |
147 | } | |
148 | } | |
149 | ||
150 | if ($file) { | |
151 | _debug(sprintf("All done. %d of %d articles imported.", $imported, $processed)); | |
152 | } else { | |
153 | print "<p style='text-align : center'>" . T_sprintf("All done. %d out of %d articles imported.", $imported, $processed) . "</p>"; | |
154 | } | |
155 | ||
156 | } else { | |
157 | print_error(__('The document has incorrect format.')); | |
158 | } | |
159 | ||
160 | } else { | |
161 | print_error(__('Error while parsing document.')); | |
162 | } | |
163 | ||
164 | if (!$file) { | |
165 | print "<div align='center'>"; | |
166 | print "<button dojoType=\"dijit.form.Button\" | |
167 | onclick=\"dijit.byId('starredImportDlg').execute()\">". | |
168 | __('Close this window')."</button>"; | |
169 | print "</div>"; | |
170 | } | |
171 | } | |
172 | ||
173 | // expects ESCAPED data | |
174 | private function create_article($owner_uid, $guid, $title, $updated, $link, $content, $author, $marked, $tags, $orig_feed_data) { | |
175 | ||
176 | if (!$guid) $guid = sha1($link); | |
177 | ||
178 | $create_archived_feeds = true; | |
179 | ||
180 | $guid = "$owner_uid,$guid"; | |
181 | ||
182 | $content_hash = sha1($content); | |
183 | ||
184 | if (filter_var($link, FILTER_VALIDATE_URL) === FALSE) return false; | |
185 | ||
186 | db_query($this->link, "BEGIN"); | |
187 | ||
188 | $feed_id = 'NULL'; | |
189 | ||
190 | // let's check for archived feed entry | |
191 | ||
192 | $feed_inserted = false; | |
193 | ||
194 | // before dealing with archived feeds we must check ttrss_feeds to maintain id consistency | |
195 | ||
196 | if ($orig_feed_data['feed_url'] && $create_archived_feeds) { | |
197 | $result = db_query($this->link, | |
198 | "SELECT id FROM ttrss_feeds WHERE feed_url = '".$orig_feed_data['feed_url']."' | |
199 | AND owner_uid = $owner_uid"); | |
200 | ||
201 | if (db_num_rows($result) != 0) { | |
202 | $feed_id = db_fetch_result($result, 0, "id"); | |
203 | } else { | |
204 | // let's insert it | |
205 | ||
206 | if (!$orig_feed_data['title']) $orig_feed_data['title'] = '[Unknown]'; | |
207 | ||
208 | $result = db_query($this->link, | |
209 | "INSERT INTO ttrss_feeds | |
210 | (owner_uid,feed_url,site_url,title,cat_id,auth_login,auth_pass,update_method) | |
211 | VALUES ($owner_uid, | |
212 | '".$orig_feed_data['feed_url']."', | |
213 | '".$orig_feed_data['site_url']."', | |
214 | '".$orig_feed_data['title']."', | |
215 | NULL, '', '', 0)"); | |
216 | ||
217 | $result = db_query($this->link, | |
218 | "SELECT id FROM ttrss_feeds WHERE feed_url = '".$orig_feed_data['feed_url']."' | |
219 | AND owner_uid = $owner_uid"); | |
220 | ||
221 | if (db_num_rows($result) != 0) { | |
222 | $feed_id = db_fetch_result($result, 0, "id"); | |
223 | $feed_inserted = true; | |
224 | } | |
225 | } | |
226 | } | |
227 | ||
228 | if ($feed_id) { | |
229 | // locate archived entry to file entries in, we don't want to file them in actual feeds because of purging | |
230 | // maybe file marked in real feeds because eh | |
231 | ||
232 | $result = db_query($this->link, "SELECT id FROM ttrss_archived_feeds WHERE | |
233 | feed_url = '".$orig_feed_data['feed_url']."' AND owner_uid = $owner_uid"); | |
234 | ||
235 | if (db_num_rows($result) != 0) { | |
236 | $orig_feed_id = db_fetch_result($result, 0, "id"); | |
237 | } else { | |
238 | db_query($this->link, "INSERT INTO ttrss_archived_feeds | |
239 | (id, owner_uid, title, feed_url, site_url) | |
240 | SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds | |
241 | WHERE id = '$feed_id'"); | |
242 | ||
243 | $result = db_query($this->link, "SELECT id FROM ttrss_archived_feeds WHERE | |
244 | feed_url = '".$orig_feed_data['feed_url']."' AND owner_uid = $owner_uid"); | |
245 | ||
246 | if (db_num_rows($result) != 0) { | |
247 | $orig_feed_id = db_fetch_result($result, 0, "id"); | |
248 | } | |
249 | } | |
250 | } | |
251 | ||
252 | // delete temporarily inserted feed | |
253 | if ($feed_id && $feed_inserted) { | |
254 | db_query($this->link, "DELETE FROM ttrss_feeds WHERE id = $feed_id"); | |
255 | } | |
256 | ||
257 | $result = db_query($this->link, "SELECT id FROM ttrss_entries, ttrss_user_entries WHERE | |
258 | guid = '$guid' AND ref_id = id AND owner_uid = '$owner_uid' LIMIT 1"); | |
259 | ||
260 | if (db_num_rows($result) == 0) { | |
261 | $result = db_query($this->link, "INSERT INTO ttrss_entries | |
262 | (title, guid, link, updated, content, content_hash, date_entered, date_updated, author) | |
263 | VALUES | |
264 | ('$title', '$guid', '$link', '$updated', '$content', '$content_hash', NOW(), NOW(), '$author')"); | |
265 | ||
266 | $result = db_query($this->link, "SELECT id FROM ttrss_entries WHERE guid = '$guid'"); | |
267 | ||
268 | if (db_num_rows($result) != 0) { | |
269 | $ref_id = db_fetch_result($result, 0, "id"); | |
270 | ||
271 | db_query($this->link, "INSERT INTO ttrss_user_entries | |
272 | (ref_id, uuid, feed_id, orig_feed_id, owner_uid, marked, tag_cache, label_cache, | |
273 | last_read, note, unread, last_marked) | |
274 | VALUES | |
275 | ('$ref_id', '', NULL, $orig_feed_id, $owner_uid, $marked, '', '', NOW(), '', false, NOW())"); | |
276 | ||
277 | $result = db_query($this->link, "SELECT int_id FROM ttrss_user_entries, ttrss_entries | |
278 | WHERE owner_uid = $owner_uid AND ref_id = id AND ref_id = $ref_id"); | |
279 | ||
280 | if (db_num_rows($result) != 0 && is_array($tags)) { | |
281 | ||
282 | $entry_int_id = db_fetch_result($result, 0, "int_id"); | |
283 | $tags_to_cache = array(); | |
284 | ||
285 | foreach ($tags as $tag) { | |
286 | ||
287 | $tag = db_escape_string($this->link, sanitize_tag($tag)); | |
288 | ||
289 | if (!tag_is_valid($tag)) continue; | |
290 | ||
291 | $result = db_query($this->link, "SELECT id FROM ttrss_tags | |
292 | WHERE tag_name = '$tag' AND post_int_id = '$entry_int_id' AND | |
293 | owner_uid = '$owner_uid' LIMIT 1"); | |
294 | ||
295 | if ($result && db_num_rows($result) == 0) { | |
296 | db_query($this->link, "INSERT INTO ttrss_tags | |
297 | (owner_uid,tag_name,post_int_id) | |
298 | VALUES ('$owner_uid','$tag', '$entry_int_id')"); | |
299 | } | |
300 | ||
301 | array_push($tags_to_cache, $tag); | |
302 | } | |
303 | ||
304 | /* update the cache */ | |
305 | ||
306 | $tags_to_cache = array_unique($tags_to_cache); | |
307 | $tags_str = db_escape_string($this->link, join(",", $tags_to_cache)); | |
308 | ||
309 | db_query($this->link, "UPDATE ttrss_user_entries | |
310 | SET tag_cache = '$tags_str' WHERE ref_id = '$ref_id' | |
311 | AND owner_uid = $owner_uid"); | |
312 | } | |
313 | ||
314 | $rc = true; | |
315 | } | |
316 | } | |
317 | ||
318 | db_query($this->link, "COMMIT"); | |
319 | ||
320 | return $rc; | |
321 | } | |
322 | ||
323 | function hook_prefs_tab($args) { | |
324 | if ($args != "prefFeeds") return; | |
325 | ||
326 | print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__("Import starred or shared items from Google Reader")."\">"; | |
327 | ||
328 | print_notice("Your imported articles will appear in Starred (in file is named starred.json) and Archived feeds."); | |
329 | ||
330 | print "<p>".__("Paste your starred.json or shared.json into the form below."). "</p>"; | |
331 | ||
332 | print "<iframe id=\"starred_upload_iframe\" | |
333 | name=\"starred_upload_iframe\" onload=\"starredImportComplete(this)\" | |
334 | style=\"width: 400px; height: 100px; display: none;\"></iframe>"; | |
335 | ||
336 | print "<form name=\"starred_form\" style='display : block' target=\"starred_upload_iframe\" | |
337 | enctype=\"multipart/form-data\" method=\"POST\" | |
338 | action=\"backend.php\"> | |
339 | <input id=\"starred_file\" name=\"starred_file\" type=\"file\"> | |
340 | <input type=\"hidden\" name=\"op\" value=\"pluginhandler\"> | |
341 | <input type=\"hidden\" name=\"method\" value=\"import\"> | |
342 | <input type=\"hidden\" name=\"plugin\" value=\"googlereaderimport\"> | |
343 | <button dojoType=\"dijit.form.Button\" onclick=\"return starredImport();\" type=\"submit\">" . | |
344 | __('Import my Starred items') . "</button>"; | |
345 | ||
346 | ||
347 | print "</div>"; #pane | |
348 | } | |
349 | } | |
350 | ?> |