]> git.wh0rd.org - tt-rss.git/blobdiff - include/functions.php
Added functionality for navigation without opening articles and toggling expansion...
[tt-rss.git] / include / functions.php
index 9125247b12a1f6edaea4f45a33bf6e0b6d09b39b..d328ea19d99d870730fd075065d8c89c300ad134 100644 (file)
@@ -1,10 +1,12 @@
 <?php
        define('EXPECTED_CONFIG_VERSION', 26);
-       define('SCHEMA_VERSION', 109);
+       define('SCHEMA_VERSION', 115);
 
        define('LABEL_BASE_INDEX', -1024);
+       define('PLUGIN_FEED_BASE_INDEX', -128);
 
        $fetch_last_error = false;
+       $fetch_last_error_code = false;
        $pluginhost = false;
 
        function __autoload($class) {
 
        require_once 'config.php';
 
+       /**
+        * Define a constant if not already defined
+        *
+        * @param string $name The constant name.
+        * @param mixed $value The constant value.
+        * @access public
+        * @return boolean True if defined successfully or not.
+        */
+       function define_default($name, $value) {
+               defined($name) or define($name, $value);
+       }
+
+       ///// Some defaults that you can override in config.php //////
+
+       define_default('FEED_FETCH_TIMEOUT', 45);
+       // How may seconds to wait for response when requesting feed from a site
+       define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
+       // How may seconds to wait for response when requesting feed from a
+       // site when that feed wasn't cached before
+       define_default('FILE_FETCH_TIMEOUT', 45);
+       // Default timeout when fetching files from remote sites
+       define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
+       // How many seconds to wait for initial response from website when
+       // fetching files from remote sites
+
        if (DB_TYPE == "pgsql") {
                define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
        } else {
                define('SUBSTRING_FOR_DATE', 'SUBSTRING');
        }
 
-       define('THEME_VERSION_REQUIRED', 1.1);
-
        /**
         * Return available translations names.
         *
@@ -60,7 +85,9 @@
                                        "pl_PL" => "Polski",
                                        "ru_RU" => "Русский",
                                        "pt_BR" => "Portuguese/Brazil",
-                                       "zh_CN" => "Simplified Chinese");
+                                       "zh_CN" => "Simplified Chinese",
+                                       "sv_SE" => "Svenska",
+                                       "fi_FI" => "Suomi");
 
                return $tr;
        }
                }
        }
 
-       function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false) {
+       function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
 
                global $fetch_last_error;
+               global $fetch_last_error_code;
 
-               if (function_exists('curl_init') && !ini_get("open_basedir")) {
+               if (!defined('NO_CURL') && function_exists('curl_init') && !ini_get("open_basedir")) {
 
                        if (ini_get("safe_mode")) {
                                $ch = curl_init(geturl($url));
                                $ch = curl_init($url);
                        }
 
-                       curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : 15);
-                       curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : 45);
+                       if ($timestamp) {
+                               curl_setopt($ch, CURLOPT_HTTPHEADER,
+                                       array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
+                       }
+
+                       curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
+                       curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : FILE_FETCH_TIMEOUT);
                        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, !ini_get("safe_mode"));
                        curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
                        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
                        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
                        $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
 
+                       $fetch_last_error_code = $http_code;
+
                        if ($http_code != 200 || $type && strpos($content_type, "$type") === false) {
                                if (curl_errno($ch) != 0) {
                                        $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
 
                        $data = @file_get_contents($url);
 
-                       @$gzdecoded = gzdecode($data);
-                       if ($gzdecoded) $data = $gzdecoded;
-
                        if (!$data && function_exists('error_get_last')) {
                                $error = error_get_last();
                                $fetch_last_error = $error["message"];
                        if (array_search($line["pref_name"], $active_prefs) === FALSE) {
 //                             print "adding " . $line["pref_name"] . "<br>";
 
+                               $line["def_value"] = db_escape_string($link, $line["def_value"]);
+                               $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
+
                                if (get_schema_version($link) < 63) {
                                        db_query($link, "INSERT INTO ttrss_user_prefs
                                                (owner_uid,pref_name,value) VALUES
        function authenticate_user($link, $login, $password, $check_only = false) {
 
                if (!SINGLE_USER_MODE) {
-
                        $user_id = false;
 
                        global $pluginhost;
                        }
 
                        if ($user_id && !$check_only) {
+                               @session_start();
+
                                $_SESSION["uid"] = $user_id;
+                               $_SESSION["version"] = VERSION;
 
                                $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
                                        WHERE id = '$user_id'");
                                        $_SESSION["uid"]);
 
                                $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
+                               $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
                                $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
 
                                $_SESSION["last_version_check"] = time();
                return $csrf_token == $_SESSION['csrf_token'];
        }
 
-       function validate_session($link) {
-               if (SINGLE_USER_MODE) return true;
-
-               $check_ip = $_SESSION['ip_address'];
-
-               switch (SESSION_CHECK_ADDRESS) {
-               case 0:
-                       $check_ip = '';
-                       break;
-               case 1:
-                       $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+1);
-                       break;
-               case 2:
-                       $check_ip = substr($check_ip, 0, strrpos($check_ip, '.'));
-                       $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+1);
-                       break;
-               };
-
-               if ($check_ip && strpos($_SERVER['REMOTE_ADDR'], $check_ip) !== 0) {
-                       $_SESSION["login_error_msg"] =
-                               __("Session failed to validate (incorrect IP)");
-                       return false;
-               }
-
-               if ($_SESSION["ref_schema_version"] != get_schema_version($link, true))
-                       return false;
-
-               if ($_SESSION["uid"]) {
-
-                       $result = db_query($link,
-                               "SELECT pwd_hash FROM ttrss_users WHERE id = '".$_SESSION["uid"]."'");
-
-                       $pwd_hash = db_fetch_result($result, 0, "pwd_hash");
-
-                       if ($pwd_hash != $_SESSION["pwd_hash"]) {
-                               return false;
-                       }
-               }
-
-/*             if ($_SESSION["cookie_lifetime"] && $_SESSION["uid"]) {
-
-                       //print_r($_SESSION);
-
-                       if (time() > $_SESSION["cookie_lifetime"]) {
-                               return false;
-                       }
-               } */
-
-               return true;
-       }
-
        function load_user_plugins($link, $owner_uid) {
                if ($owner_uid) {
                        $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
                $_SESSION["prefs_cache"] = false;
 
                if (SINGLE_USER_MODE) {
+                       @session_start();
                        authenticate_user($link, "admin", null);
                        cache_prefs($link);
                        load_user_plugins($link, $_SESSION["uid"]);
                } else {
-                       if (!$_SESSION["uid"] || !validate_session($link)) {
+                       if (!validate_session($link)) $_SESSION["uid"] = false;
+
+                       if (!$_SESSION["uid"]) {
 
                                if (AUTH_AUTO_LOGIN && authenticate_user($link, null, null)) {
                                    $_SESSION["ref_schema_version"] = get_schema_version($link, true);
                                         authenticate_user($link, null, null, true);
                                }
 
-                               if (!$_SESSION["uid"]) render_login_form($link);
+                               if (!$_SESSION["uid"]) {
+                                       @session_destroy();
+                                       setcookie(session_name(), '', time()-42000, '/');
+
+                                       render_login_form($link);
+                                       exit;
+                               }
 
                        } else {
                                /* bump login timestamp */
                }
        }
 
-       function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false) {
+       function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
 
                        if (!$owner_uid) $owner_uid = $_SESSION['uid'];
 
                        //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
 
-                       $ref_check_qpart = ($max_id &&
-                               !get_pref($link, 'REVERSE_HEADLINES')) ? "ref_id <= '$max_id'" : "true";
+                       // Todo: all this interval stuff needs some generic generator function
+
+                       $date_qpart = "false";
+
+                       switch ($mode) {
+                       case "1day":
+                               if (DB_TYPE == "pgsql") {
+                                       $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
+                               } else {
+                                       $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
+                               }
+                               break;
+                       case "1week":
+                               if (DB_TYPE == "pgsql") {
+                                       $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
+                               } else {
+                                       $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
+                               }
+                               break;
+                       case "2weeks":
+                               if (DB_TYPE == "pgsql") {
+                                       $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
+                               } else {
+                                       $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
+                               }
+                               break;
+                       default:
+                               $date_qpart = "true";
+                       }
 
                        if (is_numeric($feed)) {
                                if ($cat_view) {
                                                }
 
                                                db_query($link, "UPDATE ttrss_user_entries
-                                                       SET unread = false,last_read = NOW()
-                                                       WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart)
-                                                       AND $ref_check_qpart AND unread = true
-                                                       AND owner_uid = $owner_uid");
+                                                       SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                               (SELECT id FROM
+                                                                       (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
+                                                                               AND owner_uid = $owner_uid AND unread = true AND feed_id IN
+                                                                                       (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
 
                                        } else if ($feed == -2) {
 
                                                db_query($link, "UPDATE ttrss_user_entries
                                                        SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
                                                                FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
-                                                               AND $ref_check_qpart
-                                                               AND unread = true AND owner_uid = $owner_uid");
+                                                               AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
                                        }
 
                                } else if ($feed > 0) {
 
                                        db_query($link, "UPDATE ttrss_user_entries
-                                                       SET unread = false,last_read = NOW()
-                                                       WHERE feed_id = '$feed'
-                                                       AND $ref_check_qpart AND unread = true
-                                                       AND owner_uid = $owner_uid");
+                                               SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                       (SELECT id FROM
+                                                               (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
+                                                                       AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
 
                                } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred
 
                                        if ($feed == -1) {
                                                db_query($link, "UPDATE ttrss_user_entries
-                                                       SET unread = false,last_read = NOW()
-                                                       WHERE marked = true
-                                                       AND $ref_check_qpart AND unread = true
-                                                       AND owner_uid = $owner_uid");
+                                                       SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                               (SELECT id FROM
+                                                                       (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
+                                                                               AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
                                        }
 
                                        if ($feed == -2) {
                                                db_query($link, "UPDATE ttrss_user_entries
-                                                       SET unread = false,last_read = NOW()
-                                                       WHERE published = true
-                                                       AND $ref_check_qpart AND unread = true
-                                                       AND owner_uid = $owner_uid");
+                                                       SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                               (SELECT id FROM
+                                                                       (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
+                                                                               AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
                                        }
 
                                        if ($feed == -3) {
                                                $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
 
                                                if (DB_TYPE == "pgsql") {
-                                                       $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
+                                                       $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
                                                } else {
-                                                       $match_part = "updated > DATE_SUB(NOW(),
+                                                       $match_part = "date_entered > DATE_SUB(NOW(),
                                                                INTERVAL $intl HOUR) ";
                                                }
 
-                                               $result = db_query($link, "SELECT id FROM ttrss_entries,
-                                                       ttrss_user_entries WHERE $match_part AND
-                                                       unread = true AND
-                                                       ttrss_user_entries.ref_id = ttrss_entries.id AND
-                                                       owner_uid = $owner_uid");
-
-                                               $affected_ids = array();
-
-                                               while ($line = db_fetch_assoc($result)) {
-                                                       array_push($affected_ids, $line["id"]);
-                                               }
-
-                                               catchupArticlesById($link, $affected_ids, 0);
+                                               db_query($link, "UPDATE ttrss_user_entries
+                                                       SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                               (SELECT id FROM
+                                                                       (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
+                                                                               AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
                                        }
 
                                        if ($feed == -4) {
                                                db_query($link, "UPDATE ttrss_user_entries
-                                                       SET unread = false,last_read = NOW()
-                                                       WHERE $ref_check_qpart AND unread = true AND
-                                                       owner_uid = $owner_uid");
+                                                       SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                               (SELECT id FROM
+                                                                       (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
+                                                                               AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
                                        }
 
                                } else if ($feed < LABEL_BASE_INDEX) { // label
 
                                        $label_id = feed_to_label_id($feed);
 
-                                       db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
-                                               SET unread = false, last_read = NOW()
-                                                       WHERE label_id = '$label_id' AND unread = true
-                                                       AND $ref_check_qpart
-                                                       AND owner_uid = '$owner_uid' AND ref_id = article_id");
+                                       db_query($link, "UPDATE ttrss_user_entries
+                                               SET unread = false, last_read = NOW() WHERE ref_id IN
+                                                       (SELECT id FROM
+                                                               (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
+                                                                       AND label_id = '$label_id' AND ref_id = article_id
+                                                                       AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
 
                                }
 
                                ccache_update($link, $feed, $owner_uid, $cat_view);
 
                        } else { // tag
-                               db_query($link, "BEGIN");
+                               db_query($link, "UPDATE ttrss_user_entries
+                                       SET unread = false, last_read = NOW() WHERE ref_id IN
+                                               (SELECT id FROM
+                                                       (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
+                                                               AND post_int_id = int_id AND tag_name = '$feed'
+                                                               AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
 
-                               $tag_name = db_escape_string($link, $feed);
-
-                               $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
-                                       WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
-
-                               while ($line = db_fetch_assoc($result)) {
-                                       db_query($link, "UPDATE ttrss_user_entries SET
-                                               unread = false, last_read = NOW()
-                                               WHERE $ref_check_qpart AND unread = true
-                                               AND int_id = " . $line["post_int_id"]);
-                               }
-                               db_query($link, "COMMIT");
                        }
        }
 
                        array_push($ret_arr, $cv);
                }
 
+               global $pluginhost;
+
+               if ($pluginhost) {
+                       $feeds = $pluginhost->get_feeds(-1);
+
+                       if (is_array($feeds)) {
+                               foreach ($feeds as $feed) {
+                                       $cv = array("id" => PluginHost::pfeed_to_feed_id($feed['id']),
+                                               "counter" => $feed['sender']->get_unread($feed['id']));
+
+                                       array_push($ret_arr, $cv);
+                               }
+                       }
+               }
+
                return $ret_arr;
        }
 
 
                foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
                        "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
-                       "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
+                       "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
                        "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
 
                                 $params[strtolower($param)] = (int) get_pref($link, $param);
                                "prev_article" => __("Open previous article"),
                                "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
                                "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
+                               "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
+                               "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
                                "search_dialog" => __("Show search dialog")),
                        __("Article") => array(
                                "toggle_mark" => __("Toggle starred"),
                                "select_article_cursor" => __("Select article under cursor"),
                                "email_article" => __("Email article"),
                                "close_article" => __("Close/collapse article"),
+                               "toggle_expand" => __("Toggle article expansion (combined mode)"),
                                "toggle_widescreen" => __("Toggle widescreen mode"),
                                "toggle_embed_original" => __("Toggle embed original")),
                        __("Article selection") => array(
                                "feed_debug_update" => __("Debug feed update"),
                                "catchup_all" => __("Mark all feeds as read"),
                                "cat_toggle_collapse" => __("Un/collapse current category"),
-                               "toggle_combined_mode" => __("Toggle combined mode")),
+                               "toggle_combined_mode" => __("Toggle combined mode"),
+                               "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
                        __("Go to") => array(
                                "goto_all" => __("All articles"),
                                "goto_fresh" => __("Fresh"),
                                "help_dialog" => __("Show help dialog"))
                        );
 
+               global $pluginhost;
+               foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_INFO) as $plugin) {
+                       $hotkeys = $plugin->hook_hotkey_info($hotkeys);
+               }
+
                return $hotkeys;
        }
 
                                "f x" => "feed_reverse",
                                "f *d" => "feed_debug_update",
                                "f *c" => "toggle_combined_mode",
+                               "f c" => "toggle_cdm_expanded",
                                "*q" => "catchup_all",
                                "x" => "cat_toggle_collapse",
 //                     "goto" => array(
 
                        $commandpair = explode(":", mb_strtolower($k), 2);
 
-                       if ($commandpair[0] == "note" && $commandpair[1]) {
-
-                               if ($commandpair[1] == "true")
-                                       array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
-                               else
-                                       array_push($query_keywords, "($not (note IS NULL OR note = ''))");
-
-                       } else if ($commandpair[0] == "star" && $commandpair[1]) {
-
-                               if ($commandpair[1] == "true")
-                                       array_push($query_keywords, "($not (marked = true))");
-                               else
-                                       array_push($query_keywords, "($not (marked = false))");
-
-                       } else if ($commandpair[0] == "pub" && $commandpair[1]) {
+                       switch ($commandpair[0]) {
+                       case "title":
+                               if ($commandpair[1]) {
+                                       array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
+                                               db_escape_string($link, mb_strtolower($commandpair[1]))."%'))");
+                               }
+                               break;
+                       case "author":
+                               if ($commandpair[1]) {
+                                       array_push($query_keywords, "($not (LOWER(author) LIKE '%".
+                                               db_escape_string($link, mb_strtolower($commandpair[1]))."%'))");
+                               }
+                               break;
+                       case "note":
+                               if ($commandpair[1]) {
+                                       if ($commandpair[1] == "true")
+                                               array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
+                                       else if ($commandpair[1] == "false")
+                                               array_push($query_keywords, "($not (note IS NULL OR note = ''))");
+                                       else
+                                               array_push($query_keywords, "($not (LOWER(note) LIKE '%".
+                                                       db_escape_string($link, mb_strtolower($commandpair[1]))."%'))");
+                               }
+                               break;
+                       case "star":
 
-                               if ($commandpair[1] == "true")
-                                       array_push($query_keywords, "($not (published = true))");
-                               else
-                                       array_push($query_keywords, "($not (published = false))");
+                               if ($commandpair[1]) {
+                                       if ($commandpair[1] == "true")
+                                               array_push($query_keywords, "($not (marked = true))");
+                                       else
+                                               array_push($query_keywords, "($not (marked = false))");
+                               }
+                               break;
+                       case "pub":
+                               if ($commandpair[1]) {
+                                       if ($commandpair[1] == "true")
+                                               array_push($query_keywords, "($not (published = true))");
+                                       else
+                                               array_push($query_keywords, "($not (published = false))");
 
-                       } else if (strpos($k, "@") === 0) {
+                               }
+                               break;
+                       default:
+                               if (strpos($k, "@") === 0) {
 
-                               $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
-                               $orig_ts = strtotime(substr($k, 1));
-                               $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
+                                       $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
+                                       $orig_ts = strtotime(substr($k, 1));
+                                       $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
 
-                               //$k = date("Y-m-d", strtotime(substr($k, 1)));
+                                       //$k = date("Y-m-d", strtotime(substr($k, 1)));
 
-                               array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
-                       } else {
-                               array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
-                                               OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                                       array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
+                               } else {
+                                       array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
+                                                       OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
+                               }
                        }
                }
 
 
                        $view_query_part = "";
 
-                       if ($view_mode == "adaptive" || $view_query_part == "noscores") {
+                       if ($view_mode == "adaptive") {
                                if ($search) {
                                        $view_query_part = " ";
                                } else if ($feed != -1) {
                                $view_query_part = " marked = true AND ";
                        }
 
+                       if ($view_mode == "has_note") {
+                               $view_query_part = " (note IS NOT NULL AND note != '') AND ";
+                       }
+
                        if ($view_mode == "published") {
                                $view_query_part = " published = true AND ";
                        }
                                $view_query_part = " unread = true AND ";
                        }
 
-                       if ($view_mode == "updated") {
-                               $view_query_part = " (last_read is null and unread = false) AND ";
-                       }
-
                        if ($limit > 0) {
                                $limit_query_part = "LIMIT " . $limit;
                        }
                                $allow_archived = true;
 
                                if (!$override_order) {
-                                       if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
-                                               $override_order = "date_entered";
-                                       } else {
-                                               $override_order = "last_marked DESC, date_entered DESC";
-                                       }
+                                       $override_order = "last_marked DESC, date_entered DESC, updated DESC";
                                }
 
                        } else if ($feed == -2) { // published virtual feed OR labels category
                                        $allow_archived = true;
 
                                        if (!$override_order) {
-                                               if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
-                                                       $override_order = "date_entered";
-                                               } else {
-                                                       $override_order = "last_published DESC, date_entered DESC";
-                                               }
+                                               $override_order = "last_published DESC, date_entered DESC, updated DESC";
                                        }
 
                                } else {
                                $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
 
                                if (DB_TYPE == "pgsql") {
-                                       $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
+                                       $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
                                } else {
-                                       $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
+                                       $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
                                }
 
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
                        } else if ($feed == -4) { // all articles virtual feed
+                               $allow_archived = true;
                                $query_strategy_part = "true";
                                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
                        } else if ($feed <= LABEL_BASE_INDEX) { // labels
                                $query_strategy_part = "true";
                        }
 
-                       if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
-                               $date_sort_field = "updated";
-                       } else {
-                               $date_sort_field = "date_entered";
-                       }
-
-                       if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
-                               $order_by = "$date_sort_field";
-                       } else {
-                               $order_by = "$date_sort_field DESC";
-                       }
-
-                       if ($view_mode != "noscores") {
-                               $order_by = "score DESC, $order_by";
-                       }
+                       $order_by = "score DESC, date_entered DESC, updated DESC";
 
                        if ($view_mode == "unread_first") {
                                $order_by = "unread DESC, $order_by";
                                                hide_images,
                                                unread,feed_id,marked,published,link,last_read,orig_feed_id,
                                                last_marked, last_published,
-                                               ".SUBSTRING_FOR_DATE."(last_read,1,19) as last_read_noms,
                                                $vfeed_query_part
                                                $content_query_part
-                                               ".SUBSTRING_FOR_DATE."(updated,1,19) as updated_noms,
                                                author,score
                                        FROM
                                                $from_qpart
                                                                "last_read," .
                                                                "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
                                                                "last_marked, last_published, " .
-                                                               SUBSTRING_FOR_DATE . "(last_read,1,19) as last_read_noms," .
                                                                $since_id_part .
                                                                $vfeed_query_part .
                                                                $content_query_part .
-                                                               SUBSTRING_FOR_DATE . "(updated,1,19) as updated_noms," .
                                                                "score ";
 
                                $feed_kind = "Tags";
 
                                if ($entry->nodeName == 'img') {
                                        if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
-                                                       $force_remove_images) {
+                                                       $force_remove_images || $_SESSION["bw_limit"]) {
 
                                                $p = $doc->createElement('p');
 
 
                }
 
+               $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
+                       'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
+                       'caption', 'cite', 'center', 'code', 'col', 'colgroup',
+                       'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
+                       'dt', 'em', 'footer', 'figure', 'figcaption',
+                       'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
+                       'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
+                       'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'small',
+                       'source', 'span', 'strike', 'strong', 'sub', 'summary',
+                       'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
+                       'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
+
+               if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
+
+               $disallowed_attributes = array('id', 'style', 'class');
+
                global $pluginhost;
 
                if (isset($pluginhost)) {
                        foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE) as $plugin) {
-                               $doc = $plugin->hook_sanitize($doc, $site_url);
+                               $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
+                               if (is_array($retval)) {
+                                       $doc = $retval[0];
+                                       $allowed_elements = $retval[1];
+                                       $disallowed_attributes = $retval[2];
+                               } else {
+                                       $doc = $retval;
+                               }
                        }
                }
 
                $doc->removeChild($doc->firstChild); //remove doctype
-               $doc = strip_harmful_tags($doc);
+               $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
                $res = $doc->saveHTML();
                return $res;
        }
 
-       function strip_harmful_tags($doc) {
+       function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
                $entries = $doc->getElementsByTagName("*");
 
-               $allowed_elements = array('a', 'address', 'audio', 'article',
-                       'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
-                       'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
-                       'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
-                       'header', 'html', 'i', 'img', 'ins', 'kbd',
-                       'li', 'nav', 'ol', 'p', 'pre', 'q', 's','small',
-                       'source', 'span', 'strike', 'strong', 'sub', 'summary',
-                       'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
-                       'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
-
-               if ($_SESSION['hasSandbox']) array_push($allowed_elements, 'iframe');
-
-               $disallowed_attributes = array('id', 'style', 'class');
-
                foreach ($entries as $entry) {
                        if (!in_array($entry->nodeName, $allowed_elements)) {
                                $entry->parentNode->removeChild($entry);
        function format_warning($msg, $id = "") {
                global $link;
                return "<div class=\"warning\" id=\"$id\">
-                       <img src=\"images/sign_excl.svg\">$msg</div>";
+                       <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
        }
 
        function format_notice($msg, $id = "") {
                global $link;
                return "<div class=\"notice\" id=\"$id\">
-                       <img src=\"images/sign_info.svg\">$msg</div>";
+                       <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
        }
 
        function format_error($msg, $id = "") {
                global $link;
                return "<div class=\"error\" id=\"$id\">
-                       <img src=\"images/sign_excl.svg\">$msg</div>";
+                       <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
        }
 
        function print_notice($msg) {
                $cat_id = (int)getFeedCategory($link, $feed_id);
 
                $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
-                       owner_uid = $owner_uid AND enabled = true");
+                       owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
 
                $check_cats = join(",", array_merge(
                        getParentCategories($link, $cat_id, $owner_uid),
                        $parent_insert = "NULL";
                }
 
+               $feed_cat = mb_substr($feed_cat, 0, 250);
+
                $result = db_query($link,
                        "SELECT id FROM ttrss_feed_categories
                        WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
                                array_push($entries, $entry);
                        }
 
-                       if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES")) {
+                       if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
                                if ($always_display_enclosures ||
                                                        !preg_match("/<img/i", $article_content)) {
 
                                $rv .= "<hr clear='both'/>";
                        }
 
-                       $rv .= "<br/><div dojoType=\"dijit.form.DropDownButton\">".
-                               "<span>" . __('Attachments')."</span>";
-                       $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+                       $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
+                               "<option value=''>" . __('Attachments')."</option>";
 
-                       foreach ($entries_html as $entry) { $rv .= $entry; };
+                       foreach ($entries as $entry) {
+                               $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
 
-                       $rv .= "</div></div>";
+                       };
+
+                       $rv .= "</select>";
                }
 
                return $rv;
 
                        if (count($ids) > 0) {
                                $ids = join(",", $ids);
-                               print ".";
 
                                $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
                                $tags_deleted += db_affected_rows($link, $tmp_result);
                        $limit -= $limit_part;
                }
 
-               print "\n";
-
                return $tags_deleted;
        }