]> git.wh0rd.org - tt-rss.git/blame - plugins/instances/instances.php
add -list-plugins option; about sections to plugins
[tt-rss.git] / plugins / instances / instances.php
CommitLineData
5f0a3741 1<?php
6cbe53c9
AD
2class Instances extends Plugin implements IHandler {
3
4 private $link;
5 private $host;
5f0a3741 6
32532f1c
AD
7 private $status_codes = array(
8 0 => "Connection failed",
9 1 => "Success",
10 2 => "Invalid object received",
11 16 => "Access denied" );
12
7a866114
AD
13 function _about() {
14 return array(1.0,
15 "Support for linking tt-rss instances together and sharing popular feeds.",
16 "fox");
17 }
18
6cbe53c9
AD
19 function __construct($host) {
20 $this->link = $host->get_link();
21 $this->host = $host;
22
23 $host->add_hook($host::HOOK_PREFS_TABS, $this);
24 $host->add_handler("pref-instances", "*", $this);
41b82aa4
AD
25 $host->add_command("get-feeds", "receive popular feeds from linked instances", $this);
26 $host->add_hook($host::HOOK_UPDATE_TASK, $this);
27 }
28
29 function hook_update_task($args) {
30 _debug("Get linked feeds...");
31 $this->get_linked_feeds($this->link);
32 }
33
34 // Status codes:
35 // -1 - never connected
36 // 0 - no data received
37 // 1 - data received successfully
38 // 2 - did not receive valid data
39 // >10 - server error, code + 10 (e.g. 16 means server error 6)
40
41 function get_linked_feeds($link, $instance_id = false) {
42 if ($instance_id)
43 $instance_qpart = "id = '$instance_id' AND ";
44 else
45 $instance_qpart = "";
46
47 if (DB_TYPE == "pgsql") {
48 $date_qpart = "last_connected < NOW() - INTERVAL '6 hours'";
49 } else {
50 $date_qpart = "last_connected < DATE_SUB(NOW(), INTERVAL 6 HOUR)";
51 }
52
53 $result = db_query($link, "SELECT id, access_key, access_url FROM ttrss_linked_instances
54 WHERE $instance_qpart $date_qpart ORDER BY last_connected");
55
56 while ($line = db_fetch_assoc($result)) {
57 $id = $line['id'];
58
59 _debug("Updating: " . $line['access_url'] . " ($id)");
60
61 $fetch_url = $line['access_url'] . '/public.php?op=fbexport';
62 $post_query = 'key=' . $line['access_key'];
63
64 $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query);
65
66 // try doing it the old way
67 if (!$feeds) {
68 $fetch_url = $line['access_url'] . '/backend.php?op=fbexport';
69 $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query);
70 }
71
72 if ($feeds) {
73 $feeds = json_decode($feeds, true);
74
75 if ($feeds) {
76 if ($feeds['error']) {
77 $status = $feeds['error']['code'] + 10;
78
79 // access denied
80 if ($status == 16) {
81 db_query($link, "DELETE FROM ttrss_linked_feeds
82 WHERE instance_id = '$id'");
83 }
84 } else {
85 $status = 1;
86
87 if (count($feeds['feeds']) > 0) {
88
89 db_query($link, "DELETE FROM ttrss_linked_feeds
90 WHERE instance_id = '$id'");
91
92 foreach ($feeds['feeds'] as $feed) {
93 $feed_url = db_escape_string($feed['feed_url']);
94 $title = db_escape_string($feed['title']);
95 $subscribers = db_escape_string($feed['subscribers']);
96 $site_url = db_escape_string($feed['site_url']);
97
98 db_query($link, "INSERT INTO ttrss_linked_feeds
99 (feed_url, site_url, title, subscribers, instance_id, created, updated)
100 VALUES
101 ('$feed_url', '$site_url', '$title', '$subscribers', '$id', NOW(), NOW())");
102 }
103 } else {
104 // received 0 feeds, this might indicate that
105 // the instance on the other hand is rebuilding feedbrowser cache
106 // we will try again later
107
108 // TODO: maybe perform expiration based on updated here?
109 }
110
111 _debug("Processed " . count($feeds['feeds']) . " feeds.");
112 }
113 } else {
114 $status = 2;
115 }
116
117 } else {
118 $status = 0;
119 }
120
121 _debug("Status: $status");
122
123 db_query($link, "UPDATE ttrss_linked_instances SET
124 last_status_out = '$status', last_connected = NOW() WHERE id = '$id'");
125
126 }
127 }
128
129
130 function get_feeds() {
131 $this->get_linked_feeds($this->link, false);
6cbe53c9
AD
132 }
133
134 function get_prefs_js() {
135 return file_get_contents(dirname(__FILE__) . "/instances.js");
136 }
137
138 function hook_prefs_tabs($args) {
139 if ($_SESSION["access_level"] >= 10 || SINGLE_USER_MODE) {
140 ?><div id="instanceConfigTab" dojoType="dijit.layout.ContentPane"
141 href="backend.php?op=pref-instances"
142 title="<?php echo __('Linked') ?>"></div><?php
143 }
144 }
145
8484ce22
AD
146 function csrf_ignore($method) {
147 $csrf_ignored = array("index", "edit");
148
149 return array_search($method, $csrf_ignored) !== false;
150 }
151
17f9d200 152 function before($method) {
6cbe53c9 153 if ($_SESSION["uid"]) {
5f0a3741
AD
154 if ($_SESSION["access_level"] < 10) {
155 print __("Your access level is insufficient to open this tab.");
156 return false;
157 }
158 return true;
159 }
160 return false;
161 }
162
6cbe53c9
AD
163 function after() {
164 return true;
165 }
166
5f0a3741
AD
167 function remove() {
168 $ids = db_escape_string($_REQUEST['ids']);
169
170 db_query($this->link, "DELETE FROM ttrss_linked_instances WHERE
171 id IN ($ids)");
172 }
173
174 function add() {
175 $id = db_escape_string($_REQUEST["id"]);
176 $access_url = db_escape_string($_REQUEST["access_url"]);
177 $access_key = db_escape_string($_REQUEST["access_key"]);
178
179 db_query($this->link, "BEGIN");
180
181 $result = db_query($this->link, "SELECT id FROM ttrss_linked_instances
182 WHERE access_url = '$access_url'");
183
184 if (db_num_rows($result) == 0) {
185 db_query($this->link, "INSERT INTO ttrss_linked_instances
186 (access_url, access_key, last_connected, last_status_in, last_status_out)
187 VALUES
188 ('$access_url', '$access_key', '1970-01-01', -1, -1)");
189
190 }
191
192 db_query($this->link, "COMMIT");
193 }
194
195 function edit() {
196 $id = db_escape_string($_REQUEST["id"]);
197
198 $result = db_query($this->link, "SELECT * FROM ttrss_linked_instances WHERE
199 id = '$id'");
200
201 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$id\">";
202 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-instances\">";
203 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
204
205 print "<div class=\"dlgSec\">".__("Instance")."</div>";
206
207 print "<div class=\"dlgSecCont\">";
208
209 /* URL */
210
211 $access_url = htmlspecialchars(db_fetch_result($result, 0, "access_url"));
212
213 print __("URL:") . " ";
214
215 print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
216 placeHolder=\"".__("Instance URL")."\"
217 regExp='^(http|https)://.*'
218 style=\"font-size : 16px; width: 20em\" name=\"access_url\"
219 value=\"$access_url\">";
220
221 print "<hr/>";
222
223 $access_key = htmlspecialchars(db_fetch_result($result, 0, "access_key"));
224
225 /* Access key */
226
227 print __("Access key:") . " ";
228
229 print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
230 placeHolder=\"".__("Access key")."\" regExp='\w{40}'
231 style=\"width: 20em\" name=\"access_key\" id=\"instance_edit_key\"
232 value=\"$access_key\">";
233
234 print "<p class='insensitive'>" . __("Use one access key for both linked instances.");
235
236 print "</div>";
237
238 print "<div class=\"dlgButtons\">
239 <div style='float : left'>
240 <button dojoType=\"dijit.form.Button\"
241 onclick=\"return dijit.byId('instanceEditDlg').regenKey()\">".
242 __('Generate new key')."</button>
243 </div>
244 <button dojoType=\"dijit.form.Button\"
245 onclick=\"return dijit.byId('instanceEditDlg').execute()\">".
246 __('Save')."</button>
247 <button dojoType=\"dijit.form.Button\"
248 onclick=\"return dijit.byId('instanceEditDlg').hide()\"\">".
249 __('Cancel')."</button></div>";
250
251 }
252
253 function editSave() {
254 $id = db_escape_string($_REQUEST["id"]);
255 $access_url = db_escape_string($_REQUEST["access_url"]);
256 $access_key = db_escape_string($_REQUEST["access_key"]);
257
258 db_query($this->link, "UPDATE ttrss_linked_instances SET
259 access_key = '$access_key', access_url = '$access_url',
260 last_connected = '1970-01-01'
261 WHERE id = '$id'");
262
263 }
264
265 function index() {
266
267 if (!function_exists('curl_init')) {
268 print "<div style='padding : 1em'>";
269 print_error("This functionality requires CURL functions. Please enable CURL in your PHP configuration (you might also want to disable open_basedir in php.ini) and reload this page.");
270 print "</div>";
271 }
272
273 print "<div id=\"pref-instance-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
274 print "<div id=\"pref-instance-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
275
276 print "<div id=\"pref-instance-toolbar\" dojoType=\"dijit.Toolbar\">";
277
278 $sort = db_escape_string($_REQUEST["sort"]);
279
280 if (!$sort || $sort == "undefined") {
281 $sort = "access_url";
282 }
283
284 print "<div dojoType=\"dijit.form.DropDownButton\">".
285 "<span>" . __('Select')."</span>";
286 print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
287 print "<div onclick=\"selectTableRows('prefInstanceList', 'all')\"
288 dojoType=\"dijit.MenuItem\">".__('All')."</div>";
289 print "<div onclick=\"selectTableRows('prefInstanceList', 'none')\"
290 dojoType=\"dijit.MenuItem\">".__('None')."</div>";
291 print "</div></div>";
292
293 print "<button dojoType=\"dijit.form.Button\" onclick=\"addInstance()\">".__('Link instance')."</button>";
294 print "<button dojoType=\"dijit.form.Button\" onclick=\"editSelectedInstance()\">".__('Edit')."</button>";
295 print "<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedInstances()\">".__('Remove')."</button>";
296
297 print "</div>"; #toolbar
298
299 $result = db_query($this->link, "SELECT *,
300 (SELECT COUNT(*) FROM ttrss_linked_feeds
301 WHERE instance_id = ttrss_linked_instances.id) AS num_feeds
302 FROM ttrss_linked_instances
303 ORDER BY $sort");
304
305 print "<p class=\"insensitive\" style='margin-left : 1em;'>" . __("You can connect other instances of Tiny Tiny RSS to this one to share Popular feeds. Link to this instance of Tiny Tiny RSS by using this URL:");
306
307 print " <a href=\"#\" onclick=\"alert('".htmlspecialchars(get_self_url_prefix())."')\">(display url)</a>";
308
309 print "<p><table width='100%' id='prefInstanceList' class='prefInstanceList' cellspacing='0'>";
310
311 print "<tr class=\"title\">
312 <td align='center' width=\"5%\">&nbsp;</td>
313 <td width=''><a href=\"#\" onclick=\"updateInstanceList('access_url')\">".__('Instance URL')."</a></td>
314 <td width='20%'><a href=\"#\" onclick=\"updateInstanceList('access_key')\">".__('Access key')."</a></td>
315 <td width='10%'><a href=\"#\" onclick=\"updateUsersList('last_connected')\">".__('Last connected')."</a></td>
32532f1c 316 <td width='10%'><a href=\"#\" onclick=\"updateUsersList('last_status_out')\">".__('Status')."</a></td>
5f0a3741
AD
317 <td width='10%'><a href=\"#\" onclick=\"updateUsersList('num_feeds')\">".__('Stored feeds')."</a></td>
318 </tr>";
319
320 $lnum = 0;
321
322 while ($line = db_fetch_assoc($result)) {
323 $class = ($lnum % 2) ? "even" : "odd";
324
325 $id = $line['id'];
326 $this_row_id = "id=\"LIRR-$id\"";
327
328 $line["last_connected"] = make_local_datetime($this->link, $line["last_connected"], false);
329
330 print "<tr class=\"$class\" $this_row_id>";
331
332 print "<td align='center'><input onclick='toggleSelectRow(this);'
333 type=\"checkbox\" id=\"LICHK-$id\"></td>";
334
335 $onclick = "onclick='editInstance($id, event)' title='".__('Click to edit')."'";
336
337 $access_key = mb_substr($line['access_key'], 0, 4) . '...' .
338 mb_substr($line['access_key'], -4);
339
340 print "<td $onclick>" . htmlspecialchars($line['access_url']) . "</td>";
341 print "<td $onclick>" . htmlspecialchars($access_key) . "</td>";
342 print "<td $onclick>" . htmlspecialchars($line['last_connected']) . "</td>";
32532f1c 343 print "<td $onclick>" . $this->status_codes[$line['last_status_out']] . "</td>";
5f0a3741
AD
344 print "<td $onclick>" . htmlspecialchars($line['num_feeds']) . "</td>";
345
346 print "</tr>";
347
348 ++$lnum;
349 }
350
351 print "</table>";
352
353 print "</div>"; #pane
6065f3ad
AD
354
355 global $pluginhost;
356 $pluginhost->run_hooks($pluginhost::HOOK_PREFS_TAB,
357 "hook_prefs_tab", "prefInstances");
358
5f0a3741
AD
359 print "</div>"; #container
360
361 }
6cbe53c9 362
5f0a3741
AD
363}
364?>
6cbe53c9 365