]> git.wh0rd.org - tt-rss.git/blobdiff - classes/pref/feeds.php
Merge branch 'checkbox-feed' of dxbi/tt-rss into master
[tt-rss.git] / classes / pref / feeds.php
old mode 100644 (file)
new mode 100755 (executable)
index ab280a9..a86a1af
@@ -1,5 +1,7 @@
 <?php
 class Pref_Feeds extends Handler_Protected {
+       public static $feed_languages = array("English", "Danish", "Dutch", "Finnish", "French", "German", "Hungarian", "Italian", "Norwegian",
+               "Portuguese", "Russian", "Spanish", "Swedish", "Turkish", "Simple");
 
        function csrf_ignore($method) {
                $csrf_ignored = array("index", "getfeedtree", "add", "editcats", "editfeed",
@@ -32,12 +34,11 @@ class Pref_Feeds extends Handler_Protected {
                else
                        $search = "";
 
-               if ($search) $search_qpart = " AND LOWER(title) LIKE LOWER('%$search%')";
+               if ($search) $search_qpart = " AND (LOWER(title) LIKE LOWER('%$search%') OR LOWER(feed_url) LIKE LOWER('%$search%'))";
 
                // first one is set by API
                $show_empty_cats = $_REQUEST['force_show_empty'] ||
-                       ($_REQUEST['mode'] != 2 && !$search &&
-                               get_pref('_PREFS_SHOW_EMPTY_CATS'));
+                       ($_REQUEST['mode'] != 2 && !$search);
 
                $items = array();
 
@@ -55,12 +56,15 @@ class Pref_Feeds extends Handler_Protected {
                        $cat['type'] = 'category';
                        $cat['unread'] = 0;
                        $cat['child_unread'] = 0;
+                       $cat['auxcounter'] = 0;
+                       $cat['parent_id'] = $cat_id;
 
                        $cat['items'] = $this->get_category_items($line['id']);
 
-                       $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
+                       $num_children = $this->calculate_children_count($cat);
+                       $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children);
 
-                       if (count($cat['items']) > 0 || $show_empty_cats)
+                       if ($num_children > 0 || $show_empty_cats)
                                array_push($items, $cat);
 
                }
@@ -75,11 +79,12 @@ class Pref_Feeds extends Handler_Protected {
                        $feed = array();
                        $feed['id'] = 'FEED:' . $feed_line['id'];
                        $feed['bare_id'] = (int)$feed_line['id'];
+                       $feed['auxcounter'] = 0;
                        $feed['name'] = $feed_line['title'];
                        $feed['checkbox'] = false;
                        $feed['unread'] = 0;
                        $feed['error'] = $feed_line['last_error'];
-                       $feed['icon'] = getFeedIcon($feed_line['id']);
+                       $feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
                        $feed['param'] = make_local_datetime(
                                $feed_line['last_updated'], true);
 
@@ -133,6 +138,7 @@ class Pref_Feeds extends Handler_Protected {
                                        $item = array();
                                        $item['id'] = 'FEED:' . $feed_id;
                                        $item['bare_id'] = (int)$feed_id;
+                                       $item['auxcounter'] = 0;
                                        $item['name'] = $feed['title'];
                                        $item['checkbox'] = false;
                                        $item['error'] = '';
@@ -165,7 +171,7 @@ class Pref_Feeds extends Handler_Protected {
 
                                while ($line = $this->dbh->fetch_assoc($result)) {
 
-                                       $label_id = label_to_feed_id($line['id']);
+                                       $label_id = Labels::label_to_feed_id($line['id']);
 
                                        $feed = $this->feedlist_init_feed($label_id, false, 0);
 
@@ -185,8 +191,7 @@ class Pref_Feeds extends Handler_Protected {
 
                if ($enable_cats) {
                        $show_empty_cats = $_REQUEST['force_show_empty'] ||
-                               ($_REQUEST['mode'] != 2 && !$search &&
-                               get_pref('_PREFS_SHOW_EMPTY_CATS'));
+                               ($_REQUEST['mode'] != 2 && !$search);
 
                        $result = $this->dbh->query("SELECT id, title FROM ttrss_feed_categories
                                WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat IS NULL ORDER BY order_id, title");
@@ -195,6 +200,7 @@ class Pref_Feeds extends Handler_Protected {
                                $cat = array();
                                $cat['id'] = 'CAT:' . $line['id'];
                                $cat['bare_id'] = (int)$line['id'];
+                               $cat['auxcounter'] = 0;
                                $cat['name'] = $line['title'];
                                $cat['items'] = array();
                                $cat['checkbox'] = false;
@@ -204,9 +210,10 @@ class Pref_Feeds extends Handler_Protected {
 
                                $cat['items'] = $this->get_category_items($line['id']);
 
-                               $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
+                               $num_children = $this->calculate_children_count($cat);
+                               $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children);
 
-                               if (count($cat['items']) > 0 || $show_empty_cats)
+                               if ($num_children > 0 || $show_empty_cats)
                                        array_push($root['items'], $cat);
 
                                $root['param'] += count($cat['items']);
@@ -217,6 +224,7 @@ class Pref_Feeds extends Handler_Protected {
                        $cat = array();
                        $cat['id'] = 'CAT:0';
                        $cat['bare_id'] = 0;
+                       $cat['auxcounter'] = 0;
                        $cat['name'] = __("Uncategorized");
                        $cat['items'] = array();
                        $cat['type'] = 'category';
@@ -234,10 +242,11 @@ class Pref_Feeds extends Handler_Protected {
                                $feed = array();
                                $feed['id'] = 'FEED:' . $feed_line['id'];
                                $feed['bare_id'] = (int)$feed_line['id'];
+                               $feed['auxcounter'] = 0;
                                $feed['name'] = $feed_line['title'];
                                $feed['checkbox'] = false;
                                $feed['error'] = $feed_line['last_error'];
-                               $feed['icon'] = getFeedIcon($feed_line['id']);
+                               $feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
                                $feed['param'] = make_local_datetime(
                                        $feed_line['last_updated'], true);
                                $feed['unread'] = 0;
@@ -251,8 +260,8 @@ class Pref_Feeds extends Handler_Protected {
                        if (count($cat['items']) > 0 || $show_empty_cats)
                                array_push($root['items'], $cat);
 
-                       $root['param'] += count($cat['items']);
-                       $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
+                       $num_children = $this->calculate_children_count($root);
+                       $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children);
 
                } else {
                        $feed_result = $this->dbh->query("SELECT id, title, last_error,
@@ -265,10 +274,11 @@ class Pref_Feeds extends Handler_Protected {
                                $feed = array();
                                $feed['id'] = 'FEED:' . $feed_line['id'];
                                $feed['bare_id'] = (int)$feed_line['id'];
+                               $feed['auxcounter'] = 0;
                                $feed['name'] = $feed_line['title'];
                                $feed['checkbox'] = false;
                                $feed['error'] = $feed_line['last_error'];
-                               $feed['icon'] = getFeedIcon($feed_line['id']);
+                               $feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
                                $feed['param'] = make_local_datetime(
                                        $feed_line['last_updated'], true);
                                $feed['unread'] = 0;
@@ -287,7 +297,7 @@ class Pref_Feeds extends Handler_Protected {
                if ($_REQUEST['mode'] != 2) {
                        $fl['items'] = array($root);
                } else {
-                       $fl['items'] =& $root['items'];
+                       $fl['items'] = $root['items'];
                }
 
                return $fl;
@@ -305,11 +315,6 @@ class Pref_Feeds extends Handler_Protected {
                return;
        }
 
-       function togglehiddenfeedcats() {
-               set_pref('_PREFS_SHOW_EMPTY_CATS',
-                       (get_pref('_PREFS_SHOW_EMPTY_CATS') ? 'false' : 'true'));
-       }
-
        private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) {
                $debug = isset($_REQUEST["debug"]);
 
@@ -319,7 +324,7 @@ class Pref_Feeds extends Handler_Protected {
 
                if ($debug) _debug("$prefix C: $item_id P: $parent_id");
 
-               $bare_item_id = substr($item_id, strpos($item_id, ':')+1);
+               $bare_item_id = $this->dbh->escape_string(substr($item_id, strpos($item_id, ':')+1));
 
                if ($item_id != 'root') {
                        if ($parent_id && $parent_id != 'root') {
@@ -334,14 +339,14 @@ class Pref_Feeds extends Handler_Protected {
                                owner_uid = " . $_SESSION["uid"]);
                }
 
-               $order_id = 0;
+               $order_id = 1;
 
                $cat = $data_map[$item_id];
 
                if ($cat && is_array($cat)) {
                        foreach ($cat as $item) {
                                $id = $item['_reference'];
-                               $bare_id = substr($id, strpos($id, ':')+1);
+                               $bare_id = $this->dbh->escape_string(substr($id, strpos($id, ':')+1));
 
                                if ($debug) _debug("$prefix [$order_id] $id/$bare_id");
 
@@ -393,7 +398,7 @@ class Pref_Feeds extends Handler_Protected {
 #              print_r($data['items']);
 
                if (is_array($data) && is_array($data['items'])) {
-                       $cat_order_id = 0;
+#                      $cat_order_id = 0;
 
                        $data_map = array();
                        $root_item = false;
@@ -405,7 +410,7 @@ class Pref_Feeds extends Handler_Protected {
                                                if (isset($item['items']['_reference'])) {
                                                        $data_map[$item['id']] = array($item['items']);
                                                } else {
-                                                       $data_map[$item['id']] =& $item['items'];
+                                                       $data_map[$item['id']] = $item['items'];
                                                }
                                        }
                                if ($item['id'] == 'root') {
@@ -492,7 +497,7 @@ class Pref_Feeds extends Handler_Protected {
                $feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
 
                if (is_file($icon_file) && $feed_id) {
-                       if (filesize($icon_file) < 20000) {
+                       if (filesize($icon_file) < 65535) {
 
                                $result = $this->dbh->query("SELECT id FROM ttrss_feeds
                                        WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]);
@@ -528,6 +533,9 @@ class Pref_Feeds extends Handler_Protected {
                global $purge_intervals;
                global $update_intervals;
 
+               print '<div dojoType="dijit.layout.TabContainer" style="height : 450px">
+                       <div dojoType="dijit.layout.ContentPane" title="'.__('General').'">';
+
                $feed_id = $this->dbh->escape_string($_REQUEST["id"]);
 
                $result = $this->dbh->query(
@@ -540,9 +548,9 @@ class Pref_Feeds extends Handler_Protected {
                $title = htmlspecialchars($this->dbh->fetch_result($result,
                        0, "title"));
 
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$feed_id\">";
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
+               print_hidden("id", "$feed_id");
+               print_hidden("op", "pref-feeds");
+               print_hidden("method", "editSave");
 
                print "<div class=\"dlgSec\">".__("Feed")."</div>";
                print "<div class=\"dlgSecCont\">";
@@ -570,8 +578,9 @@ class Pref_Feeds extends Handler_Protected {
                $last_error = $this->dbh->fetch_result($result, 0, "last_error");
 
                if ($last_error) {
-                       print "&nbsp;<span title=\"".htmlspecialchars($last_error)."\"
-                               class=\"feed_error\">(error)</span>";
+                       print "&nbsp;<img src=\"images/error.png\" alt=\"(error)\"
+                               style=\"vertical-align : middle\"
+                               title=\"".htmlspecialchars($last_error)."\">";
 
                }
 
@@ -589,6 +598,18 @@ class Pref_Feeds extends Handler_Protected {
                                'dojoType="dijit.form.Select"');
                }
 
+               /* FTS Stemming Language */
+
+               if (DB_TYPE == "pgsql") {
+                       $feed_language = $this->dbh->fetch_result($result, 0, "feed_language");
+
+                       print "<hr/>";
+
+                       print __('Language:') . " ";
+                       print_select("feed_language", $feed_language, $this::$feed_languages,
+                               'dojoType="dijit.form.Select"');
+               }
+
                print "</div>";
 
                print "<div class=\"dlgSec\">".__("Update")."</div>";
@@ -613,25 +634,31 @@ class Pref_Feeds extends Handler_Protected {
                                ((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"'));
 
                print "</div>";
-               print "<div class=\"dlgSec\">".__("Authentication")."</div>";
-               print "<div class=\"dlgSecCont\">";
 
                $auth_login = htmlspecialchars($this->dbh->fetch_result($result, 0, "auth_login"));
-
-               print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\"
-                       placeHolder=\"".__("Login")."\"
-                       name=\"auth_login\" value=\"$auth_login\"><hr/>";
-
                $auth_pass = $this->dbh->fetch_result($result, 0, "auth_pass");
 
-               if ($auth_pass_encrypted) {
+               if ($auth_pass_encrypted && function_exists("mcrypt_decrypt")) {
                        require_once "crypt.php";
                        $auth_pass = decrypt_string($auth_pass);
                }
 
                $auth_pass = htmlspecialchars($auth_pass);
+               $auth_enabled = $auth_login !== '' || $auth_pass !== '';
+
+               $auth_style = $auth_enabled ? '' : 'display: none';
+               print "<div id='feedEditDlg_loginContainer' style='$auth_style'>";
+               print "<div class=\"dlgSec\">".__("Authentication")."</div>";
+               print "<div class=\"dlgSecCont\">";
+
+               print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\"
+                       placeHolder=\"".__("Login")."\"
+                       autocomplete=\"new-password\"
+                       name=\"auth_login\" value=\"$auth_login\"><hr/>";
+
 
                print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
+                       autocomplete=\"new-password\"
                        placeHolder=\"".__("Password")."\"
                        value=\"$auth_pass\">";
 
@@ -639,9 +666,19 @@ class Pref_Feeds extends Handler_Protected {
                        ".__('<b>Hint:</b> you need to fill in your login information if your feed requires authentication, except for Twitter feeds.')."
                        </div>";
 
-               print "</div>";
-               print "<div class=\"dlgSec\">".__("Options")."</div>";
-               print "<div class=\"dlgSecCont\">";
+               print "</div></div>";
+
+               $auth_checked = $auth_enabled ? 'checked' : '';
+               print "<div style=\"clear : both\">
+                               <input type=\"checkbox\" $auth_checked name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedEditDlg_loginCheck\"
+                                               onclick='checkboxToggleElement(this, \"feedEditDlg_loginContainer\")'>
+                                       <label for=\"feedEditDlg_loginCheck\">".
+                                       __('This feed requires authentication.')."</div>";
+
+               print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">';
+
+               //print "<div class=\"dlgSec\">".__("Options")."</div>";
+               print "<div class=\"dlgSecSimple\">";
 
                $private = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "private"));
 
@@ -703,7 +740,7 @@ class Pref_Feeds extends Handler_Protected {
                print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"cache_images\"
                name=\"cache_images\"
                        $checked>&nbsp;<label for=\"cache_images\">".
-               __('Cache images locally')."</label>";
+               __('Cache media')."</label>";
 
                $mark_unread_on_update = sql_bool_to_bool($this->dbh->fetch_result($result, 0, "mark_unread_on_update"));
 
@@ -719,10 +756,11 @@ class Pref_Feeds extends Handler_Protected {
 
                print "</div>";
 
+               print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Icon').'">';
+
                /* Icon */
 
-               print "<div class=\"dlgSec\">".__("Icon")."</div>";
-               print "<div class=\"dlgSecCont\">";
+               print "<div class=\"dlgSecSimple\">";
 
                print "<iframe name=\"icon_upload_iframe\"
                        style=\"width: 400px; height: 100px; display: none;\"></iframe>";
@@ -733,43 +771,37 @@ class Pref_Feeds extends Handler_Protected {
                        <input id=\"icon_file\" size=\"10\" name=\"icon_file\" type=\"file\">
                        <input type=\"hidden\" name=\"op\" value=\"pref-feeds\">
                        <input type=\"hidden\" name=\"feed_id\" value=\"$feed_id\">
-                       <input type=\"hidden\" name=\"method\" value=\"uploadicon\">
-                       <button dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\"
+                       <input type=\"hidden\" name=\"method\" value=\"uploadicon\"><p>
+                       <button class=\"\" dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\"
                                type=\"submit\">".__('Replace')."</button>
-                       <button dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\"
+                       <button class=\"\" dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\"
                                type=\"submit\">".__('Remove')."</button>
                        </form>";
 
                print "</div>";
 
+               print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Plugins').'">';
+
                PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED,
                        "hook_prefs_edit_feed", $feed_id);
 
+
+               print "</div></div>";
+
                $title = htmlspecialchars($title, ENT_QUOTES);
 
                print "<div class='dlgButtons'>
                        <div style=\"float : left\">
-                       <button dojoType=\"dijit.form.Button\" onclick='return unsubscribeFeed($feed_id, \"$title\")'>".
+                       <button class=\"danger\" dojoType=\"dijit.form.Button\" onclick='return unsubscribeFeed($feed_id, \"$title\")'>".
                                __('Unsubscribe')."</button>";
 
-               if (PUBSUBHUBBUB_ENABLED) {
-                       $pubsub_state = $this->dbh->fetch_result($result, 0, "pubsub_state");
-                       $pubsub_btn_disabled = ($pubsub_state == 2) ? "" : "disabled=\"1\"";
-
-                       print "<button dojoType=\"dijit.form.Button\" id=\"pubsubReset_Btn\" $pubsub_btn_disabled
-                                       onclick='return resetPubSub($feed_id, \"$title\")'>".__('Resubscribe to push updates').
-                                       "</button>";
-               }
-
                print "</div>";
 
-               print "<div dojoType=\"dijit.Tooltip\" connectId=\"pubsubReset_Btn\" position=\"below\">".
-                       __('Resets PubSubHubbub subscription status for push-enabled feeds.')."</div>";
-
                print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button>
                        <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button>
                </div>";
 
+
                return;
        }
 
@@ -783,47 +815,38 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<p>";
 
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"ids\" value=\"$feed_ids\">";
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchEditSave\">";
+               print_hidden("ids", "$feed_ids");
+               print_hidden("op", "pref-feeds");
+               print_hidden("method", "batchEditSave");
 
                print "<div class=\"dlgSec\">".__("Feed")."</div>";
                print "<div class=\"dlgSecCont\">";
 
-               /* Title */
-
-               print "<input dojoType=\"dijit.form.ValidationTextBox\"
-                       disabled=\"1\" style=\"font-size : 16px; width : 20em;\" required=\"1\"
-                       name=\"title\" value=\"$title\">";
-
-               $this->batch_edit_cbox("title");
-
-               /* Feed URL */
-
-               print "<br/>";
-
-               print __('URL:') . " ";
-               print "<input dojoType=\"dijit.form.ValidationTextBox\" disabled=\"1\"
-                       required=\"1\" regExp='^(http|https)://.*' style=\"width : 20em\"
-                       name=\"feed_url\" value=\"$feed_url\">";
-
-               $this->batch_edit_cbox("feed_url");
-
                /* Category */
 
                if (get_pref('ENABLE_FEED_CATS')) {
 
-                       print "<br/>";
-
                        print __('Place in category:') . " ";
 
-                       print_feed_cat_select("cat_id", $cat_id,
+                       print_feed_cat_select("cat_id", false,
                                'disabled="1" dojoType="dijit.form.Select"');
 
                        $this->batch_edit_cbox("cat_id");
 
                }
 
+               /* FTS Stemming Language */
+
+               if (DB_TYPE == "pgsql") {
+                       print "<hr/>";
+
+                       print __('Language:') . " ";
+                       print_select("feed_language", "", $this::$feed_languages,
+                               'disabled="1" dojoType="dijit.form.Select"');
+
+                       $this->batch_edit_cbox("feed_language");
+               }
+
                print "</div>";
 
                print "<div class=\"dlgSec\">".__("Update")."</div>";
@@ -831,7 +854,7 @@ class Pref_Feeds extends Handler_Protected {
 
                /* Update Interval */
 
-               print_select_hash("update_interval", $update_interval, $update_intervals,
+               print_select_hash("update_interval", "", $update_intervals,
                        'disabled="1" dojoType="dijit.form.Select"');
 
                $this->batch_edit_cbox("update_interval");
@@ -844,7 +867,7 @@ class Pref_Feeds extends Handler_Protected {
 
                        print __('Article purging:') . " ";
 
-                       print_select_hash("purge_interval", $purge_interval, $purge_intervals,
+                       print_select_hash("purge_interval", "", $purge_intervals,
                                'disabled="1" dojoType="dijit.form.Select"');
 
                        $this->batch_edit_cbox("purge_interval");
@@ -856,13 +879,15 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<input dojoType=\"dijit.form.TextBox\"
                        placeHolder=\"".__("Login")."\" disabled=\"1\"
-                       name=\"auth_login\" value=\"$auth_login\">";
+                       autocomplete=\"new-password\"
+                       name=\"auth_login\" value=\"\">";
 
                $this->batch_edit_cbox("auth_login");
 
-               print "<br/><input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
+               print "<hr/> <input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
+                       autocomplete=\"new-password\"
                        placeHolder=\"".__("Password")."\" disabled=\"1\"
-                       value=\"$auth_pass\">";
+                       value=\"\">";
 
                $this->batch_edit_cbox("auth_pass");
 
@@ -899,7 +924,7 @@ class Pref_Feeds extends Handler_Protected {
                        name=\"cache_images\"
                        dojoType=\"dijit.form.CheckBox\">&nbsp;<label class='insensitive' id=\"cache_images_l\"
                        for=\"cache_images\">".
-               __('Cache images locally')."</label>";
+               __('Cache media')."</label>";
 
                print "&nbsp;"; $this->batch_edit_cbox("cache_images", "cache_images_l");
 
@@ -955,13 +980,7 @@ class Pref_Feeds extends Handler_Protected {
                $mark_unread_on_update = checkbox_to_sql_bool(
                        $this->dbh->escape_string($_POST["mark_unread_on_update"]));
 
-               if (strlen(FEED_CRYPT_KEY) > 0) {
-                       require_once "crypt.php";
-                       $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
-                       $auth_pass_encrypted = 'true';
-               } else {
-                       $auth_pass_encrypted = 'false';
-               }
+               $feed_language = $this->dbh->escape_string(trim($_POST["feed_language"]));
 
                $auth_pass = $this->dbh->escape_string($auth_pass);
 
@@ -979,23 +998,37 @@ class Pref_Feeds extends Handler_Protected {
                }
 
                if (!$batch) {
+                       if ($_POST["need_auth"] !== 'on') {
+                               $auth_login = '';
+                               $auth_pass = '';
+                       }
+
+                       $result = db_query("SELECT feed_url FROM ttrss_feeds WHERE id = " . $feed_id);
+                       $orig_feed_url = db_fetch_result($result, 0, "feed_url");
 
-                       $result = $this->dbh->query("UPDATE ttrss_feeds SET
+                       $reset_basic_info = $orig_feed_url != $feed_link;
+
+                       $this->dbh->query("UPDATE ttrss_feeds SET
                                $category_qpart
                                title = '$feed_title', feed_url = '$feed_link',
                                update_interval = '$upd_intl',
                                purge_interval = '$purge_intl',
                                auth_login = '$auth_login',
                                auth_pass = '$auth_pass',
-                               auth_pass_encrypted = $auth_pass_encrypted,
+                               auth_pass_encrypted = false,
                                private = $private,
                                cache_images = $cache_images,
                                hide_images = $hide_images,
                                include_in_digest = $include_in_digest,
                                always_display_enclosures = $always_display_enclosures,
-                               mark_unread_on_update = $mark_unread_on_update
+                               mark_unread_on_update = $mark_unread_on_update,
+                               feed_language = '$feed_language'
                        WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]);
 
+                       if ($reset_basic_info) {
+                               RSSUtils::set_basic_feed_info($feed_id);
+                       }
+
                        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_SAVE_FEED,
                                "hook_prefs_save_feed", $feed_id);
 
@@ -1036,8 +1069,7 @@ class Pref_Feeds extends Handler_Protected {
                                                break;
 
                                        case "auth_pass":
-                                               $qpart = "auth_pass = '$auth_pass' AND
-                                                       auth_pass_encrypted = $auth_pass_encrypted";
+                                               $qpart = "auth_pass = '$auth_pass', auth_pass_encrypted = false";
                                                break;
 
                                        case "private":
@@ -1068,6 +1100,10 @@ class Pref_Feeds extends Handler_Protected {
                                                $qpart = $category_qpart_nocomma;
                                                break;
 
+                                       case "feed_language":
+                                               $qpart = "feed_language = '$feed_language'";
+                                               break;
+
                                }
 
                                if ($qpart) {
@@ -1083,16 +1119,6 @@ class Pref_Feeds extends Handler_Protected {
                return;
        }
 
-       function resetPubSub() {
-
-               $ids = $this->dbh->escape_string($_REQUEST["ids"]);
-
-               $this->dbh->query("UPDATE ttrss_feeds SET pubsub_state = 0 WHERE id IN ($ids)
-                       AND owner_uid = " . $_SESSION["uid"]);
-
-               return;
-       }
-
        function remove() {
 
                $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
@@ -1110,8 +1136,6 @@ class Pref_Feeds extends Handler_Protected {
        }
 
        function rescore() {
-               require_once "rssfuncs.php";
-
                $ids = explode(",", $this->dbh->escape_string($_REQUEST["ids"]));
 
                foreach ($ids as $id) {
@@ -1131,13 +1155,13 @@ class Pref_Feeds extends Handler_Protected {
 
                        while ($line = $this->dbh->fetch_assoc($result)) {
 
-                               $tags = get_article_tags($line["ref_id"]);
+                               $tags = Article::get_article_tags($line["ref_id"]);
 
-                               $article_filters = get_article_filters($filters, $line['title'],
+                               $article_filters = RSSUtils::get_article_filters($filters, $line['title'],
                                        $line['content'], $line['link'], strtotime($line['updated']),
                                        $line['author'], $tags);
 
-                               $new_score = calculate_article_score($article_filters);
+                               $new_score = RSSUtils::calculate_article_score($article_filters);
 
                                if (!$scores[$new_score]) $scores[$new_score] = array();
 
@@ -1188,13 +1212,13 @@ class Pref_Feeds extends Handler_Protected {
 
                        while ($line = $this->dbh->fetch_assoc($tmp_result)) {
 
-                               $tags = get_article_tags($line["ref_id"]);
+                               $tags = Article::get_article_tags($line["ref_id"]);
 
-                               $article_filters = get_article_filters($filters, $line['title'],
+                               $article_filters = RSSUtils::get_article_filters($filters, $line['title'],
                                        $line['content'], $line['link'], strtotime($line['updated']),
                                        $line['author'], $tags);
 
-                               $new_score = calculate_article_score($article_filters);
+                               $new_score = RSSUtils::calculate_article_score($article_filters);
 
                                if (!$scores[$new_score]) $scores[$new_score] = array();
 
@@ -1271,25 +1295,11 @@ class Pref_Feeds extends Handler_Protected {
                                __("Feeds with errors") . "</button>";
                }
 
-               if (DB_TYPE == "pgsql") {
-                       $interval_qpart = "NOW() - INTERVAL '3 months'";
-               } else {
-                       $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
-               }
-
-               $result = $this->dbh->query("SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE
-                                       (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
-                                               ttrss_entries.id = ref_id AND
-                                                       ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND
-                       ttrss_feeds.owner_uid = ".$_SESSION["uid"]);
-
-               $num_inactive = $this->dbh->fetch_result($result, 0, "num_inactive");
-
-               if ($num_inactive > 0) {
-                       $inactive_button = "<button dojoType=\"dijit.form.Button\"
-                                       onclick=\"showInactiveFeeds()\">" .
-                                       __("Inactive feeds") . "</button>";
-               }
+               $inactive_button = "<button dojoType=\"dijit.form.Button\"
+                               id=\"pref_feeds_inactive_btn\"
+                               style=\"display : none\"
+                               onclick=\"showInactiveFeeds()\">" .
+                               __("Inactive feeds") . "</button>";
 
                $feed_search = $this->dbh->escape_string($_REQUEST["search"]);
 
@@ -1351,9 +1361,6 @@ class Pref_Feeds extends Handler_Protected {
                print $error_button;
                print $inactive_button;
 
-               print "<button onclick=\"toggleHiddenFeedCats()\"
-                       dojoType=\"dijit.form.Button\">".__('(Un)hide empty categories')."</button>";
-
                if (defined('_ENABLE_FEED_DEBUGGING')) {
 
                        print "<select id=\"feedActionChooser\" onchange=\"feedActionChange()\">
@@ -1391,6 +1398,7 @@ class Pref_Feeds extends Handler_Protected {
                <div dojoType=\"fox.PrefFeedTree\" id=\"feedTree\"
                        dndController=\"dijit.tree.dndSource\"
                        betweenThreshold=\"5\"
+                       autoExpand='true'
                        model=\"feedModel\" openOnClick=\"false\">
                <script type=\"dojo/method\" event=\"onClick\" args=\"item\">
                        var id = String(item.id);
@@ -1404,6 +1412,8 @@ class Pref_Feeds extends Handler_Protected {
                </script>
                <script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
                        Element.hide(\"feedlistLoading\");
+
+                       checkInactiveFeeds();
                </script>
                </div>";
 
@@ -1418,9 +1428,8 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('OPML')."\">";
 
-               print "<p>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . " ";
-
-               print __("Only main settings profile can be migrated using OPML.") . "</p>";
+               print "<p>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") .
+                       __("Only main settings profile can be migrated using OPML.") . "</p>";
 
                print "<iframe id=\"upload_iframe\"
                        name=\"upload_iframe\" onload=\"opmlImportComplete(this)\"
@@ -1437,8 +1446,10 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<hr>";
 
+               $opml_export_filename = "TinyTinyRSS_".date("Y-m-d").".opml";
+
                print "<p>" . __('Filename:') .
-            " <input type=\"text\" id=\"filename\" value=\"TinyTinyRSS.opml\" />&nbsp;" .
+            " <input type=\"text\" id=\"filename\" value=\"$opml_export_filename\" />&nbsp;" .
                                __('Include settings') . "<input type=\"checkbox\" id=\"settings\" checked=\"1\"/>";
 
                print "</p><button dojoType=\"dijit.form.Button\"
@@ -1447,9 +1458,9 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<hr>";
 
-               print "<p>".__('Your OPML can be published publicly and can be subscribed by anyone who knows the URL below.') . " ";
+               print "<p>" . __('Your OPML can be published publicly and can be subscribed by anyone who knows the URL below.') . "</p>";
 
-               print __("Published OPML does not include your Tiny Tiny RSS settings, feeds that require authentication or feeds hidden from Popular feeds.") . "</p>";
+               print_warning("Published OPML does not include your Tiny Tiny RSS settings, feeds that require authentication or feeds hidden from Popular feeds.");
 
                print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('".__("Public OPML URL")."','pubOPMLUrl')\">".
                        __('Display published OPML URL')."</button> ";
@@ -1463,13 +1474,13 @@ class Pref_Feeds extends Handler_Protected {
 
                        print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Firefox integration')."\">";
 
-                       print "<p>" . __('This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the link below.') . "</p>";
+                       print_notice(__('This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the link below.'));
 
                        print "<p>";
 
                        print "<button onclick='window.navigator.registerContentHandler(" .
                       "\"application/vnd.mozilla.maybe.feed\", " .
-                      "\"" . add_feed_url() . "\", " . " \"Tiny Tiny RSS\")'>" .
+                      "\"" . $this->subscribe_to_feed_url() . "\", " . " \"Tiny Tiny RSS\")'>" .
                                                         __('Click here to register this site as a feed reader.') .
                                "</button>";
 
@@ -1480,25 +1491,20 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Published & shared articles / Generated feeds')."\">";
 
-               print "<h3>" . __("Published articles and generated feeds") . "</h3>";
-
-               print "<p>".__('Published articles are exported as a public RSS feed and can be subscribed by anyone who knows the URL specified below.')."</p>";
+               print "<p>" . __('Published articles are exported as a public RSS feed and can be subscribed by anyone who knows the URL specified below.') . "</p>";
 
                $rss_url = '-2::' . htmlspecialchars(get_self_url_prefix() .
                                "/public.php?op=rss&id=-2&view-mode=all_articles");;
 
+               print "<p>";
+
                print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('".__("View as RSS")."','generatedFeed', '$rss_url')\">".
                        __('Display URL')."</button> ";
 
-               print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearFeedAccessKeys()\">".
+               print "<button class=\"warning\" dojoType=\"dijit.form.Button\" onclick=\"return clearFeedAccessKeys()\">".
                        __('Clear all generated URLs')."</button> ";
 
-               print "<h3>" . __("Articles shared by URL") . "</h3>";
-
-               print "<p>" . __("You can disable all articles shared by unique URLs here.") . "</p>";
-
-               print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearArticleAccessKeys()\">".
-                       __('Unshare all articles')."</button> ";
+               print "</p>";
 
                PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
                        "hook_prefs_tab_section", "prefFeedsPublishedGenerated");
@@ -1516,14 +1522,14 @@ class Pref_Feeds extends Handler_Protected {
                $cat_id = (int) $cat_id;
 
                if ($cat_id > 0) {
-                       $cat_unread = ccache_find($cat_id, $_SESSION["uid"], true);
+                       $cat_unread = CCache::find($cat_id, $_SESSION["uid"], true);
                } else if ($cat_id == 0 || $cat_id == -2) {
-                       $cat_unread = getCategoryUnread($cat_id);
+                       $cat_unread = Feeds::getCategoryUnread($cat_id);
                }
 
                $obj['id'] = 'CAT:' . $cat_id;
                $obj['items'] = array();
-               $obj['name'] = getCategoryTitle($cat_id);
+               $obj['name'] = Feeds::getCategoryTitle($cat_id);
                $obj['type'] = 'category';
                $obj['unread'] = (int) $cat_unread;
                $obj['bare_id'] = $cat_id;
@@ -1536,7 +1542,7 @@ class Pref_Feeds extends Handler_Protected {
                $feed_id = (int) $feed_id;
 
                if (!$title)
-                       $title = getFeedTitle($feed_id, false);
+                       $title = Feeds::getFeedTitle($feed_id, false);
 
                if ($unread === false)
                        $unread = getFeedUnread($feed_id, false);
@@ -1547,8 +1553,9 @@ class Pref_Feeds extends Handler_Protected {
                $obj['type'] = 'feed';
                $obj['error'] = $error;
                $obj['updated'] = $updated;
-               $obj['icon'] = getFeedIcon($feed_id);
+               $obj['icon'] = Feeds::getFeedIcon($feed_id);
                $obj['bare_id'] = $feed_id;
+               $obj['auxcounter'] = 0;
 
                return $obj;
        }
@@ -1573,7 +1580,7 @@ class Pref_Feeds extends Handler_Protected {
                        GROUP BY ttrss_feeds.title, ttrss_feeds.id, ttrss_feeds.site_url, ttrss_feeds.feed_url
                        ORDER BY last_article");
 
-               print "<h2" .__("These feeds have not been updated with new content for 3 months (oldest first):") . "</h2>";
+               print "<p" .__("These feeds have not been updated with new content for 3 months (oldest first):") . "</p>";
 
                print "<div dojoType=\"dijit.Toolbar\">";
                print "<div dojoType=\"dijit.form.DropDownButton\">".
@@ -1600,8 +1607,6 @@ class Pref_Feeds extends Handler_Protected {
                        # class needed for selectTableRows()
                        print "<tr class=\"placeholder\" $this_row_id>";
 
-                       $edit_title = htmlspecialchars($line["title"]);
-
                        # id needed for selectTableRows()
                        print "<td width='5%' align='center'><input
                                onclick='toggleSelectRow2(this);' dojoType=\"dijit.form.CheckBox\"
@@ -1626,7 +1631,7 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<div class='dlgButtons'>";
                print "<div style='float : left'>";
-               print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('inactiveFeedsDlg').removeSelected()\">"
+               print "<button class=\"danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('inactiveFeedsDlg').removeSelected()\">"
                        .__('Unsubscribe from selected feeds')."</button> ";
                print "</div>";
 
@@ -1638,9 +1643,6 @@ class Pref_Feeds extends Handler_Protected {
        }
 
        function feedsWithErrors() {
-               print "<h2>" . __("These feeds have not been updated because of errors:") .
-                       "</h2>";
-
                $result = $this->dbh->query("SELECT id,title,feed_url,last_error,site_url
                FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
 
@@ -1669,8 +1671,6 @@ class Pref_Feeds extends Handler_Protected {
                        # class needed for selectTableRows()
                        print "<tr class=\"placeholder\" $this_row_id>";
 
-                       $edit_title = htmlspecialchars($line["title"]);
-
                        # id needed for selectTableRows()
                        print "<td width='5%' align='center'><input
                                onclick='toggleSelectRow2(this);' dojoType=\"dijit.form.CheckBox\"
@@ -1697,7 +1697,7 @@ class Pref_Feeds extends Handler_Protected {
 
                print "<div class='dlgButtons'>";
                print "<div style='float : left'>";
-               print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').removeSelected()\">"
+               print "<button class=\"danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').removeSelected()\">"
                        .__('Unsubscribe from selected feeds')."</button> ";
                print "</div>";
 
@@ -1727,7 +1727,7 @@ class Pref_Feeds extends Handler_Protected {
                $result = $this->dbh->query("DELETE FROM ttrss_entries WHERE
                        (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
 
-               ccache_update($id, $_SESSION['uid']);
+               CCache::update($id, $_SESSION['uid']);
        } // function clear_feed_articles
 
        private function remove_feed_category($id, $owner_uid) {
@@ -1735,7 +1735,7 @@ class Pref_Feeds extends Handler_Protected {
                $this->dbh->query("DELETE FROM ttrss_feed_categories
                        WHERE id = '$id' AND owner_uid = $owner_uid");
 
-               ccache_remove($id, $owner_uid, true);
+               CCache::remove($id, $owner_uid, true);
        }
 
        static function remove_feed($id, $owner_uid) {
@@ -1790,17 +1790,17 @@ class Pref_Feeds extends Handler_Protected {
                                unlink(ICONS_DIR . "/$id.ico");
                        }
 
-                       ccache_remove($id, $owner_uid);
+                       CCache::remove($id, $owner_uid);
 
                } else {
-                       label_remove(feed_to_label_id($id), $owner_uid);
-                       //ccache_remove($id, $owner_uid); don't think labels are cached
+                       Labels::remove(Labels::feed_to_label_id($id), $owner_uid);
+                       //CCache::remove($id, $owner_uid); don't think labels are cached
                }
        }
 
        function batchSubscribe() {
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
-               print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchaddfeeds\">";
+               print_hidden("op", "pref-feeds");
+               print_hidden("method", "batchaddfeeds");
 
                print "<table width='100%'><tr><td>
                        ".__("Add one valid RSS feed per line (no feed detection is done)")."
@@ -1811,7 +1811,7 @@ class Pref_Feeds extends Handler_Protected {
                }
                print "</td></tr><tr><td colspan='2'>";
                print "<textarea
-                       style='font-size : 12px; width : 100%; height: 200px;'
+                       style='font-size : 12px; width : 98%; height: 200px;'
                        placeHolder=\"".__("Feeds to subscribe, One per line")."\"
                        dojoType=\"dijit.form.SimpleTextarea\" required=\"1\" name=\"feeds\"></textarea>";
 
@@ -1825,6 +1825,7 @@ class Pref_Feeds extends Handler_Protected {
                                " <input
                                        placeHolder=\"".__("Password")."\"
                                        dojoType=\"dijit.form.TextBox\" type='password'
+                                       autocomplete=\"new-password\"
                                        style=\"width : 10em;\" name='pass'\">".
                                "</div>";
 
@@ -1869,14 +1870,6 @@ class Pref_Feeds extends Handler_Protected {
                                        "SELECT id FROM ttrss_feeds
                                        WHERE feed_url = '$feed' AND owner_uid = ".$_SESSION["uid"]);
 
-                               if (strlen(FEED_CRYPT_KEY) > 0) {
-                                       require_once "crypt.php";
-                                       $pass = substr(encrypt_string($pass), 0, 250);
-                                       $auth_pass_encrypted = 'true';
-                               } else {
-                                       $auth_pass_encrypted = 'false';
-                               }
-
                                $pass = $this->dbh->escape_string($pass);
 
                                if ($this->dbh->num_rows($result) == 0) {
@@ -1884,7 +1877,7 @@ class Pref_Feeds extends Handler_Protected {
                                                "INSERT INTO ttrss_feeds
                                                        (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted)
                                                VALUES ('".$_SESSION["uid"]."', '$feed',
-                                                       '[Unknown]', $cat_qpart, '$login', '$pass', 0, $auth_pass_encrypted)");
+                                                       '[Unknown]', $cat_qpart, '$login', '$pass', 0, false)");
                                }
 
                                $this->dbh->query("COMMIT");
@@ -1921,7 +1914,7 @@ class Pref_Feeds extends Handler_Protected {
                        AND owner_uid = " . $owner_uid);
 
                if ($this->dbh->num_rows($result) == 1) {
-                       $key = $this->dbh->escape_string(sha1(uniqid(rand(), true)));
+                       $key = $this->dbh->escape_string(uniqid_short());
 
                        $this->dbh->query("UPDATE ttrss_access_keys SET access_key = '$key'
                                WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
@@ -1940,6 +1933,40 @@ class Pref_Feeds extends Handler_Protected {
                        owner_uid = " . $_SESSION["uid"]);
        }
 
+       private function calculate_children_count($cat) {
+               $c = 0;
+
+               foreach ($cat['items'] as $child) {
+                       if ($child['type'] == 'category') {
+                               $c += $this->calculate_children_count($child);
+                       } else {
+                               $c += 1;
+                       }
+               }
+
+               return $c;
+       }
+
+       function getinactivefeeds() {
+               if (DB_TYPE == "pgsql") {
+                       $interval_qpart = "NOW() - INTERVAL '3 months'";
+               } else {
+                       $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
+               }
+
+               $result = $this->dbh->query("SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE
+                               (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
+                                       ttrss_entries.id = ref_id AND
+                                               ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND
+                         ttrss_feeds.owner_uid = ".$_SESSION["uid"]);
+
+               print (int) $this->dbh->fetch_result($result, 0, "num_inactive");
+       }
+
+       static function subscribe_to_feed_url() {
+               $url_path = get_self_url_prefix() .
+                       "/public.php?op=subscribe&feed_url=%s";
+               return $url_path;
+       }
 
-}
-?>
+}
\ No newline at end of file