]> git.wh0rd.org Git - tt-rss.git/blob - classes/pref/feeds.php
Merge pull request #179 from nottwo/$link-cleanup
[tt-rss.git] / classes / pref / feeds.php
1 <?php
2 class Pref_Feeds extends Handler_Protected {
3
4         function csrf_ignore($method) {
5                 $csrf_ignored = array("index", "getfeedtree", "add", "editcats", "editfeed",
6                         "savefeedorder", "uploadicon", "feedswitherrors", "inactivefeeds",
7                         "batchsubscribe");
8
9                 return array_search($method, $csrf_ignored) !== false;
10         }
11
12         function batch_edit_cbox($elem, $label = false) {
13                 print "<input type=\"checkbox\" title=\"".__("Check to enable field")."\"
14                         onchange=\"dijit.byId('feedEditDlg').toggleField(this, '$elem', '$label')\">";
15         }
16
17         function renamecat() {
18                 $title = $this->dbh->escape_string($_REQUEST['title']);
19                 $id = $this->dbh->escape_string($_REQUEST['id']);
20
21                 if ($title) {
22                         $this->dbh->query("UPDATE ttrss_feed_categories SET
23                                 title = '$title' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]);
24                 }
25                 return;
26         }
27
28         private function get_category_items($cat_id) {
29
30                 if ($_REQUEST['mode'] != 2)
31                         $search = $_SESSION["prefs_feed_search"];
32                 else
33                         $search = "";
34
35                 if ($search) $search_qpart = " AND LOWER(title) LIKE LOWER('%$search%')";
36
37                 // first one is set by API
38                 $show_empty_cats = $_REQUEST['force_show_empty'] ||
39                         ($_REQUEST['mode'] != 2 && !$search &&
40                                 get_pref('_PREFS_SHOW_EMPTY_CATS'));
41
42                 $items = array();
43
44                 $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories
45                                 WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat = '$cat_id' ORDER BY order_id, title");
46
47                 while ($line = $this->dbh->fetch_assoc($result)) {
48
49                         $cat = array();
50                         $cat['id'] = 'CAT:' . $line['id'];
51                         $cat['bare_id'] = (int)$line['id'];
52                         $cat['name'] = $line['title'];
53                         $cat['items'] = array();
54                         $cat['checkbox'] = false;
55                         $cat['type'] = 'category';
56                         $cat['unread'] = 0;
57                         $cat['child_unread'] = 0;
58
59                         $cat['items'] = $this->get_category_items($line['id']);
60
61                         $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
62
63                         if (count($cat['items']) > 0 || $show_empty_cats)
64                                 array_push($items, $cat);
65
66                 }
67
68                 $feed_result = $this->dbh->query("SELECT id, title, last_error,
69                         ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
70                         FROM ttrss_feeds
71                         WHERE cat_id = '$cat_id' AND owner_uid = ".$_SESSION["uid"].
72                         "$search_qpart ORDER BY order_id, title");
73
74                 while ($feed_line = $this->dbh->fetch_assoc($feed_result)) {
75                         $feed = array();
76                         $feed['id'] = 'FEED:' . $feed_line['id'];
77                         $feed['bare_id'] = (int)$feed_line['id'];
78                         $feed['name'] = $feed_line['title'];
79                         $feed['checkbox'] = false;
80                         $feed['unread'] = 0;
81                         $feed['error'] = $feed_line['last_error'];
82                         $feed['icon'] = getFeedIcon($feed_line['id']);
83                         $feed['param'] = make_local_datetime(
84                                 $feed_line['last_updated'], true);
85
86                         array_push($items, $feed);
87                 }
88
89                 return $items;
90         }
91
92         function getfeedtree() {
93                 print json_encode($this->makefeedtree());
94         }
95
96         function makefeedtree() {
97
98                 if ($_REQUEST['mode'] != 2)
99                         $search = $_SESSION["prefs_feed_search"];
100                 else
101                         $search = "";
102
103                 if ($search) $search_qpart = " AND LOWER(title) LIKE LOWER('%$search%')";
104
105                 $root = array();
106                 $root['id'] = 'root';
107                 $root['name'] = __('Feeds');
108                 $root['items'] = array();
109                 $root['type'] = 'category';
110
111                 $enable_cats = get_pref('ENABLE_FEED_CATS');
112
113                 if ($_REQUEST['mode'] == 2) {
114
115                         if ($enable_cats) {
116                                 $cat = $this->feedlist_init_cat(-1);
117                         } else {
118                                 $cat['items'] = array();
119                         }
120
121                         foreach (array(-4, -3, -1, -2, 0, -6) as $i) {
122                                 array_push($cat['items'], $this->feedlist_init_feed($i));
123                         }
124
125                         /* Plugin feeds for -1 */
126
127                         $feeds = PluginHost::getInstance()->get_feeds(-1);
128
129                         if ($feeds) {
130                                 foreach ($feeds as $feed) {
131                                         $feed_id = PluginHost::pfeed_to_feed_id($feed['id']);
132
133                                         $item = array();
134                                         $item['id'] = 'FEED:' . $feed_id;
135                                         $item['bare_id'] = (int)$feed_id;
136                                         $item['name'] = $feed['title'];
137                                         $item['checkbox'] = false;
138                                         $item['error'] = '';
139                                         $item['icon'] = $feed['icon'];
140
141                                         $item['param'] = '';
142                                         $item['unread'] = 0; //$feed['sender']->get_unread($feed['id']);
143                                         $item['type'] = 'feed';
144
145                                         array_push($cat['items'], $item);
146                                 }
147                         }
148
149                         if ($enable_cats) {
150                                 array_push($root['items'], $cat);
151                         } else {
152                                 $root['items'] = array_merge($root['items'], $cat['items']);
153                         }
154
155                         $result = $this->dbh->query("SELECT * FROM
156                                 ttrss_labels2 WHERE owner_uid = ".$_SESSION['uid']." ORDER by caption");
157
158                         if ($this->dbh->num_rows($result) > 0) {
159
160                                 if (get_pref('ENABLE_FEED_CATS')) {
161                                         $cat = $this->feedlist_init_cat(-2);
162                                 } else {
163                                         $cat['items'] = array();
164                                 }
165
166                                 while ($line = $this->dbh->fetch_assoc($result)) {
167
168                                         $label_id = label_to_feed_id($line['id']);
169
170                                         $feed = $this->feedlist_init_feed($label_id, false, 0);
171
172                                         $feed['fg_color'] = $line['fg_color'];
173                                         $feed['bg_color'] = $line['bg_color'];
174
175                                         array_push($cat['items'], $feed);
176                                 }
177
178                                 if ($enable_cats) {
179                                         array_push($root['items'], $cat);
180                                 } else {
181                                         $root['items'] = array_merge($root['items'], $cat['items']);
182                                 }
183                         }
184                 }
185
186                 if ($enable_cats) {
187                         $show_empty_cats = $_REQUEST['force_show_empty'] ||
188                                 ($_REQUEST['mode'] != 2 && !$search &&
189                                 get_pref('_PREFS_SHOW_EMPTY_CATS'));
190
191                         $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories
192                                 WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat IS NULL ORDER BY order_id, title");
193
194                         while ($line = $this->dbh->fetch_assoc($result)) {
195                                 $cat = array();
196                                 $cat['id'] = 'CAT:' . $line['id'];
197                                 $cat['bare_id'] = (int)$line['id'];
198                                 $cat['name'] = $line['title'];
199                                 $cat['items'] = array();
200                                 $cat['checkbox'] = false;
201                                 $cat['type'] = 'category';
202                                 $cat['unread'] = 0;
203                                 $cat['child_unread'] = 0;
204
205                                 $cat['items'] = $this->get_category_items($line['id']);
206
207                                 $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
208
209                                 if (count($cat['items']) > 0 || $show_empty_cats)
210                                         array_push($root['items'], $cat);
211
212                                 $root['param'] += count($cat['items']);
213                         }
214
215                         /* Uncategorized is a special case */
216
217                         $cat = array();
218                         $cat['id'] = 'CAT:0';
219                         $cat['bare_id'] = 0;
220                         $cat['name'] = __("Uncategorized");
221                         $cat['items'] = array();
222                         $cat['type'] = 'category';
223                         $cat['checkbox'] = false;
224                         $cat['unread'] = 0;
225                         $cat['child_unread'] = 0;
226
227                         $feed_result = $this->dbh->query("SELECT id, title,last_error,
228                                 ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
229                                 FROM ttrss_feeds
230                                 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"].
231                                 "$search_qpart ORDER BY order_id, title");
232
233                         while ($feed_line = $this->dbh->fetch_assoc($feed_result)) {
234                                 $feed = array();
235                                 $feed['id'] = 'FEED:' . $feed_line['id'];
236                                 $feed['bare_id'] = (int)$feed_line['id'];
237                                 $feed['name'] = $feed_line['title'];
238                                 $feed['checkbox'] = false;
239                                 $feed['error'] = $feed_line['last_error'];
240                                 $feed['icon'] = getFeedIcon($feed_line['id']);
241                                 $feed['param'] = make_local_datetime(
242                                         $feed_line['last_updated'], true);
243                                 $feed['unread'] = 0;
244                                 $feed['type'] = 'feed';
245
246                                 array_push($cat['items'], $feed);
247                         }
248
249                         $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
250
251                         if (count($cat['items']) > 0 || $show_empty_cats)
252                                 array_push($root['items'], $cat);
253
254                         $root['param'] += count($cat['items']);
255                         $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
256
257                 } else {
258                         $feed_result = $this->dbh->query("SELECT id, title, last_error,
259                                 ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
260                                 FROM ttrss_feeds
261                                 WHERE owner_uid = ".$_SESSION["uid"].
262                                 "$search_qpart ORDER BY order_id, title");
263
264                         while ($feed_line = $this->dbh->fetch_assoc($feed_result)) {
265                                 $feed = array();
266                                 $feed['id'] = 'FEED:' . $feed_line['id'];
267                                 $feed['bare_id'] = (int)$feed_line['id'];
268                                 $feed['name'] = $feed_line['title'];
269                                 $feed['checkbox'] = false;
270                                 $feed['error'] = $feed_line['last_error'];
271                                 $feed['icon'] = getFeedIcon($feed_line['id']);
272                                 $feed['param'] = make_local_datetime(
273                                         $feed_line['last_updated'], true);
274                                 $feed['unread'] = 0;
275                                 $feed['type'] = 'feed';
276
277                                 array_push($root['items'], $feed);
278                         }
279
280                         $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
281                 }
282
283                 $fl = array();
284                 $fl['identifier'] = 'id';
285                 $fl['label'] = 'name';
286
287                 if ($_REQUEST['mode'] != 2) {
288                         $fl['items'] = array($root);
289                 } else {
290                         $fl['items'] =& $root['items'];
291                 }
292
293                 return $fl;
294         }
295
296         function catsortreset() {
297                 $this->dbh->query("UPDATE ttrss_feed_categories
298                                 SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]);
299                 return;
300         }
301
302         function feedsortreset() {
303                 $this->dbh->query("UPDATE ttrss_feeds
304                                 SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]);
305                 return;
306         }
307
308         function togglehiddenfeedcats() {
309                 set_pref('_PREFS_SHOW_EMPTY_CATS',
310                         (get_pref('_PREFS_SHOW_EMPTY_CATS') ? 'false' : 'true'));
311         }
312
313         private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) {
314                 $debug = isset($_REQUEST["debug"]);
315
316                 $prefix = "";
317                 for ($i = 0; $i < $nest_level; $i++)
318                         $prefix .= "   ";
319
320                 if ($debug) _debug("$prefix C: $item_id P: $parent_id");
321
322                 $bare_item_id = substr($item_id, strpos($item_id, ':')+1);
323
324                 if ($item_id != 'root') {
325                         if ($parent_id && $parent_id != 'root') {
326                                 $parent_bare_id = substr($parent_id, strpos($parent_id, ':')+1);
327                                 $parent_qpart = $this->dbh->escape_string($parent_bare_id);
328                         } else {
329                                 $parent_qpart = 'NULL';
330                         }
331
332                         $this->dbh->query("UPDATE ttrss_feed_categories
333                                 SET parent_cat = $parent_qpart WHERE id = '$bare_item_id' AND
334                                 owner_uid = " . $_SESSION["uid"]);
335                 }
336
337                 $order_id = 0;
338
339                 $cat = $data_map[$item_id];
340
341                 if ($cat && is_array($cat)) {
342                         foreach ($cat as $item) {
343                                 $id = $item['_reference'];
344                                 $bare_id = substr($id, strpos($id, ':')+1);
345
346                                 if ($debug) _debug("$prefix [$order_id] $id/$bare_id");
347
348                                 if ($item['_reference']) {
349
350                                         if (strpos($id, "FEED") === 0) {
351
352                                                 $cat_id = ($item_id != "root") ?
353                                                         $this->dbh->escape_string($bare_item_id) : "NULL";
354
355                                                 $cat_qpart = ($cat_id != 0) ? "cat_id = '$cat_id'" :
356                                                         "cat_id = NULL";
357
358                                                 $this->dbh->query("UPDATE ttrss_feeds
359                                                         SET order_id = $order_id, $cat_qpart
360                                                         WHERE id = '$bare_id' AND
361                                                                 owner_uid = " . $_SESSION["uid"]);
362
363                                         } else if (strpos($id, "CAT:") === 0) {
364                                                 $this->process_category_order($data_map, $item['_reference'], $item_id,
365                                                         $nest_level+1);
366
367                                                 if ($item_id != 'root') {
368                                                         $parent_qpart = $this->dbh->escape_string($bare_id);
369                                                 } else {
370                                                         $parent_qpart = 'NULL';
371                                                 }
372
373                                                 $this->dbh->query("UPDATE ttrss_feed_categories
374                                                                 SET order_id = '$order_id' WHERE id = '$bare_id' AND
375                                                                 owner_uid = " . $_SESSION["uid"]);
376                                         }
377                                 }
378
379                                 ++$order_id;
380                         }
381                 }
382         }
383
384         function savefeedorder() {
385                 $data = json_decode($_POST['payload'], true);
386
387                 #file_put_contents("/tmp/saveorder.json", $_POST['payload']);
388                 #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true);
389
390                 if (!is_array($data['items']))
391                         $data['items'] = json_decode($data['items'], true);
392
393 #               print_r($data['items']);
394
395                 if (is_array($data) && is_array($data['items'])) {
396                         $cat_order_id = 0;
397
398                         $data_map = array();
399                         $root_item = false;
400
401                         foreach ($data['items'] as $item) {
402
403 #                               if ($item['id'] != 'root') {
404                                         if (is_array($item['items'])) {
405                                                 if (isset($item['items']['_reference'])) {
406                                                         $data_map[$item['id']] = array($item['items']);
407                                                 } else {
408                                                         $data_map[$item['id']] =& $item['items'];
409                                                 }
410                                         }
411                                 if ($item['id'] == 'root') {
412                                         $root_item = $item['id'];
413                                 }
414                         }
415
416                         $this->process_category_order($data_map, $root_item);
417
418                         /* foreach ($data['items'][0]['items'] as $item) {
419                                 $id = $item['_reference'];
420                                 $bare_id = substr($id, strpos($id, ':')+1);
421
422                                 ++$cat_order_id;
423
424                                 if ($bare_id > 0) {
425                                         $this->dbh->query("UPDATE ttrss_feed_categories
426                                                 SET order_id = '$cat_order_id' WHERE id = '$bare_id' AND
427                                                 owner_uid = " . $_SESSION["uid"]);
428                                 }
429
430                                 $feed_order_id = 0;
431
432                                 if (is_array($data_map[$id])) {
433                                         foreach ($data_map[$id] as $feed) {
434                                                 $id = $feed['_reference'];
435                                                 $feed_id = substr($id, strpos($id, ':')+1);
436
437                                                 if ($bare_id != 0)
438                                                         $cat_query = "cat_id = '$bare_id'";
439                                                 else
440                                                         $cat_query = "cat_id = NULL";
441
442                                                 $this->dbh->query("UPDATE ttrss_feeds
443                                                         SET order_id = '$feed_order_id',
444                                                         $cat_query
445                                                         WHERE id = '$feed_id' AND
446                                                                 owner_uid = " . $_SESSION["uid"]);
447
448                                                 ++$feed_order_id;
449                                         }
450                                 }
451                         } */
452                 }
453
454                 return;
455         }
456
457         function removeicon() {
458                 $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
459
460                 $result = $this->dbh->query("SELECT id FROM ttrss_feeds
461                         WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]);
462
463                 if ($this->dbh->num_rows($result) != 0) {
464                         @unlink(ICONS_DIR . "/$feed_id.ico");
465
466                         $this->dbh->query("UPDATE ttrss_feeds SET favicon_avg_color = NULL
467                                 where id = '$feed_id'");
468                 }
469
470                 return;
471         }
472
473         function uploadicon() {
474                 header("Content-type: text/html");
475
476                 $tmp_file = false;
477
478                 if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) {
479                         $tmp_file = tempnam(CACHE_DIR . '/upload', 'icon');
480
481                         $result = move_uploaded_file($_FILES['icon_file']['tmp_name'],
482                                 $tmp_file);
483
484                         if (!$result) {
485                                 return;
486                         }
487                 } else {
488                         return;
489                 }
490
491                 $icon_file = $tmp_file;
492                 $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
493
494                 if (is_file($icon_file) && $feed_id) {
495                         if (filesize($icon_file) < 20000) {
496
497                                 $result = $this->dbh->query("SELECT id FROM ttrss_feeds
498                                         WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]);
499
500                                 if ($this->dbh->num_rows($result) != 0) {
501                                         @unlink(ICONS_DIR . "/$feed_id.ico");
502                                         if (rename($icon_file, ICONS_DIR . "/$feed_id.ico")) {
503                                                 $this->dbh->query("UPDATE ttrss_feeds SET
504                                                         favicon_avg_color = ''
505                                                         WHERE id = '$feed_id'");
506
507                                                 $rc = 0;
508                                         }
509                                 } else {
510                                         $rc = 2;
511                                 }
512                         } else {
513                                 $rc = 1;
514                         }
515                 } else {
516                         $rc = 2;
517                 }
518
519                 @unlink($icon_file);
520
521                 print "<script type=\"text/javascript\">";
522                 print "parent.uploadIconHandler($rc);";
523                 print "</script>";
524                 return;
525         }
526
527         function editfeed() {
528                 global $purge_intervals;
529                 global $update_intervals;
530
531                 $feed_id = $this->dbh->escape_string($_REQUEST["id"]);
532
533                 $result = $this->dbh->query(
534                         "SELECT * FROM ttrss_feeds WHERE id = '$feed_id' AND
535                                 owner_uid = " . $_SESSION["uid"]);
536
537                 $auth_pass_encrypted = sql_bool_to_bool($this->dbh->fetch_result($result, 0,
538                         "auth_pass_encrypted"));
539
540                 $title = htmlspecialchars($this->dbh->fetch_result($result,
541                         0, "title"));
542
543                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$feed_id\">";
544                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
545                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
546
547                 print "<div class=\"dlgSec\">".__("Feed")."</div>";
548                 print "<div class=\"dlgSecCont\">";
549
550                 /* Title */
551
552                 print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
553                         placeHolder=\"".__("Feed Title")."\"
554                         style=\"font-size : 16px; width: 20em\" name=\"title\" value=\"$title\">";
555
556                 /* Feed URL */
557
558                 $feed_url = $this->dbh->fetch_result($result, 0, "feed_url");
559                 $feed_url = htmlspecialchars($this->dbh->fetch_result($result,
560                         0, "feed_url"));
561
562                 print "<hr/>";
563
564                 print __('URL:') . " ";
565                 print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
566                         placeHolder=\"".__("Feed URL")."\"
567                         regExp='^(http|https)://.*' style=\"width : 20em\"
568                         name=\"feed_url\" value=\"$feed_url\">";
569
570                 $last_error = $this->dbh->fetch_result($result, 0, "last_error");
571
572                 if ($last_error) {
573                         print "&nbsp;<span title=\"".htmlspecialchars($last_error)."\"
574                                 class=\"feed_error\">(error)</span>";
575
576                 }
577
578                 /* Category */
579
580                 if (get_pref('ENABLE_FEED_CATS')) {
581
582                         $cat_id = $this->dbh->fetch_result($result, 0, "cat_id");
583
584                         print "<hr/>";
585
586                         print __('Place in category:') . " ";
587
588                         print_feed_cat_select("cat_id", $cat_id,
589                                 'dojoType="dijit.form.Select"');
590                 }
591
592                 print "</div>";
593
594                 print "<div class=\"dlgSec\">".__("Update")."</div>";
595                 print "<div class=\"dlgSecCont\">";
596
597                 /* Update Interval */
598
599                 $update_interval = $this->dbh->fetch_result($result, 0, "update_interval");
600
601                 print_select_hash("update_interval", $update_interval, $update_intervals,
602                         'dojoType="dijit.form.Select"');
603
604                 /* Purge intl */
605
606                 $purge_interval = $this->dbh->fetch_result($result, 0, "purge_interval");
607
608                 print "<hr/>";
609                 print __('Article purging:') . " ";
610
611                 print_select_hash("purge_interval", $purge_interval, $purge_intervals,
612                         'dojoType="dijit.form.Select" ' .
613                                 ((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"'));
614
615                 print "</div>";
616                 print "<div class=\"dlgSec\">".__("Authentication")."</div>";
617                 print "<div class=\"dlgSecCont\">";
618
619                 $auth_login = htmlspecialchars($this->dbh->fetch_result($result, 0, "auth_login"));
620
621                 print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\"
622                         placeHolder=\"".__("Login")."\"
623                         name=\"auth_login\" value=\"$auth_login\"><hr/>";
624
625                 $auth_pass = $this->dbh->fetch_result($result, 0, "auth_pass");
626
627                 if ($auth_pass_encrypted) {
628                         require_once "crypt.php";
629                         $auth_pass = decrypt_string($auth_pass);
630                 }
631
632                 $auth_pass = htmlspecialchars($auth_pass);
633
634                 print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
635                         placeHolder=\"".__("Password")."\"
636                         value=\"$auth_pass\">";
637
638                 print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedEditDlg_login\" position=\"below\">
639                         ".__('<b>Hint:</b> you need to fill in your login information if your feed requires authentication, except for Twitter feeds.')."
640                         </div>";
641
642                 print "</div>";
643                 print "<div class=\"dlgSec\">".__("Options")."</div>";
644                 print "<div class=\"dlgSecCont\">";
645
646                 $private = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "private"));
647
648                 if ($private) {
649                         $checked = "checked=\"1\"";
650                 } else {
651                         $checked = "";
652                 }
653
654                 print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"private\" id=\"private\"
655                         $checked>&nbsp;<label for=\"private\">".__('Hide from Popular feeds')."</label>";
656
657                 $include_in_digest = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "include_in_digest"));
658
659                 if ($include_in_digest) {
660                         $checked = "checked=\"1\"";
661                 } else {
662                         $checked = "";
663                 }
664
665                 print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"include_in_digest\"
666                         name=\"include_in_digest\"
667                         $checked>&nbsp;<label for=\"include_in_digest\">".__('Include in e-mail digest')."</label>";
668
669
670                 $always_display_enclosures = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "always_display_enclosures"));
671
672                 if ($always_display_enclosures) {
673                         $checked = "checked";
674                 } else {
675                         $checked = "";
676                 }
677
678                 print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"always_display_enclosures\"
679                         name=\"always_display_enclosures\"
680                         $checked>&nbsp;<label for=\"always_display_enclosures\">".__('Always display image attachments')."</label>";
681
682                 $hide_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "hide_images"));
683
684                 if ($hide_images) {
685                         $checked = "checked=\"1\"";
686                 } else {
687                         $checked = "";
688                 }
689
690                 print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"hide_images\"
691                 name=\"hide_images\"
692                         $checked>&nbsp;<label for=\"hide_images\">".
693                 __('Do not embed images')."</label>";
694
695                 $cache_images = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "cache_images"));
696
697                 if ($cache_images) {
698                         $checked = "checked=\"1\"";
699                 } else {
700                         $checked = "";
701                 }
702
703                 print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"cache_images\"
704                 name=\"cache_images\"
705                         $checked>&nbsp;<label for=\"cache_images\">".
706                 __('Cache images locally')."</label>";
707
708                 $mark_unread_on_update = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "mark_unread_on_update"));
709
710                 if ($mark_unread_on_update) {
711                         $checked = "checked";
712                 } else {
713                         $checked = "";
714                 }
715
716                 print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"mark_unread_on_update\"
717                         name=\"mark_unread_on_update\"
718                         $checked>&nbsp;<label for=\"mark_unread_on_update\">".__('Mark updated articles as unread')."</label>";
719
720                 print "</div>";
721
722                 /* Icon */
723
724                 print "<div class=\"dlgSec\">".__("Icon")."</div>";
725                 print "<div class=\"dlgSecCont\">";
726
727                 print "<iframe name=\"icon_upload_iframe\"
728                         style=\"width: 400px; height: 100px; display: none;\"></iframe>";
729
730                 print "<form style='display : block' target=\"icon_upload_iframe\"
731                         enctype=\"multipart/form-data\" method=\"POST\"
732                         action=\"backend.php\">
733                         <input id=\"icon_file\" size=\"10\" name=\"icon_file\" type=\"file\">
734                         <input type=\"hidden\" name=\"op\" value=\"pref-feeds\">
735                         <input type=\"hidden\" name=\"feed_id\" value=\"$feed_id\">
736                         <input type=\"hidden\" name=\"method\" value=\"uploadicon\">
737                         <button dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\"
738                                 type=\"submit\">".__('Replace')."</button>
739                         <button dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\"
740                                 type=\"submit\">".__('Remove')."</button>
741                         </form>";
742
743                 print "</div>";
744
745                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED,
746                         "hook_prefs_edit_feed", $feed_id);
747
748                 $title = htmlspecialchars($title, ENT_QUOTES);
749
750                 print "<div class='dlgButtons'>
751                         <div style=\"float : left\">
752                         <button dojoType=\"dijit.form.Button\" onclick='return unsubscribeFeed($feed_id, \"$title\")'>".
753                                 __('Unsubscribe')."</button>";
754
755                 if (PUBSUBHUBBUB_ENABLED) {
756                         $pubsub_state = $this->dbh->fetch_result($result, 0, "pubsub_state");
757                         $pubsub_btn_disabled = ($pubsub_state == 2) ? "" : "disabled=\"1\"";
758
759                         print "<button dojoType=\"dijit.form.Button\" id=\"pubsubReset_Btn\" $pubsub_btn_disabled
760                                         onclick='return resetPubSub($feed_id, \"$title\")'>".__('Resubscribe to push updates').
761                                         "</button>";
762                 }
763
764                 print "</div>";
765
766                 print "<div dojoType=\"dijit.Tooltip\" connectId=\"pubsubReset_Btn\" position=\"below\">".
767                         __('Resets PubSubHubbub subscription status for push-enabled feeds.')."</div>";
768
769                 print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button>
770                         <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button>
771                 </div>";
772
773                 return;
774         }
775
776         function editfeeds() {
777                 global $purge_intervals;
778                 global $update_intervals;
779
780                 $feed_ids = $this->dbh->escape_string($_REQUEST["ids"]);
781
782                 print_notice("Enable the options you wish to apply using checkboxes on the right:");
783
784                 print "<p>";
785
786                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"ids\" value=\"$feed_ids\">";
787                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
788                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchEditSave\">";
789
790                 print "<div class=\"dlgSec\">".__("Feed")."</div>";
791                 print "<div class=\"dlgSecCont\">";
792
793                 /* Title */
794
795                 print "<input dojoType=\"dijit.form.ValidationTextBox\"
796                         disabled=\"1\" style=\"font-size : 16px; width : 20em;\" required=\"1\"
797                         name=\"title\" value=\"$title\">";
798
799                 $this->batch_edit_cbox("title");
800
801                 /* Feed URL */
802
803                 print "<br/>";
804
805                 print __('URL:') . " ";
806                 print "<input dojoType=\"dijit.form.ValidationTextBox\" disabled=\"1\"
807                         required=\"1\" regExp='^(http|https)://.*' style=\"width : 20em\"
808                         name=\"feed_url\" value=\"$feed_url\">";
809
810                 $this->batch_edit_cbox("feed_url");
811
812                 /* Category */
813
814                 if (get_pref('ENABLE_FEED_CATS')) {
815
816                         print "<br/>";
817
818                         print __('Place in category:') . " ";
819
820                         print_feed_cat_select("cat_id", $cat_id,
821                                 'disabled="1" dojoType="dijit.form.Select"');
822
823                         $this->batch_edit_cbox("cat_id");
824
825                 }
826
827                 print "</div>";
828
829                 print "<div class=\"dlgSec\">".__("Update")."</div>";
830                 print "<div class=\"dlgSecCont\">";
831
832                 /* Update Interval */
833
834                 print_select_hash("update_interval", $update_interval, $update_intervals,
835                         'disabled="1" dojoType="dijit.form.Select"');
836
837                 $this->batch_edit_cbox("update_interval");
838
839                 /* Purge intl */
840
841                 if (FORCE_ARTICLE_PURGE == 0) {
842
843                         print "<br/>";
844
845                         print __('Article purging:') . " ";
846
847                         print_select_hash("purge_interval", $purge_interval, $purge_intervals,
848                                 'disabled="1" dojoType="dijit.form.Select"');
849
850                         $this->batch_edit_cbox("purge_interval");
851                 }
852
853                 print "</div>";
854                 print "<div class=\"dlgSec\">".__("Authentication")."</div>";
855                 print "<div class=\"dlgSecCont\">";
856
857                 print "<input dojoType=\"dijit.form.TextBox\"
858                         placeHolder=\"".__("Login")."\" disabled=\"1\"
859                         name=\"auth_login\" value=\"$auth_login\">";
860
861                 $this->batch_edit_cbox("auth_login");
862
863                 print "<br/><input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
864                         placeHolder=\"".__("Password")."\" disabled=\"1\"
865                         value=\"$auth_pass\">";
866
867                 $this->batch_edit_cbox("auth_pass");
868
869                 print "</div>";
870                 print "<div class=\"dlgSec\">".__("Options")."</div>";
871                 print "<div class=\"dlgSecCont\">";
872
873                 print "<input disabled=\"1\" type=\"checkbox\" name=\"private\" id=\"private\"
874                         dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"private_l\" class='insensitive' for=\"private\">".__('Hide from Popular feeds')."</label>";
875
876                 print "&nbsp;"; $this->batch_edit_cbox("private", "private_l");
877
878                 print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"include_in_digest\"
879                         name=\"include_in_digest\"
880                         dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"include_in_digest_l\" class='insensitive' for=\"include_in_digest\">".__('Include in e-mail digest')."</label>";
881
882                 print "&nbsp;"; $this->batch_edit_cbox("include_in_digest", "include_in_digest_l");
883
884                 print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"always_display_enclosures\"
885                         name=\"always_display_enclosures\"
886                         dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"always_display_enclosures_l\" class='insensitive' for=\"always_display_enclosures\">".__('Always display image attachments')."</label>";
887
888                 print "&nbsp;"; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l");
889
890                 print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"hide_images\"
891                         name=\"hide_images\"
892                         dojoType=\"dijit.form.CheckBox\">&nbsp;<label class='insensitive' id=\"hide_images_l\"
893                         for=\"hide_images\">".
894                 __('Do not embed images')."</label>";
895
896                 print "&nbsp;"; $this->batch_edit_cbox("hide_images", "hide_images_l");
897
898                 print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"cache_images\"
899                         name=\"cache_images\"
900                         dojoType=\"dijit.form.CheckBox\">&nbsp;<label class='insensitive' id=\"cache_images_l\"
901                         for=\"cache_images\">".
902                 __('Cache images locally')."</label>";
903
904                 print "&nbsp;"; $this->batch_edit_cbox("cache_images", "cache_images_l");
905
906                 print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"mark_unread_on_update\"
907                         name=\"mark_unread_on_update\"
908                         dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"mark_unread_on_update_l\" class='insensitive' for=\"mark_unread_on_update\">".__('Mark updated articles as unread')."</label>";
909
910                 print "&nbsp;"; $this->batch_edit_cbox("mark_unread_on_update", "mark_unread_on_update_l");
911
912                 print "</div>";
913
914                 print "<div class='dlgButtons'>
915                         <button dojoType=\"dijit.form.Button\"
916                                 onclick=\"return dijit.byId('feedEditDlg').execute()\">".
917                                 __('Save')."</button>
918                         <button dojoType=\"dijit.form.Button\"
919                         onclick=\"return dijit.byId('feedEditDlg').hide()\">".
920                                 __('Cancel')."</button>
921                         </div>";
922
923                 return;
924         }
925
926         function batchEditSave() {
927                 return $this->editsaveops(true);
928         }
929
930         function editSave() {
931                 return $this->editsaveops(false);
932         }
933
934         function editsaveops($batch) {
935
936                 $feed_title = $this->dbh->escape_string(trim($_POST["title"]));
937                 $feed_link = $this->dbh->escape_string(trim($_POST["feed_url"]));
938                 $upd_intl = (int) $this->dbh->escape_string($_POST["update_interval"]);
939                 $purge_intl = (int) $this->dbh->escape_string($_POST["purge_interval"]);
940                 $feed_id = (int) $this->dbh->escape_string($_POST["id"]); /* editSave */
941                 $feed_ids = $this->dbh->escape_string($_POST["ids"]); /* batchEditSave */
942                 $cat_id = (int) $this->dbh->escape_string($_POST["cat_id"]);
943                 $auth_login = $this->dbh->escape_string(trim($_POST["auth_login"]));
944                 $auth_pass = trim($_POST["auth_pass"]);
945                 $private = checkbox_to_sql_bool($this->dbh->escape_string($_POST["private"]));
946                 $include_in_digest = checkbox_to_sql_bool(
947                         $this->dbh->escape_string($_POST["include_in_digest"]));
948                 $cache_images = checkbox_to_sql_bool(
949                         $this->dbh->escape_string($_POST["cache_images"]));
950                 $hide_images = checkbox_to_sql_bool(
951                         $this->dbh->escape_string($_POST["hide_images"]));
952                 $always_display_enclosures = checkbox_to_sql_bool(
953                         $this->dbh->escape_string($_POST["always_display_enclosures"]));
954
955                 $mark_unread_on_update = checkbox_to_sql_bool(
956                         $this->dbh->escape_string($_POST["mark_unread_on_update"]));
957
958                 if (strlen(FEED_CRYPT_KEY) > 0) {
959                         require_once "crypt.php";
960                         $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
961                         $auth_pass_encrypted = 'true';
962                 } else {
963                         $auth_pass_encrypted = 'false';
964                 }
965
966                 $auth_pass = $this->dbh->escape_string($auth_pass);
967
968                 if (get_pref('ENABLE_FEED_CATS')) {
969                         if ($cat_id && $cat_id != 0) {
970                                 $category_qpart = "cat_id = '$cat_id',";
971                                 $category_qpart_nocomma = "cat_id = '$cat_id'";
972                         } else {
973                                 $category_qpart = 'cat_id = NULL,';
974                                 $category_qpart_nocomma = 'cat_id = NULL';
975                         }
976                 } else {
977                         $category_qpart = "";
978                         $category_qpart_nocomma = "";
979                 }
980
981                 if (!$batch) {
982
983                         $result = $this->dbh->query("UPDATE ttrss_feeds SET
984                                 $category_qpart
985                                 title = '$feed_title', feed_url = '$feed_link',
986                                 update_interval = '$upd_intl',
987                                 purge_interval = '$purge_intl',
988                                 auth_login = '$auth_login',
989                                 auth_pass = '$auth_pass',
990                                 auth_pass_encrypted = $auth_pass_encrypted,
991                                 private = $private,
992                                 cache_images = $cache_images,
993                                 hide_images = $hide_images,
994                                 include_in_digest = $include_in_digest,
995                                 always_display_enclosures = $always_display_enclosures,
996                                 mark_unread_on_update = $mark_unread_on_update
997                         WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]);
998
999                         PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_SAVE_FEED,
1000                                 "hook_prefs_save_feed", $feed_id);
1001
1002                 } else {
1003                         $feed_data = array();
1004
1005                         foreach (array_keys($_POST) as $k) {
1006                                 if ($k != "op" && $k != "method" && $k != "ids") {
1007                                         $feed_data[$k] = $_POST[$k];
1008                                 }
1009                         }
1010
1011                         $this->dbh->query("BEGIN");
1012
1013                         foreach (array_keys($feed_data) as $k) {
1014
1015                                 $qpart = "";
1016
1017                                 switch ($k) {
1018                                         case "title":
1019                                                 $qpart = "title = '$feed_title'";
1020                                                 break;
1021
1022                                         case "feed_url":
1023                                                 $qpart = "feed_url = '$feed_link'";
1024                                                 break;
1025
1026                                         case "update_interval":
1027                                                 $qpart = "update_interval = '$upd_intl'";
1028                                                 break;
1029
1030                                         case "purge_interval":
1031                                                 $qpart = "purge_interval = '$purge_intl'";
1032                                                 break;
1033
1034                                         case "auth_login":
1035                                                 $qpart = "auth_login = '$auth_login'";
1036                                                 break;
1037
1038                                         case "auth_pass":
1039                                                 $qpart = "auth_pass = '$auth_pass' AND
1040                                                         auth_pass_encrypted = $auth_pass_encrypted";
1041                                                 break;
1042
1043                                         case "private":
1044                                                 $qpart = "private = $private";
1045                                                 break;
1046
1047                                         case "include_in_digest":
1048                                                 $qpart = "include_in_digest = $include_in_digest";
1049                                                 break;
1050
1051                                         case "always_display_enclosures":
1052                                                 $qpart = "always_display_enclosures = $always_display_enclosures";
1053                                                 break;
1054
1055                                         case "mark_unread_on_update":
1056                                                 $qpart = "mark_unread_on_update = $mark_unread_on_update";
1057                                                 break;
1058
1059                                         case "cache_images":
1060                                                 $qpart = "cache_images = $cache_images";
1061                                                 break;
1062
1063                                         case "hide_images":
1064                                                 $qpart = "hide_images = $hide_images";
1065                                                 break;
1066
1067                                         case "cat_id":
1068                                                 $qpart = $category_qpart_nocomma;
1069                                                 break;
1070
1071                                 }
1072
1073                                 if ($qpart) {
1074                                         $this->dbh->query(
1075                                                 "UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids)
1076                                                 AND owner_uid = " . $_SESSION["uid"]);
1077                                         print "<br/>";
1078                                 }
1079                         }
1080
1081                         $this->dbh->query("COMMIT");
1082                 }
1083                 return;
1084         }
1085
1086         function resetPubSub() {
1087
1088                 $ids = $this->dbh->escape_string($_REQUEST["ids"]);
1089
1090                 $this->dbh->query("UPDATE ttrss_feeds SET pubsub_state = 0 WHERE id IN ($ids)
1091                         AND owner_uid = " . $_SESSION["uid"]);
1092
1093                 return;
1094         }
1095
1096         function remove() {
1097
1098                 $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
1099
1100                 foreach ($ids as $id) {
1101                         Pref_Feeds::remove_feed($id, $_SESSION["uid"]);
1102                 }
1103
1104                 return;
1105         }
1106
1107         function clear() {
1108                 $id = $this->dbh->escape_string($_REQUEST["id"]);
1109                 $this->clear_feed_articles($id);
1110         }
1111
1112         function rescore() {
1113                 require_once "rssfuncs.php";
1114
1115                 $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
1116
1117                 foreach ($ids as $id) {
1118
1119                         $filters = load_filters($id, $_SESSION["uid"], 6);
1120
1121                         $result = $this->dbh->query("SELECT
1122                                 title, content, link, ref_id, author,".
1123                                 SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated
1124                                 FROM
1125                                         ttrss_user_entries, ttrss_entries
1126                                         WHERE ref_id = id AND feed_id = '$id' AND
1127                                                 owner_uid = " .$_SESSION['uid']."
1128                                         ");
1129
1130                         $scores = array();
1131
1132                         while ($line = $this->dbh->fetch_assoc($result)) {
1133
1134                                 $tags = get_article_tags($line["ref_id"]);
1135
1136                                 $article_filters = get_article_filters($filters, $line['title'],
1137                                         $line['content'], $line['link'], strtotime($line['updated']),
1138                                         $line['author'], $tags);
1139
1140                                 $new_score = calculate_article_score($article_filters);
1141
1142                                 if (!$scores[$new_score]) $scores[$new_score] = array();
1143
1144                                 array_push($scores[$new_score], $line['ref_id']);
1145                         }
1146
1147                         foreach (array_keys($scores) as $s) {
1148                                 if ($s > 1000) {
1149                                         $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s',
1150                                                 marked = true WHERE
1151                                                 ref_id IN (" . join(',', $scores[$s]) . ")");
1152                                 } else if ($s < -500) {
1153                                         $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s',
1154                                                 unread = false WHERE
1155                                                 ref_id IN (" . join(',', $scores[$s]) . ")");
1156                                 } else {
1157                                         $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s' WHERE
1158                                                 ref_id IN (" . join(',', $scores[$s]) . ")");
1159                                 }
1160                         }
1161                 }
1162
1163                 print __("All done.");
1164
1165         }
1166
1167         function rescoreAll() {
1168
1169                 $result = $this->dbh->query(
1170                         "SELECT id FROM ttrss_feeds WHERE owner_uid = " . $_SESSION['uid']);
1171
1172                 while ($feed_line = $this->dbh->fetch_assoc($result)) {
1173
1174                         $id = $feed_line["id"];
1175
1176                         $filters = load_filters($id, $_SESSION["uid"], 6);
1177
1178                         $tmp_result = $this->dbh->query("SELECT
1179                                 title, content, link, ref_id, author,".
1180                                         SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated
1181                                         FROM
1182                                         ttrss_user_entries, ttrss_entries
1183                                         WHERE ref_id = id AND feed_id = '$id' AND
1184                                                 owner_uid = " .$_SESSION['uid']."
1185                                         ");
1186
1187                         $scores = array();
1188
1189                         while ($line = $this->dbh->fetch_assoc($tmp_result)) {
1190
1191                                 $tags = get_article_tags($line["ref_id"]);
1192
1193                                 $article_filters = get_article_filters($filters, $line['title'],
1194                                         $line['content'], $line['link'], strtotime($line['updated']),
1195                                         $line['author'], $tags);
1196
1197                                 $new_score = calculate_article_score($article_filters);
1198
1199                                 if (!$scores[$new_score]) $scores[$new_score] = array();
1200
1201                                 array_push($scores[$new_score], $line['ref_id']);
1202                         }
1203
1204                         foreach (array_keys($scores) as $s) {
1205                                 if ($s > 1000) {
1206                                         $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s',
1207                                                 marked = true WHERE
1208                                                 ref_id IN (" . join(',', $scores[$s]) . ")");
1209                                 } else {
1210                                         $this->dbh->query("UPDATE ttrss_user_entries SET score = '$s' WHERE
1211                                                 ref_id IN (" . join(',', $scores[$s]) . ")");
1212                                 }
1213                         }
1214                 }
1215
1216                 print __("All done.");
1217
1218         }
1219
1220         function categorize() {
1221                 $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
1222
1223                 $cat_id = $this->dbh->escape_string($_REQUEST["cat_id"]);
1224
1225                 if ($cat_id == 0) {
1226                         $cat_id_qpart = 'NULL';
1227                 } else {
1228                         $cat_id_qpart = "'$cat_id'";
1229                 }
1230
1231                 $this->dbh->query("BEGIN");
1232
1233                 foreach ($ids as $id) {
1234
1235                         $this->dbh->query("UPDATE ttrss_feeds SET cat_id = $cat_id_qpart
1236                                 WHERE id = '$id'
1237                                 AND owner_uid = " . $_SESSION["uid"]);
1238
1239                 }
1240
1241                 $this->dbh->query("COMMIT");
1242         }
1243
1244         function removeCat() {
1245                 $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
1246                 foreach ($ids as $id) {
1247                         $this->remove_feed_category($id, $_SESSION["uid"]);
1248                 }
1249         }
1250
1251         function addCat() {
1252                 $feed_cat = $this->dbh->escape_string(trim($_REQUEST["cat"]));
1253
1254                 add_feed_category($feed_cat);
1255         }
1256
1257         function index() {
1258
1259                 print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">";
1260                 print "<div id=\"pref-feeds-feeds\" dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Feeds')."\">";
1261
1262                 $result = $this->dbh->query("SELECT COUNT(id) AS num_errors
1263                         FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
1264
1265                 $num_errors = $this->dbh->fetch_result($result, 0, "num_errors");
1266
1267                 if ($num_errors > 0) {
1268
1269                         $error_button = "<button dojoType=\"dijit.form.Button\"
1270                                         onclick=\"showFeedsWithErrors()\" id=\"errorButton\">" .
1271                                 __("Feeds with errors") . "</button>";
1272                 }
1273
1274                 if (DB_TYPE == "pgsql") {
1275                         $interval_qpart = "NOW() - INTERVAL '3 months'";
1276                 } else {
1277                         $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
1278                 }
1279
1280                 $result = $this->dbh->query("SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE
1281                                         (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
1282                                                 ttrss_entries.id = ref_id AND
1283                                                         ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND
1284                         ttrss_feeds.owner_uid = ".$_SESSION["uid"]);
1285
1286                 $num_inactive = $this->dbh->fetch_result($result, 0, "num_inactive");
1287
1288                 if ($num_inactive > 0) {
1289                         $inactive_button = "<button dojoType=\"dijit.form.Button\"
1290                                         onclick=\"showInactiveFeeds()\">" .
1291                                         __("Inactive feeds") . "</button>";
1292                 }
1293
1294                 $feed_search = $this->dbh->escape_string($_REQUEST["search"]);
1295
1296                 if (array_key_exists("search", $_REQUEST)) {
1297                         $_SESSION["prefs_feed_search"] = $feed_search;
1298                 } else {
1299                         $feed_search = $_SESSION["prefs_feed_search"];
1300                 }
1301
1302                 print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
1303
1304                 print "<div region='top' dojoType=\"dijit.Toolbar\">"; #toolbar
1305
1306                 print "<div style='float : right; padding-right : 4px;'>
1307                         <input dojoType=\"dijit.form.TextBox\" id=\"feed_search\" size=\"20\" type=\"search\"
1308                                 value=\"$feed_search\">
1309                         <button dojoType=\"dijit.form.Button\" onclick=\"updateFeedList()\">".
1310                                 __('Search')."</button>
1311                         </div>";
1312
1313                 print "<div dojoType=\"dijit.form.DropDownButton\">".
1314                                 "<span>" . __('Select')."</span>";
1315                 print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1316                 print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(true)\"
1317                         dojoType=\"dijit.MenuItem\">".__('All')."</div>";
1318                 print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(false)\"
1319                         dojoType=\"dijit.MenuItem\">".__('None')."</div>";
1320                 print "</div></div>";
1321
1322                 print "<div dojoType=\"dijit.form.DropDownButton\">".
1323                                 "<span>" . __('Feeds')."</span>";
1324                 print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1325                 print "<div onclick=\"quickAddFeed()\"
1326                         dojoType=\"dijit.MenuItem\">".__('Subscribe to feed')."</div>";
1327                 print "<div onclick=\"editSelectedFeed()\"
1328                         dojoType=\"dijit.MenuItem\">".__('Edit selected feeds')."</div>";
1329                 print "<div onclick=\"resetFeedOrder()\"
1330                         dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>";
1331                 print "<div onclick=\"batchSubscribe()\"
1332                         dojoType=\"dijit.MenuItem\">".__('Batch subscribe')."</div>";
1333                 print "<div dojoType=\"dijit.MenuItem\" onclick=\"removeSelectedFeeds()\">"
1334                         .__('Unsubscribe')."</div> ";
1335                 print "</div></div>";
1336
1337                 if (get_pref('ENABLE_FEED_CATS')) {
1338                         print "<div dojoType=\"dijit.form.DropDownButton\">".
1339                                         "<span>" . __('Categories')."</span>";
1340                         print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1341                         print "<div onclick=\"createCategory()\"
1342                                 dojoType=\"dijit.MenuItem\">".__('Add category')."</div>";
1343                         print "<div onclick=\"resetCatOrder()\"
1344                                 dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>";
1345                         print "<div onclick=\"removeSelectedCategories()\"
1346                                 dojoType=\"dijit.MenuItem\">".__('Remove selected')."</div>";
1347                         print "</div></div>";
1348
1349                 }
1350
1351                 print $error_button;
1352                 print $inactive_button;
1353
1354                 print "<button onclick=\"toggleHiddenFeedCats()\"
1355                         dojoType=\"dijit.form.Button\">".__('(Un)hide empty categories')."</button>";
1356
1357                 if (defined('_ENABLE_FEED_DEBUGGING')) {
1358
1359                         print "<select id=\"feedActionChooser\" onchange=\"feedActionChange()\">
1360                                 <option value=\"facDefault\" selected>".__('More actions...')."</option>";
1361
1362                         if (FORCE_ARTICLE_PURGE == 0) {
1363                                 print
1364                                         "<option value=\"facPurge\">".__('Manual purge')."</option>";
1365                         }
1366
1367                         print "
1368                                 <option value=\"facClear\">".__('Clear feed data')."</option>
1369                                 <option value=\"facRescore\">".__('Rescore articles')."</option>";
1370
1371                         print "</select>";
1372
1373                 }
1374
1375                 print "</div>"; # toolbar
1376
1377                 //print '</div>';
1378                 print '<div dojoType="dijit.layout.ContentPane" region="center">';
1379
1380                 print "<div id=\"feedlistLoading\">
1381                 <img src='images/indicator_tiny.gif'>".
1382                  __("Loading, please wait...")."</div>";
1383
1384                 print "<div dojoType=\"fox.PrefFeedStore\" jsId=\"feedStore\"
1385                         url=\"backend.php?op=pref-feeds&method=getfeedtree\">
1386                 </div>
1387                 <div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"feedModel\" store=\"feedStore\"
1388                 query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Feeds\"
1389                         childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
1390                 </div>
1391                 <div dojoType=\"fox.PrefFeedTree\" id=\"feedTree\"
1392                         dndController=\"dijit.tree.dndSource\"
1393                         betweenThreshold=\"5\"
1394                         model=\"feedModel\" openOnClick=\"false\">
1395                 <script type=\"dojo/method\" event=\"onClick\" args=\"item\">
1396                         var id = String(item.id);
1397                         var bare_id = id.substr(id.indexOf(':')+1);
1398
1399                         if (id.match('FEED:')) {
1400                                 editFeed(bare_id);
1401                         } else if (id.match('CAT:')) {
1402                                 editCat(bare_id, item);
1403                         }
1404                 </script>
1405                 <script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
1406                         Element.hide(\"feedlistLoading\");
1407                 </script>
1408                 </div>";
1409
1410 #               print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedTree\" position=\"below\">
1411 #                       ".__('<b>Hint:</b> you can drag feeds and categories around.')."
1412 #                       </div>";
1413
1414                 print '</div>';
1415                 print '</div>';
1416
1417                 print "</div>"; # feeds pane
1418
1419                 print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('OPML')."\">";
1420
1421                 print "<p>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . " ";
1422
1423                 print __("Only main settings profile can be migrated using OPML.") . "</p>";
1424
1425                 print "<iframe id=\"upload_iframe\"
1426                         name=\"upload_iframe\" onload=\"opmlImportComplete(this)\"
1427                         style=\"width: 400px; height: 100px; display: none;\"></iframe>";
1428
1429                 print "<form  name=\"opml_form\" style='display : block' target=\"upload_iframe\"
1430                         enctype=\"multipart/form-data\" method=\"POST\"
1431                         action=\"backend.php\">
1432                         <input id=\"opml_file\" name=\"opml_file\" type=\"file\">&nbsp;
1433                         <input type=\"hidden\" name=\"op\" value=\"dlg\">
1434                         <input type=\"hidden\" name=\"method\" value=\"importOpml\">
1435                         <button dojoType=\"dijit.form.Button\" onclick=\"return opmlImport();\" type=\"submit\">" .
1436                         __('Import my OPML') . "</button>";
1437
1438                 print "<hr>";
1439
1440                 print "<p>" . __('Filename:') .
1441             " <input type=\"text\" id=\"filename\" value=\"TinyTinyRSS.opml\" />&nbsp;" .
1442                                 __('Include settings') . "<input type=\"checkbox\" id=\"settings\" checked=\"1\"/>";
1443
1444                 print "</p><button dojoType=\"dijit.form.Button\"
1445                         onclick=\"gotoExportOpml(document.opml_form.filename.value, document.opml_form.settings.checked)\" >" .
1446               __('Export OPML') . "</button></p></form>";
1447
1448                 print "<hr>";
1449
1450                 print "<p>".__('Your OPML can be published publicly and can be subscribed by anyone who knows the URL below.') . " ";
1451
1452                 print __("Published OPML does not include your Tiny Tiny RSS settings, feeds that require authentication or feeds hidden from Popular feeds.") . "</p>";
1453
1454                 print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('".__("Public OPML URL")."','pubOPMLUrl')\">".
1455                         __('Display published OPML URL')."</button> ";
1456
1457                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
1458                         "hook_prefs_tab_section", "prefFeedsOPML");
1459
1460                 print "</div>"; # pane
1461
1462                 if (strpos($_SERVER['HTTP_USER_AGENT'], "Firefox") !== false) {
1463
1464                         print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Firefox integration')."\">";
1465
1466                         print "<p>" . __('This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the link below.') . "</p>";
1467
1468                         print "<p>";
1469
1470                         print "<button onclick='window.navigator.registerContentHandler(" .
1471                       "\"application/vnd.mozilla.maybe.feed\", " .
1472                       "\"" . add_feed_url() . "\", " . " \"Tiny Tiny RSS\")'>" .
1473                                                          __('Click here to register this site as a feed reader.') .
1474                                 "</button>";
1475
1476                         print "</p>";
1477
1478                         print "</div>"; # pane
1479                 }
1480
1481                 print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Published & shared articles / Generated feeds')."\">";
1482
1483                 print "<h3>" . __("Published articles and generated feeds") . "</h3>";
1484
1485                 print "<p>".__('Published articles are exported as a public RSS feed and can be subscribed by anyone who knows the URL specified below.')."</p>";
1486
1487                 $rss_url = '-2::' . htmlspecialchars(get_self_url_prefix() .
1488                                 "/public.php?op=rss&id=-2&view-mode=all_articles");;
1489
1490                 print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('".__("View as RSS")."','generatedFeed', '$rss_url')\">".
1491                         __('Display URL')."</button> ";
1492
1493                 print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearFeedAccessKeys()\">".
1494                         __('Clear all generated URLs')."</button> ";
1495
1496                 print "<h3>" . __("Articles shared by URL") . "</h3>";
1497
1498                 print "<p>" . __("You can disable all articles shared by unique URLs here.") . "</p>";
1499
1500                 print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearArticleAccessKeys()\">".
1501                         __('Unshare all articles')."</button> ";
1502
1503                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
1504                         "hook_prefs_tab_section", "prefFeedsPublishedGenerated");
1505
1506                 print "</div>"; #pane
1507
1508                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
1509                         "hook_prefs_tab", "prefFeeds");
1510
1511                 print "</div>"; #container
1512         }
1513
1514         private function feedlist_init_cat($cat_id) {
1515                 $obj = array();
1516                 $cat_id = (int) $cat_id;
1517
1518                 if ($cat_id > 0) {
1519                         $cat_unread = ccache_find($cat_id, $_SESSION["uid"], true);
1520                 } else if ($cat_id == 0 || $cat_id == -2) {
1521                         $cat_unread = getCategoryUnread($cat_id);
1522                 }
1523
1524                 $obj['id'] = 'CAT:' . $cat_id;
1525                 $obj['items'] = array();
1526                 $obj['name'] = getCategoryTitle($cat_id);
1527                 $obj['type'] = 'category';
1528                 $obj['unread'] = (int) $cat_unread;
1529                 $obj['bare_id'] = $cat_id;
1530
1531                 return $obj;
1532         }
1533
1534         private function feedlist_init_feed($feed_id, $title = false, $unread = false, $error = '', $updated = '') {
1535                 $obj = array();
1536                 $feed_id = (int) $feed_id;
1537
1538                 if (!$title)
1539                         $title = getFeedTitle($feed_id, false);
1540
1541                 if ($unread === false)
1542                         $unread = getFeedUnread($feed_id, false);
1543
1544                 $obj['id'] = 'FEED:' . $feed_id;
1545                 $obj['name'] = $title;
1546                 $obj['unread'] = (int) $unread;
1547                 $obj['type'] = 'feed';
1548                 $obj['error'] = $error;
1549                 $obj['updated'] = $updated;
1550                 $obj['icon'] = getFeedIcon($feed_id);
1551                 $obj['bare_id'] = $feed_id;
1552
1553                 return $obj;
1554         }
1555
1556         function inactiveFeeds() {
1557
1558                 if (DB_TYPE == "pgsql") {
1559                         $interval_qpart = "NOW() - INTERVAL '3 months'";
1560                 } else {
1561                         $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
1562                 }
1563
1564                 $result = $this->dbh->query("SELECT ttrss_feeds.title, ttrss_feeds.site_url,
1565                                 ttrss_feeds.feed_url, ttrss_feeds.id, MAX(updated) AS last_article
1566                         FROM ttrss_feeds, ttrss_entries, ttrss_user_entries WHERE
1567                                 (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
1568                                         ttrss_entries.id = ref_id AND
1569                                                 ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart
1570                         AND ttrss_feeds.owner_uid = ".$_SESSION["uid"]." AND
1571                                 ttrss_user_entries.feed_id = ttrss_feeds.id AND
1572                                 ttrss_entries.id = ref_id
1573                         GROUP BY ttrss_feeds.title, ttrss_feeds.id, ttrss_feeds.site_url, ttrss_feeds.feed_url
1574                         ORDER BY last_article");
1575
1576                 print "<h2" .__("These feeds have not been updated with new content for 3 months (oldest first):") . "</h2>";
1577
1578                 print "<div dojoType=\"dijit.Toolbar\">";
1579                 print "<div dojoType=\"dijit.form.DropDownButton\">".
1580                                 "<span>" . __('Select')."</span>";
1581                 print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1582                 print "<div onclick=\"selectTableRows('prefInactiveFeedList', 'all')\"
1583                         dojoType=\"dijit.MenuItem\">".__('All')."</div>";
1584                 print "<div onclick=\"selectTableRows('prefInactiveFeedList', 'none')\"
1585                         dojoType=\"dijit.MenuItem\">".__('None')."</div>";
1586                 print "</div></div>";
1587                 print "</div>"; #toolbar
1588
1589                 print "<div class=\"inactiveFeedHolder\">";
1590
1591                 print "<table width=\"100%\" cellspacing=\"0\" id=\"prefInactiveFeedList\">";
1592
1593                 $lnum = 1;
1594
1595                 while ($line = $this->dbh->fetch_assoc($result)) {
1596
1597                         $feed_id = $line["id"];
1598                         $this_row_id = "id=\"FUPDD-$feed_id\"";
1599
1600                         # class needed for selectTableRows()
1601                         print "<tr class=\"placeholder\" $this_row_id>";
1602
1603                         $edit_title = htmlspecialchars($line["title"]);
1604
1605                         # id needed for selectTableRows()
1606                         print "<td width='5%' align='center'><input
1607                                 onclick='toggleSelectRow2(this);' dojoType=\"dijit.form.CheckBox\"
1608                                 type=\"checkbox\" id=\"FUPDC-$feed_id\"></td>";
1609                         print "<td>";
1610
1611                         print "<a class=\"visibleLink\" href=\"#\" ".
1612                                 "title=\"".__("Click to edit feed")."\" ".
1613                                 "onclick=\"editFeed(".$line["id"].")\">".
1614                                 htmlspecialchars($line["title"])."</a>";
1615
1616                         print "</td><td class=\"insensitive\" align='right'>";
1617                         print make_local_datetime($line['last_article'], false);
1618                         print "</td>";
1619                         print "</tr>";
1620
1621                         ++$lnum;
1622                 }
1623
1624                 print "</table>";
1625                 print "</div>";
1626
1627                 print "<div class='dlgButtons'>";
1628                 print "<div style='float : left'>";
1629                 print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('inactiveFeedsDlg').removeSelected()\">"
1630                         .__('Unsubscribe from selected feeds')."</button> ";
1631                 print "</div>";
1632
1633                 print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('inactiveFeedsDlg').hide()\">".
1634                         __('Close this window')."</button>";
1635
1636                 print "</div>";
1637
1638         }
1639
1640         function feedsWithErrors() {
1641                 print "<h2>" . __("These feeds have not been updated because of errors:") .
1642                         "</h2>";
1643
1644                 $result = $this->dbh->query("SELECT id,title,feed_url,last_error,site_url
1645                 FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
1646
1647                 print "<div dojoType=\"dijit.Toolbar\">";
1648                 print "<div dojoType=\"dijit.form.DropDownButton\">".
1649                                 "<span>" . __('Select')."</span>";
1650                 print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1651                 print "<div onclick=\"selectTableRows('prefErrorFeedList', 'all')\"
1652                         dojoType=\"dijit.MenuItem\">".__('All')."</div>";
1653                 print "<div onclick=\"selectTableRows('prefErrorFeedList', 'none')\"
1654                         dojoType=\"dijit.MenuItem\">".__('None')."</div>";
1655                 print "</div></div>";
1656                 print "</div>"; #toolbar
1657
1658                 print "<div class=\"inactiveFeedHolder\">";
1659
1660                 print "<table width=\"100%\" cellspacing=\"0\" id=\"prefErrorFeedList\">";
1661
1662                 $lnum = 1;
1663
1664                 while ($line = $this->dbh->fetch_assoc($result)) {
1665
1666                         $feed_id = $line["id"];
1667                         $this_row_id = "id=\"FERDD-$feed_id\"";
1668
1669                         # class needed for selectTableRows()
1670                         print "<tr class=\"placeholder\" $this_row_id>";
1671
1672                         $edit_title = htmlspecialchars($line["title"]);
1673
1674                         # id needed for selectTableRows()
1675                         print "<td width='5%' align='center'><input
1676                                 onclick='toggleSelectRow2(this);' dojoType=\"dijit.form.CheckBox\"
1677                                 type=\"checkbox\" id=\"FERDC-$feed_id\"></td>";
1678                         print "<td>";
1679
1680                         print "<a class=\"visibleLink\" href=\"#\" ".
1681                                 "title=\"".__("Click to edit feed")."\" ".
1682                                 "onclick=\"editFeed(".$line["id"].")\">".
1683                                 htmlspecialchars($line["title"])."</a>: ";
1684
1685                         print "<span class=\"insensitive\">";
1686                         print htmlspecialchars($line["last_error"]);
1687                         print "</span>";
1688
1689                         print "</td>";
1690                         print "</tr>";
1691
1692                         ++$lnum;
1693                 }
1694
1695                 print "</table>";
1696                 print "</div>";
1697
1698                 print "<div class='dlgButtons'>";
1699                 print "<div style='float : left'>";
1700                 print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').removeSelected()\">"
1701                         .__('Unsubscribe from selected feeds')."</button> ";
1702                 print "</div>";
1703
1704                 print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').hide()\">".
1705                         __('Close this window')."</button>";
1706
1707                 print "</div>";
1708         }
1709
1710         /**
1711          * Purge a feed contents, marked articles excepted.
1712          *
1713          * @param mixed $link The database connection.
1714          * @param integer $id The id of the feed to purge.
1715          * @return void
1716          */
1717         private function clear_feed_articles($id) {
1718
1719                 if ($id != 0) {
1720                         $result = $this->dbh->query("DELETE FROM ttrss_user_entries
1721                         WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]);
1722                 } else {
1723                         $result = $this->dbh->query("DELETE FROM ttrss_user_entries
1724                         WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]);
1725                 }
1726
1727                 $result = $this->dbh->query("DELETE FROM ttrss_entries WHERE
1728                         (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
1729
1730                 ccache_update($id, $_SESSION['uid']);
1731         } // function clear_feed_articles
1732
1733         private function remove_feed_category($id, $owner_uid) {
1734
1735                 $this->dbh->query("DELETE FROM ttrss_feed_categories
1736                         WHERE id = '$id' AND owner_uid = $owner_uid");
1737
1738                 ccache_remove($id, $owner_uid, true);
1739         }
1740
1741         static function remove_feed($id, $owner_uid) {
1742
1743                 if ($id > 0) {
1744
1745                         /* save starred articles in Archived feed */
1746
1747                         db_query("BEGIN");
1748
1749                         /* prepare feed if necessary */
1750
1751                         $result = db_query("SELECT feed_url FROM ttrss_feeds WHERE id = $id
1752                                 AND owner_uid = $owner_uid");
1753
1754                         $feed_url = db_escape_string(db_fetch_result($result, 0, "feed_url"));
1755
1756                         $result = db_query("SELECT id FROM ttrss_archived_feeds
1757                                 WHERE feed_url = '$feed_url' AND owner_uid = $owner_uid");
1758
1759                         if (db_num_rows($result) == 0) {
1760                                 $result = db_query("SELECT MAX(id) AS id FROM ttrss_archived_feeds");
1761                                 $new_feed_id = (int)db_fetch_result($result, 0, "id") + 1;
1762
1763                                 db_query("INSERT INTO ttrss_archived_feeds
1764                                         (id, owner_uid, title, feed_url, site_url)
1765                                 SELECT $new_feed_id, owner_uid, title, feed_url, site_url from ttrss_feeds
1766                                 WHERE id = '$id'");
1767
1768                                 $archive_id = $new_feed_id;
1769                         } else {
1770                                 $archive_id = db_fetch_result($result, 0, "id");
1771                         }
1772
1773                         db_query("UPDATE ttrss_user_entries SET feed_id = NULL,
1774                                 orig_feed_id = '$archive_id' WHERE feed_id = '$id' AND
1775                                         marked = true AND owner_uid = $owner_uid");
1776
1777                         /* Remove access key for the feed */
1778
1779                         db_query("DELETE FROM ttrss_access_keys WHERE
1780                                 feed_id = '$id' AND owner_uid = $owner_uid");
1781
1782                         /* remove the feed */
1783
1784                         db_query("DELETE FROM ttrss_feeds
1785                                         WHERE id = '$id' AND owner_uid = $owner_uid");
1786
1787                         db_query("COMMIT");
1788
1789                         if (file_exists(ICONS_DIR . "/$id.ico")) {
1790                                 unlink(ICONS_DIR . "/$id.ico");
1791                         }
1792
1793                         ccache_remove($id, $owner_uid);
1794
1795                 } else {
1796                         label_remove(feed_to_label_id($id), $owner_uid);
1797                         //ccache_remove($id, $owner_uid); don't think labels are cached
1798                 }
1799         }
1800
1801         function batchSubscribe() {
1802                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
1803                 print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchaddfeeds\">";
1804
1805                 print "<table width='100%'><tr><td>
1806                         ".__("Add one valid RSS feed per line (no feed detection is done)")."
1807                 </td><td align='right'>";
1808                 if (get_pref('ENABLE_FEED_CATS')) {
1809                         print __('Place in category:') . " ";
1810                         print_feed_cat_select("cat", false, 'dojoType="dijit.form.Select"');
1811                 }
1812                 print "</td></tr><tr><td colspan='2'>";
1813                 print "<textarea
1814                         style='font-size : 12px; width : 100%; height: 200px;'
1815                         placeHolder=\"".__("Feeds to subscribe, One per line")."\"
1816                         dojoType=\"dijit.form.SimpleTextarea\" required=\"1\" name=\"feeds\"></textarea>";
1817
1818                 print "</td></tr><tr><td colspan='2'>";
1819
1820                 print "<div id='feedDlg_loginContainer' style='display : none'>
1821                                 " .
1822                                 " <input dojoType=\"dijit.form.TextBox\" name='login'\"
1823                                         placeHolder=\"".__("Login")."\"
1824                                         style=\"width : 10em;\"> ".
1825                                 " <input
1826                                         placeHolder=\"".__("Password")."\"
1827                                         dojoType=\"dijit.form.TextBox\" type='password'
1828                                         style=\"width : 10em;\" name='pass'\">".
1829                                 "</div>";
1830
1831                 print "</td></tr><tr><td colspan='2'>";
1832
1833                 print "<div style=\"clear : both\">
1834                         <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\"
1835                                         onclick='checkboxToggleElement(this, \"feedDlg_loginContainer\")'>
1836                                 <label for=\"feedDlg_loginCheck\">".
1837                                 __('Feeds require authentication.')."</div>";
1838
1839                 print "</form>";
1840
1841                 print "</td></tr></table>";
1842
1843                 print "<div class=\"dlgButtons\">
1844                         <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').execute()\">".__('Subscribe')."</button>
1845                         <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button>
1846                         </div>";
1847         }
1848
1849         function batchAddFeeds() {
1850                 $cat_id = $this->dbh->escape_string($_REQUEST['cat']);
1851                 $feeds = explode("\n", $_REQUEST['feeds']);
1852                 $login = $this->dbh->escape_string($_REQUEST['login']);
1853                 $pass = trim($_REQUEST['pass']);
1854
1855                 foreach ($feeds as $feed) {
1856                         $feed = $this->dbh->escape_string(trim($feed));
1857
1858                         if (validate_feed_url($feed)) {
1859
1860                                 $this->dbh->query("BEGIN");
1861
1862                                 if ($cat_id == "0" || !$cat_id) {
1863                                         $cat_qpart = "NULL";
1864                                 } else {
1865                                         $cat_qpart = "'$cat_id'";
1866                                 }
1867
1868                                 $result = $this->dbh->query(
1869                                         "SELECT id FROM ttrss_feeds
1870                                         WHERE feed_url = '$feed' AND owner_uid = ".$_SESSION["uid"]);
1871
1872                                 if (strlen(FEED_CRYPT_KEY) > 0) {
1873                                         require_once "crypt.php";
1874                                         $pass = substr(encrypt_string($pass), 0, 250);
1875                                         $auth_pass_encrypted = 'true';
1876                                 } else {
1877                                         $auth_pass_encrypted = 'false';
1878                                 }
1879
1880                                 $pass = $this->dbh->escape_string($pass);
1881
1882                                 if ($this->dbh->num_rows($result) == 0) {
1883                                         $result = $this->dbh->query(
1884                                                 "INSERT INTO ttrss_feeds
1885                                                         (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted)
1886                                                 VALUES ('".$_SESSION["uid"]."', '$feed',
1887                                                         '[Unknown]', $cat_qpart, '$login', '$pass', 0, $auth_pass_encrypted)");
1888                                 }
1889
1890                                 $this->dbh->query("COMMIT");
1891                         }
1892                 }
1893         }
1894
1895         function regenOPMLKey() {
1896                 $this->update_feed_access_key('OPML:Publish',
1897                 false, $_SESSION["uid"]);
1898
1899                 $new_link = Opml::opml_publish_url();
1900
1901                 print json_encode(array("link" => $new_link));
1902         }
1903
1904         function regenFeedKey() {
1905                 $feed_id = $this->dbh->escape_string($_REQUEST['id']);
1906                 $is_cat = $this->dbh->escape_string($_REQUEST['is_cat']) == "true";
1907
1908                 $new_key = $this->update_feed_access_key($feed_id, $is_cat);
1909
1910                 print json_encode(array("link" => $new_key));
1911         }
1912
1913
1914         private function update_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
1915                 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1916
1917                 $sql_is_cat = bool_to_sql_bool($is_cat);
1918
1919                 $result = $this->dbh->query("SELECT access_key FROM ttrss_access_keys
1920                         WHERE feed_id = '$feed_id'      AND is_cat = $sql_is_cat
1921                         AND owner_uid = " . $owner_uid);
1922
1923                 if ($this->dbh->num_rows($result) == 1) {
1924                         $key = $this->dbh->escape_string(sha1(uniqid(rand(), true)));
1925
1926                         $this->dbh->query("UPDATE ttrss_access_keys SET access_key = '$key'
1927                                 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
1928                                 AND owner_uid = " . $owner_uid);
1929
1930                         return $key;
1931
1932                 } else {
1933                         return get_feed_access_key($feed_id, $is_cat, $owner_uid);
1934                 }
1935         }
1936
1937         // Silent
1938         function clearKeys() {
1939                 $this->dbh->query("DELETE FROM ttrss_access_keys WHERE
1940                         owner_uid = " . $_SESSION["uid"]);
1941         }
1942
1943
1944 }
1945 ?>