From 107d0cf39e3801547a9a86b32762b772b25f6953 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 11 Dec 2011 23:59:25 +0400 Subject: overall directory tree cleanup --- FeedTree.js | 434 --- PrefFeedTree.js | 79 - PrefFilterTree.js | 52 - PrefLabelTree.js | 43 - backend.php | 2 + db-prefs.php | 155 - db-updater.php | 4 +- db.php | 142 - deprecated.js | 29 - digest.js | 841 ----- digest.php | 6 +- errors.php | 2 + feedlist.js | 505 --- functions.js | 1657 --------- functions.php | 7648 ----------------------------------------- image.php | 2 + include/db-prefs.php | 155 + include/db.php | 142 + include/functions.php | 7647 ++++++++++++++++++++++++++++++++++++++++ include/localized_schema.php | 57 + include/login_form.php | 199 ++ include/sanity_check.php | 175 + include/sanity_config.php | 3 + include/sessions.php | 108 + include/version.php | 3 + index.php | 212 +- js/FeedTree.js | 434 +++ js/PrefFeedTree.js | 79 + js/PrefFilterTree.js | 52 + js/PrefLabelTree.js | 43 + js/deprecated.js | 29 + js/digest.js | 841 +++++ js/feedlist.js | 505 +++ js/functions.js | 1657 +++++++++ js/prefs.js | 1967 +++++++++++ js/tt-rss.js | 1164 +++++++ js/viewfeed.js | 2245 ++++++++++++ localized_js.php | 2 + localized_schema.php | 57 - login_form.php | 199 -- messages.pot | 2751 --------------- mobile/article.php | 8 +- mobile/cat.php | 8 +- mobile/classic/functions.php | 789 ----- mobile/classic/index.php | 114 - mobile/classic/login_form.php | 79 - mobile/classic/logout.php | 9 - mobile/classic/mobile.css | 216 -- mobile/classic/mobile.js | 94 - mobile/feed.php | 8 +- mobile/functions.php | 538 --- mobile/home.php | 10 +- mobile/index.php | 12 +- mobile/logout.php | 6 +- mobile/mobile-functions.php | 548 +++ mobile/prefs.php | 8 +- opml.php | 2 + prefs.js | 1967 ----------- prefs.php | 9 +- register.php | 18 +- sanity_check.php | 175 - sanity_config.php | 3 - sessions.php | 108 - tt-rss.js | 1164 ------- tt-rss.php | 209 -- twitter.php | 20 +- update.php | 4 +- update_daemon2.php | 2 + version.php | 3 - viewfeed.js | 2245 ------------ 70 files changed, 18323 insertions(+), 22380 deletions(-) delete mode 100644 FeedTree.js delete mode 100644 PrefFeedTree.js delete mode 100644 PrefFilterTree.js delete mode 100644 PrefLabelTree.js delete mode 100644 db-prefs.php delete mode 100644 db.php delete mode 100644 deprecated.js delete mode 100644 digest.js delete mode 100644 feedlist.js delete mode 100644 functions.js delete mode 100644 functions.php create mode 100644 include/db-prefs.php create mode 100644 include/db.php create mode 100644 include/functions.php create mode 100644 include/localized_schema.php create mode 100644 include/login_form.php create mode 100644 include/sanity_check.php create mode 100644 include/sanity_config.php create mode 100644 include/sessions.php create mode 100644 include/version.php create mode 100644 js/FeedTree.js create mode 100644 js/PrefFeedTree.js create mode 100644 js/PrefFilterTree.js create mode 100644 js/PrefLabelTree.js create mode 100644 js/deprecated.js create mode 100644 js/digest.js create mode 100644 js/feedlist.js create mode 100644 js/functions.js create mode 100644 js/prefs.js create mode 100644 js/tt-rss.js create mode 100644 js/viewfeed.js delete mode 100644 localized_schema.php delete mode 100644 login_form.php delete mode 100644 messages.pot delete mode 100644 mobile/classic/functions.php delete mode 100644 mobile/classic/index.php delete mode 100644 mobile/classic/login_form.php delete mode 100644 mobile/classic/logout.php delete mode 100644 mobile/classic/mobile.css delete mode 100644 mobile/classic/mobile.js delete mode 100644 mobile/functions.php create mode 100644 mobile/mobile-functions.php delete mode 100644 prefs.js delete mode 100644 sanity_check.php delete mode 100644 sanity_config.php delete mode 100644 sessions.php delete mode 100644 tt-rss.js delete mode 100644 tt-rss.php delete mode 100644 version.php delete mode 100644 viewfeed.js diff --git a/FeedTree.js b/FeedTree.js deleted file mode 100644 index b5b757164..000000000 --- a/FeedTree.js +++ /dev/null @@ -1,434 +0,0 @@ -dojo.provide("fox.FeedTree"); -dojo.provide("fox.FeedStoreModel"); - -dojo.require("dijit.Tree"); -dojo.require("dijit.Menu"); - -dojo.declare("fox.FeedStoreModel", dijit.tree.ForestStoreModel, { - getItemsInCategory: function (id) { - if (!this.store._itemsByIdentity) return undefined; - - cat = this.store._itemsByIdentity['CAT:' + id]; - - if (cat && cat.items) - return cat.items; - else - return undefined; - - }, - getItemById: function(id) { - return this.store._itemsByIdentity[id]; - }, - getFeedValue: function(feed, is_cat, key) { - if (!this.store._itemsByIdentity) return undefined; - - if (is_cat) - treeItem = this.store._itemsByIdentity['CAT:' + feed]; - else - treeItem = this.store._itemsByIdentity['FEED:' + feed]; - - if (treeItem) - return this.store.getValue(treeItem, key); - }, - getFeedName: function(feed, is_cat) { - return this.getFeedValue(feed, is_cat, 'name'); - }, - getFeedUnread: function(feed, is_cat) { - var unread = parseInt(this.getFeedValue(feed, is_cat, 'unread')); - return (isNaN(unread)) ? 0 : unread; - }, - setFeedUnread: function(feed, is_cat, unread) { - return this.setFeedValue(feed, is_cat, 'unread', parseInt(unread)); - }, - setFeedValue: function(feed, is_cat, key, value) { - if (!value) value = ''; - if (!this.store._itemsByIdentity) return undefined; - - if (is_cat) - treeItem = this.store._itemsByIdentity['CAT:' + feed]; - else - treeItem = this.store._itemsByIdentity['FEED:' + feed]; - - if (treeItem) - return this.store.setValue(treeItem, key, value); - }, - getNextUnreadFeed: function (feed, is_cat) { - if (!this.store._itemsByIdentity) - return null; - - if (is_cat) { - treeItem = this.store._itemsByIdentity['CAT:' + feed]; - items = this.store._arrayOfTopLevelItems; - } else { - treeItem = this.store._itemsByIdentity['FEED:' + feed]; - items = this.store._arrayOfAllItems; - } - - for (var i = 0; i < items.length; i++) { - if (items[i] == treeItem) { - - for (var j = i+1; j < items.length; j++) { - var unread = this.store.getValue(items[j], 'unread'); - var id = this.store.getValue(items[j], 'id'); - - if (unread > 0 && (is_cat || id.match("FEED:"))) return items[j]; - } - - for (var j = 0; j < i; j++) { - var unread = this.store.getValue(items[j], 'unread'); - var id = this.store.getValue(items[j], 'id'); - - if (unread > 0 && (is_cat || id.match("FEED:"))) return items[j]; - } - } - } - - return null; - }, - hasCats: function() { - if (this.store && this.store._itemsByIdentity) - return this.store._itemsByIdentity['CAT:-1'] != undefined; - else - return false; - }, -}); - -dojo.declare("fox.FeedTree", dijit.Tree, { - _createTreeNode: function(args) { - var tnode = new dijit._TreeNode(args); - - if (args.item.icon) - tnode.iconNode.src = args.item.icon[0]; - - var id = args.item.id[0]; - var bare_id = parseInt(id.substr(id.indexOf(':')+1)); - - if (bare_id < -10) { - var span = dojo.doc.createElement('span'); - var fg_color = args.item.fg_color[0]; - var bg_color = args.item.bg_color[0]; - - span.innerHTML = "α"; - span.className = 'labelColorIndicator'; - span.setStyle({ - color: fg_color, - backgroundColor: bg_color}); - - dojo.place(span, tnode.iconNode, 'replace'); - } - - if (id.match("FEED:") && bare_id > 0) { - var menu = new dijit.Menu(); - menu.row_id = bare_id; - - menu.addChild(new dijit.MenuItem({ - label: __("Mark as read"), - onClick: function() { - catchupFeed(this.getParent().row_id); - }})); - - menu.addChild(new dijit.MenuItem({ - label: __("Edit feed"), - onClick: function() { - editFeed(this.getParent().row_id, false); - }})); - - menu.addChild(new dijit.MenuItem({ - label: __("Update feed"), - onClick: function() { - scheduleFeedUpdate(this.getParent().row_id, false); - }})); - - menu.bindDomNode(tnode.domNode); - tnode._menu = menu; - } - - if (id.match("CAT:") && bare_id > 0) { - var menu = new dijit.Menu(); - menu.row_id = bare_id; - - menu.addChild(new dijit.MenuItem({ - label: __("Mark as read"), - onClick: function() { - catchupFeed(this.getParent().row_id, true); - }})); - - menu.bindDomNode(tnode.domNode); - tnode._menu = menu; - } - - //tnode.labelNode.innerHTML = args.label; - return tnode; - }, - getIconClass: function (item, opened) { - return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon"; - }, - getLabelClass: function (item, opened) { - return (item.unread == 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread"; - }, - getRowClass: function (item, opened) { - return (!item.error || item.error == '') ? "dijitTreeRow" : - "dijitTreeRow Error"; - }, - getLabel: function(item) { - var name = String(item.name); - - /* Horrible */ - name = name.replace(/"/g, "\""); - name = name.replace(/&/g, "&"); - name = name.replace(/—/g, "-"); - name = name.replace(/</g, "<"); - name = name.replace(/>/g, ">"); - - if (item.unread > 0) { - return name + " (" + item.unread + ")"; - } else { - return name; - } - }, - selectFeed: function(feed, is_cat) { - if (is_cat) - treeNode = this._itemNodesMap['CAT:' + feed]; - else - treeNode = this._itemNodesMap['FEED:' + feed]; - - if (treeNode) { - treeNode = treeNode[0]; - if (!is_cat) this._expandNode(treeNode); - this.set("selectedNodes", [treeNode]); - } - }, - setFeedIcon: function(feed, is_cat, src) { - if (is_cat) - treeNode = this._itemNodesMap['CAT:' + feed]; - else - treeNode = this._itemNodesMap['FEED:' + feed]; - - if (treeNode) { - treeNode = treeNode[0]; - treeNode.iconNode.src = src; - return true; - } - return false; - }, - setFeedExpandoIcon: function(feed, is_cat, src) { - if (is_cat) - treeNode = this._itemNodesMap['CAT:' + feed]; - else - treeNode = this._itemNodesMap['FEED:' + feed]; - - if (treeNode) { - treeNode = treeNode[0]; - treeNode.expandoNode.src = src; - return true; - } - - return false; - }, - hasCats: function() { - return this.model.hasCats(); - }, - hideRead: function (hide, show_special) { - if (this.hasCats()) { - - var tree = this; - var cats = this.model.store._arrayOfTopLevelItems; - - cats.each(function(cat) { - var cat_unread = tree.hideReadFeeds(cat.items, hide, show_special); - - var id = String(cat.id); - var node = tree._itemNodesMap[id]; - var bare_id = parseInt(id.substr(id.indexOf(":")+1)); - - if (node) { - var check_unread = tree.model.getFeedUnread(bare_id, true); - - if (hide && cat_unread == 0 && check_unread == 0) { - Effect.Fade(node[0].rowNode, {duration : 0.3, - queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }}); - } else { - Element.show(node[0].rowNode); - ++cat_unread; - } - } - }); - - } else { - this.hideReadFeeds(this.model.store._arrayOfTopLevelItems, hide, - show_special); - } - }, - hideReadFeeds: function (items, hide, show_special) { - var tree = this; - var cat_unread = 0; - - items.each(function(feed) { - var id = String(feed.id); - var bare_id = parseInt(feed.bare_id);; - - var unread = feed.unread[0]; - var node = tree._itemNodesMap[id]; - - if (node) { - if (hide && unread == 0 && (bare_id > 0 || !show_special)) { - Effect.Fade(node[0].rowNode, {duration : 0.3, - queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }}); - } else { - Element.show(node[0].rowNode); - ++cat_unread; - } - } - }); - - return cat_unread; - }, - collapseCat: function(id) { - if (!this.model.hasCats()) return; - - var tree = this; - - var node = tree._itemNodesMap['CAT:' + id][0]; - var item = tree.model.store._itemsByIdentity['CAT:' + id]; - - if (node && item) { - var hidden = tree.model.store.getValue(item, 'hidden'); - - if (hidden) - tree._expandNode(node); - else - tree._collapseNode(node); - - tree.model.store.setValue(item, 'hidden', !hidden); - } - }, - collapseHiddenCats: function() { - if (!this.model.hasCats()) return; - - var cats = this.model.store._arrayOfTopLevelItems; - var tree = this; - - dojo.forEach(cats, function(cat) { - var hidden = tree.model.store.getValue(cat, 'hidden'); - var id = tree.model.store.getValue(cat, 'id'); - var node = tree._itemNodesMap[id][0]; - - if (hidden) - tree._collapseNode(node); - else - tree._expandNode(node); - - }); - }, - getVisibleUnreadFeeds: function() { - var items = this.model.store._arrayOfAllItems; - var rv = []; - - for (var i = 0; i < items.length; i++) { - var id = String(items[i].id); - var box = this._itemNodesMap[id]; - - if (box) { - var row = box[0].rowNode; - var cat = false; - - try { - cat = box[0].rowNode.parentNode.parentNode; - } catch (e) { } - - if (row) { - if (Element.visible(row) && (!cat || Element.visible(cat))) { - var feed_id = String(items[i].bare_id); - var is_cat = !id.match('FEED:'); - var unread = this.model.getFeedUnread(feed_id, is_cat); - - if (unread > 0) - rv.push([feed_id, is_cat]); - - } - } - } - } - - return rv; - }, - getNextFeed: function (feed, is_cat) { - if (is_cat) { - treeItem = this.model.store._itemsByIdentity['CAT:' + feed]; - } else { - treeItem = this.model.store._itemsByIdentity['FEED:' + feed]; - } - - items = this.model.store._arrayOfAllItems; - var item = items[0]; - - for (var i = 0; i < items.length; i++) { - if (items[i] == treeItem) { - - for (var j = i+1; j < items.length; j++) { - var id = String(items[j].id); - var box = this._itemNodesMap[id]; - - if (box) { - var row = box[0].rowNode; - var cat = box[0].rowNode.parentNode.parentNode; - - if (Element.visible(cat) && Element.visible(row)) { - item = items[j]; - break; - } - } - } - break; - } - } - - if (item) { - return [this.model.store.getValue(item, 'bare_id'), - !this.model.store.getValue(item, 'id').match('FEED:')]; - } else { - return false; - } - }, - getPreviousFeed: function (feed, is_cat) { - if (is_cat) { - treeItem = this.model.store._itemsByIdentity['CAT:' + feed]; - } else { - treeItem = this.model.store._itemsByIdentity['FEED:' + feed]; - } - - items = this.model.store._arrayOfAllItems; - var item = items[0]; - - for (var i = 0; i < items.length; i++) { - if (items[i] == treeItem) { - - for (var j = i-1; j > 0; j--) { - var id = String(items[j].id); - var box = this._itemNodesMap[id]; - - if (box) { - var row = box[0].rowNode; - var cat = box[0].rowNode.parentNode.parentNode; - - if (Element.visible(cat) && Element.visible(row)) { - item = items[j]; - break; - } - } - - } - break; - } - } - - if (item) { - return [this.model.store.getValue(item, 'bare_id'), - !this.model.store.getValue(item, 'id').match('FEED:')]; - } else { - return false; - } - - }, - -}); diff --git a/PrefFeedTree.js b/PrefFeedTree.js deleted file mode 100644 index 4ea486609..000000000 --- a/PrefFeedTree.js +++ /dev/null @@ -1,79 +0,0 @@ -dojo.provide("fox.PrefFeedTree"); -dojo.provide("fox.PrefFeedStore"); - -dojo.require("lib.CheckBoxTree"); -dojo.require("dojo.data.ItemFileWriteStore"); - -dojo.declare("fox.PrefFeedStore", dojo.data.ItemFileWriteStore, { - - _saveEverything: function(saveCompleteCallback, saveFailedCallback, - newFileContentString) { - - dojo.xhrPost({ - url: "backend.php", - content: {op: "pref-feeds", subop: "savefeedorder", - payload: newFileContentString}, - error: saveFailedCallback, - load: saveCompleteCallback}); - }, - -}); - -dojo.declare("fox.PrefFeedTree", lib.CheckBoxTree, { - _createTreeNode: function(args) { - var tnode = this.inherited(arguments); - - if (args.item.icon) - tnode.iconNode.src = args.item.icon[0]; - - var param = this.model.store.getValue(args.item, 'param'); - - if (param) { - param = dojo.doc.createElement('span'); - param.className = 'feedParam'; - param.innerHTML = args.item.param[0]; - dojo.place(param, tnode.labelNode, 'after'); - } - - return tnode; - }, - onDndDrop: function() { - this.inherited(arguments); - this.tree.model.store.save(); - }, - getRowClass: function (item, opened) { - return (!item.error || item.error == '') ? "dijitTreeRow" : - "dijitTreeRow Error"; - }, - getIconClass: function (item, opened) { - return (!item || this.model.store.getValue(item, 'type') == 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon"; - }, - checkItemAcceptance: function(target, source, position) { - var item = dijit.getEnclosingWidget(target).item; - - // disable copying items - source.copyState = function() { return false; }; - - var source_item = false; - - source.forInSelectedItems(function(node) { - source_item = node.data.item; - }); - - if (!source_item || !item) return false; - - var id = this.tree.model.store.getValue(item, 'id'); - var source_id = source.tree.model.store.getValue(source_item, 'id'); - - //console.log(id + " " + position + " " + source_id); - - if (source_id.match("FEED:")) { - return ((id.match("CAT:") && position == "over") || - (id.match("FEED:") && position != "over")); - } else if (source_id.match("CAT:")) { - return ((id.match("CAT:") && position != "over") || - (id.match("root") && position == "over")); - } - }, -}); - diff --git a/PrefFilterTree.js b/PrefFilterTree.js deleted file mode 100644 index a4cf3dac8..000000000 --- a/PrefFilterTree.js +++ /dev/null @@ -1,52 +0,0 @@ -dojo.provide("fox.PrefFilterTree"); - -dojo.require("lib.CheckBoxTree"); - -dojo.declare("fox.PrefFilterTree", lib.CheckBoxTree, { - _createTreeNode: function(args) { - var tnode = this.inherited(arguments); - - var enabled = this.model.store.getValue(args.item, 'enabled'); - var param = this.model.store.getValue(args.item, 'param'); - - if (param) { - param = dojo.doc.createElement('span'); - param.className = (enabled != false) ? 'labelParam' : 'labelParam Disabled'; - param.innerHTML = args.item.param[0]; - dojo.place(param, tnode.labelNode, 'after'); - } - - return tnode; - }, - - getLabel: function(item) { - var label = item.name; - - var feed = this.model.store.getValue(item, 'feed'); - var inverse = this.model.store.getValue(item, 'inverse'); - - if (feed) - label += " (" + __("Feed:") + " " + feed + ")"; - - if (inverse) - label += " (" + __("Inverse") + ")"; - -/* if (item.param) - label = "" + label + - "" + item.param[0]; */ - - return label; - }, - getIconClass: function (item, opened) { - return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; - }, - getLabelClass: function (item, opened) { - var enabled = this.model.store.getValue(item, 'enabled'); - return (enabled != false) ? "dijitTreeLabel labelFixedLength" : "dijitTreeLabel labelFixedLength Disabled"; - }, - getRowClass: function (item, opened) { - return (!item.error || item.error == '') ? "dijitTreeRow" : - "dijitTreeRow Error"; - }, -}); - diff --git a/PrefLabelTree.js b/PrefLabelTree.js deleted file mode 100644 index 05a0c15b6..000000000 --- a/PrefLabelTree.js +++ /dev/null @@ -1,43 +0,0 @@ -dojo.provide("fox.PrefLabelTree"); - -dojo.require("lib.CheckBoxTree"); -dojo.require("dijit.form.DropDownButton"); - -dojo.declare("fox.PrefLabelTree", lib.CheckBoxTree, { - setNameById: function (id, name) { - var item = this.model.store._itemsByIdentity['LABEL:' + id]; - - if (item) - this.model.store.setValue(item, 'name', name); - - }, - _createTreeNode: function(args) { - var tnode = this.inherited(arguments); - - var fg_color = this.model.store.getValue(args.item, 'fg_color'); - var bg_color = this.model.store.getValue(args.item, 'bg_color'); - var type = this.model.store.getValue(args.item, 'type'); - var bare_id = this.model.store.getValue(args.item, 'bare_id'); - - if (type == 'label') { - var span = dojo.doc.createElement('span'); - span.innerHTML = 'α'; - span.className = 'labelColorIndicator2'; - span.id = 'LICID-' + bare_id; - - span.setStyle({ - color: fg_color, - backgroundColor: bg_color}); - - tnode._labelIconNode = span; - - dojo.place(tnode._labelIconNode, tnode.labelNode, 'before'); - } - - return tnode; - }, - getIconClass: function (item, opened) { - return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; - }, -}); - diff --git a/backend.php b/backend.php index 7bbec8c5a..247cab38b 100644 --- a/backend.php +++ b/backend.php @@ -1,4 +1,6 @@ 0) { - $value = db_fetch_result($result, 0, "value"); - $type_name = db_fetch_result($result, 0, "type_name"); - - if (!defined('DISABLE_SESSIONS')) { - if ($user_id == $_SESSION["uid"]) { - $_SESSION["prefs_cache"][$pref_name]["type"] = $type_name; - $_SESSION["prefs_cache"][$pref_name]["value"] = $value; - } - } - - return convert_pref_type($value, $type_name); - - } else { - if ($die_on_error) { - die("Fatal error, unknown preferences key: $pref_name"); - } else { - return null; - } - } - } - - function convert_pref_type($value, $type_name) { - if ($type_name == "bool") { - return $value == "true"; - } else if ($type_name == "integer") { - return sprintf("%d", $value); - } else { - return $value; - } - } - - function set_pref($link, $pref_name, $value, $user_id = false) { - $pref_name = db_escape_string($pref_name); - $value = db_escape_string($value); - - if (!$user_id) { - $user_id = $_SESSION["uid"]; - @$profile = $_SESSION["profile"]; - } else { - $user_id = sprintf("%d", $user_id); - $prefs_cache = false; - } - - if ($profile) { - $profile_qpart = "AND profile = '$profile'"; - } else { - $profile_qpart = "AND profile IS NULL"; - } - - if (get_schema_version($link) < 63) $profile_qpart = ""; - - $type_name = ""; - $current_value = ""; - - if (!defined('DISABLE_SESSIONS')) { - if ($_SESSION["prefs_cache"] && @$_SESSION["prefs_cache"][$pref_name]) { - $type_name = $_SESSION["prefs_cache"][$pref_name]["type"]; - $current_value = $_SESSION["prefs_cache"][$pref_name]["value"]; - } - } - - if (!$type_name) { - $result = db_query($link, "SELECT type_name - FROM ttrss_prefs,ttrss_prefs_types - WHERE pref_name = '$pref_name' AND type_id = ttrss_prefs_types.id"); - - if (db_num_rows($result) > 0) - $type_name = db_fetch_result($result, 0, "type_name"); - } else if ($current_value == $value) { - return; - } - - if ($type_name) { - if ($type_name == "bool") { - if ($value == "1" || $value == "true") { - $value = "true"; - } else { - $value = "false"; - } - } else if ($type_name == "integer") { - $value = sprintf("%d", $value); - } - - if ($pref_name == 'DEFAULT_ARTICLE_LIMIT' && $value == 0) { - $value = 30; - } - - if ($pref_name == 'USER_TIMEZONE' && $value == '') { - $value = 'UTC'; - } - - db_query($link, "UPDATE ttrss_user_prefs SET - value = '$value' WHERE pref_name = '$pref_name' - $profile_qpart - AND owner_uid = " . $_SESSION["uid"]); - - if (!defined('DISABLE_SESSIONS')) { - if ($user_id == $_SESSION["uid"]) { - $_SESSION["prefs_cache"][$pref_name]["type"] = $type_name; - $_SESSION["prefs_cache"][$pref_name]["value"] = $value; - } - } - } - } -?> diff --git a/db-updater.php b/db-updater.php index edee3bc83..9a4fdf604 100644 --- a/db-updater.php +++ b/db-updater.php @@ -1,4 +1,6 @@ + print "
"; } diff --git a/db.php b/db.php deleted file mode 100644 index 0682b58f8..000000000 --- a/db.php +++ /dev/null @@ -1,142 +0,0 @@ -$query failed [$result]: " . pg_last_error($link)); - } - } - return $result; - } else if (DB_TYPE == "mysql") { - $result = mysql_query($query, $link); - if (!$result) { - $query = htmlspecialchars($query); - if ($die_on_error) { - die("Query $query failed: " . mysql_error($link)); - } - } - return $result; - } -} - -function db_fetch_assoc($result) { - if (DB_TYPE == "pgsql") { - return pg_fetch_assoc($result); - } else if (DB_TYPE == "mysql") { - return mysql_fetch_assoc($result); - } -} - - -function db_num_rows($result) { - if (DB_TYPE == "pgsql") { - return pg_num_rows($result); - } else if (DB_TYPE == "mysql") { - return mysql_num_rows($result); - } -} - -function db_fetch_result($result, $row, $param) { - if (DB_TYPE == "pgsql") { - return pg_fetch_result($result, $row, $param); - } else if (DB_TYPE == "mysql") { - // I hate incoherent naming of PHP functions - return mysql_result($result, $row, $param); - } -} - -function db_unescape_string($str) { - $tmp = str_replace("\\\"", "\"", $str); - $tmp = str_replace("\\'", "'", $tmp); - return $tmp; -} - -function db_close($link) { - if (DB_TYPE == "pgsql") { - - return pg_close($link); - - } else if (DB_TYPE == "mysql") { - return mysql_close($link); - } -} - -function db_affected_rows($link, $result) { - if (DB_TYPE == "pgsql") { - return pg_affected_rows($result); - } else if (DB_TYPE == "mysql") { - return mysql_affected_rows($link); - } -} - -function db_last_error($link) { - if (DB_TYPE == "pgsql") { - return pg_last_error($link); - } else if (DB_TYPE == "mysql") { - return mysql_error($link); - } -} - -function db_quote($str){ - return("'$str'"); -} - -?> diff --git a/deprecated.js b/deprecated.js deleted file mode 100644 index 1d04a1adc..000000000 --- a/deprecated.js +++ /dev/null @@ -1,29 +0,0 @@ -function selectTableRow(r, do_select) { - - if (do_select) { - r.addClassName("Selected"); - } else { - r.removeClassName("Selected"); - } -} - -function selectTableRowById(elem_id, check_id, do_select) { - - try { - - var row = $(elem_id); - - if (row) { - selectTableRow(row, do_select); - } - - var check = $(check_id); - - if (check) { - check.checked = do_select; - } - } catch (e) { - exception_error("selectTableRowById", e); - } -} - diff --git a/digest.js b/digest.js deleted file mode 100644 index 7dba6d36e..000000000 --- a/digest.js +++ /dev/null @@ -1,841 +0,0 @@ -var last_feeds = []; -var init_params = {}; - -var _active_feed_id = false; -var _update_timeout = false; -var _view_update_timeout = false; -var _feedlist_expanded = false; -var _update_seq = 1; - -function article_appear(article_id) { - try { - new Effect.Appear('A-' + article_id); - } catch (e) { - exception_error("article_appear", e); - } -} - -function catchup_feed(feed_id, callback) { - try { - - var fn = find_feed(last_feeds, feed_id).title; - - if (confirm(__("Mark all articles in %s as read?").replace("%s", fn))) { - - var is_cat = ""; - - if (feed_id < 0) is_cat = "true"; // KLUDGE - - var query = "?op=rpc&subop=catchupFeed&feed_id=" + - feed_id + "&is_cat=" + is_cat; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - if (callback) callback(transport); - - update(); - } }); - } - - } catch (e) { - exception_error("catchup_article", e); - } -} - -function get_visible_article_ids() { - try { - var elems = $("headlines-content").getElementsByTagName("LI"); - var ids = []; - - for (var i = 0; i < elems.length; i++) { - if (elems[i].id && elems[i].id.match("A-")) { - ids.push(elems[i].id.replace("A-", "")); - } - } - - return ids; - - } catch (e) { - exception_error("get_visible_article_ids", e); - } -} - -function catchup_visible_articles(callback) { - try { - - var ids = get_visible_article_ids(); - - if (confirm(__("Mark %d displayed articles as read?").replace("%d", ids.length))) { - - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=0&ids=" + param_escape(ids); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - if (callback) callback(transport); - - viewfeed(_active_feed_id, 0); - } }); - - } - - } catch (e) { - exception_error("catchup_visible_articles", e); - } -} - -function catchup_article(article_id, callback) { - try { - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=0&ids=" + article_id; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - if (callback) callback(transport); - } }); - - } catch (e) { - exception_error("catchup_article", e); - } -} - -function set_selected_article(article_id) { - try { - $$("#headlines-content > li[id*=A-]").each(function(article) { - var id = article.id.replace("A-", ""); - - var cb = article.getElementsByTagName("INPUT")[0]; - - if (id == article_id) { - article.addClassName("selected"); - cb.checked = true; - } else { - article.removeClassName("selected"); - cb.checked = false; - } - - }); - - } catch (e) { - exception_error("mark_selected_feed", e); - } -} - - -function set_selected_feed(feed_id) { - try { - var feeds = $("feeds-content").getElementsByTagName("LI"); - - for (var i = 0; i < feeds.length; i++) { - if (feeds[i].id == "F-" + feed_id) - feeds[i].className = "selected"; - else - feeds[i].className = ""; - } - - _active_feed_id = feed_id; - - } catch (e) { - exception_error("mark_selected_feed", e); - } -} - -function load_more() { - try { - var pr = $("H-LOADING-IMG"); - - if (pr) Element.show(pr); - - var offset = $$("#headlines-content > li[id*=A-][class*=fresh],li[id*=A-][class*=unread]").length; - - viewfeed(false, offset, false, false, true, - function() { - var pr = $("H-LOADING-IMG"); - - if (pr) Element.hide(pr); - }); - } catch (e) { - exception_error("load_more", e); - } -} - -function update(callback) { - try { - console.log('updating feeds...'); - - window.clearTimeout(_update_timeout); - - new Ajax.Request("backend.php", { - parameters: "?op=rpc&subop=digest-init", - onComplete: function(transport) { - fatal_error_check(transport); - parse_feeds(transport); - set_selected_feed(_active_feed_id); - - if (callback) callback(transport); - } }); - - _update_timeout = window.setTimeout('update()', 5*1000); - } catch (e) { - exception_error("update", e); - } -} - -function remove_headline_entry(article_id) { - try { - var elem = $('A-' + article_id); - - if (elem) { - elem.parentNode.removeChild(elem); - } - - } catch (e) { - exception_error("remove_headline_entry", e); - } -} - -function view_update() { - try { - viewfeed(_active_feed_id, _active_feed_offset, false, true, true); - update(); - } catch (e) { - exception_error("view_update", e); - } -} - -function view(article_id) { - try { - $("content").addClassName("move"); - - var a = $("A-" + article_id); - var h = $("headlines"); - - setTimeout(function() { - // below or above viewport, reposition headline - if (a.offsetTop > h.scrollTop + h.offsetHeight || a.offsetTop+a.offsetHeight < h.scrollTop+a.offsetHeight) - h.scrollTop = a.offsetTop - (h.offsetHeight/2 - a.offsetHeight/2); - }, 500); - - new Ajax.Request("backend.php", { - parameters: "?op=rpc&subop=digest-get-contents&article_id=" + - article_id, - onComplete: function(transport) { - fatal_error_check(transport); - - var reply = JSON.parse(transport.responseText); - - if (reply) { - var article = reply['article']; - - var mark_part = ""; - var publ_part = ""; - - var tags_part = ""; - - if (article.tags.length > 0) { - tags_part = " " + __("in") + " "; - - for (var i = 0; i < Math.min(5, article.tags.length); i++) { - //tags_part += "" + - // article.tags[i] + ", "; - - tags_part += article.tags[i] + ", "; - } - - tags_part = tags_part.replace(/, $/, ""); - tags_part = "" + tags_part + ""; - - } - - if (article.marked) - mark_part = ""; - else - mark_part = ""; - - if (article.published) - publ_part = ""; - else - publ_part = ""; - - var tmp = "
" + - "" + __("Original article") + "" + - "
" + - __("Close this panel") + "
" + - "
" + - "
" + - mark_part + - publ_part + - "
" + - "

" + article.title + "

" + - "
" + - tags_part + - "
" + - article.content + "
"; - - $("article-content").innerHTML = tmp; - $("article").addClassName("visible"); - - set_selected_article(article.id); - - catchup_article(article_id, - function() { - $("A-" + article_id).addClassName("read"); - }); - - } else { - elem.innerHTML = __("Error: unable to load article."); - } - } - }); - - - return false; - } catch (e) { - exception_error("view", e); - } -} - -function close_article() { - $("content").removeClassName("move"); - $("article").removeClassName("visible"); -} - -function viewfeed(feed_id, offset, replace, no_effects, no_indicator, callback) { - try { - - if (!feed_id) feed_id = _active_feed_id; - if (offset == undefined) offset = 0; - if (replace == undefined) replace = (offset == 0); - - _update_seq = _update_seq + 1; - - if (!offset) $("headlines").scrollTop = 0; - - var query = "backend.php?op=rpc&subop=digest-update&feed_id=" + - param_escape(feed_id) + "&offset=" + offset + - "&seq=" + _update_seq; - - console.log(query); - - var img = false; - - if ($("F-" + feed_id)) { - img = $("F-" + feed_id).getElementsByTagName("IMG")[0]; - - if (img && !no_indicator) { - img.setAttribute("orig_src", img.src); - img.src = 'images/indicator_tiny.gif'; - } - } - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - Element.hide("overlay"); - - fatal_error_check(transport); - parse_headlines(transport, replace, no_effects); - set_selected_feed(feed_id); - _active_feed_offset = offset; - - if (img && !no_indicator) - img.src = img.getAttribute("orig_src"); - - if (callback) callback(transport); - - } }); - - } catch (e) { - exception_error("view", e); - } -} - -function find_article(articles, article_id) { - try { - for (var i = 0; i < articles.length; i++) { - if (articles[i].id == article_id) - return articles[i]; - } - - return false; - - } catch (e) { - exception_error("find_article", e); - } -} - -function find_feed(feeds, feed_id) { - try { - for (var i = 0; i < feeds.length; i++) { - if (feeds[i].id == feed_id) - return feeds[i]; - } - - return false; - - } catch (e) { - exception_error("find_feed", e); - } -} - -function get_feed_icon(feed) { - try { - if (feed.has_icon) - return getInitParam('icons_url') + "/" + feed.id + '.ico'; - - if (feed.id == -1) - return 'images/mark_set.png'; - - if (feed.id == -2) - return 'images/pub_set.png'; - - if (feed.id == -3) - return 'images/fresh.png'; - - if (feed.id == -4) - return 'images/tag.png'; - - if (feed.id < -10) - return 'images/label.png'; - - return 'images/blank_icon.gif'; - - } catch (e) { - exception_error("get_feed_icon", e); - } -} - -function add_feed_entry(feed) { - try { - var icon_part = ""; - - icon_part = ""; - - var tmp_html = "
  • " + - icon_part + feed.title + - "
    " + "" + feed.unread + "" + - "
    " + "
  • "; - - $("feeds-content").innerHTML += tmp_html; - - - } catch (e) { - exception_error("add_feed_entry", e); - } -} - -function add_headline_entry(article, feed, no_effects) { - try { - - var icon_part = ""; - - icon_part = ""; - - - var style = ""; - - //if (!no_effects) style = "style=\"display : none\""; - - if (article.excerpt.trim() == "") - article.excerpt = __("Click to expand article."); - - var li_class = "unread"; - - var fresh_max = getInitParam("fresh_article_max_age") * 60 * 60; - var d = new Date(); - - if (d.getTime() / 1000 - article.updated < fresh_max) - li_class = "fresh"; - - //"" + - - //"" + - - var checkbox_part = ""; - - var date = new Date(article.updated * 1000); - - var date_part = date.toString().substring(0,21); - - var tmp_html = "
  • " + - checkbox_part + - icon_part + - "" + - article.title + "" + - "
    " + - "
    " + - article.excerpt + "
    " + - "
    "; - -/* tmp_html += "" + - feed.title + " " + " @ "; */ - - tmp_html += date_part + "
    " + - "
  • "; - - $("headlines-content").innerHTML += tmp_html; - - if (!no_effects) - window.setTimeout('article_appear(' + article.id + ')', 100); - - } catch (e) { - exception_error("add_headline_entry", e); - } -} - -function expand_feeds() { - try { - _feedlist_expanded = true; - - redraw_feedlist(last_feeds); - - } catch (e) { - exception_error("expand_feeds", e); - } -} - -function redraw_feedlist(feeds) { - try { - - $('feeds-content').innerHTML = ""; - - var limit = 10; - - if (_feedlist_expanded) limit = feeds.length; - - for (var i = 0; i < Math.min(limit, feeds.length); i++) { - add_feed_entry(feeds[i]); - } - - if (feeds.length > limit) { - $('feeds-content').innerHTML += "
  • " + - "" + - "" + - __("%d more...").replace("%d", feeds.length-10) + - "" + "
  • "; - } - - if (feeds.length == 0) { - $('feeds-content').innerHTML = - "
    " + - __("No unread feeds.") + "
    "; - } - - if (_active_feed_id) - set_selected_feed(_active_feed_id); - - } catch (e) { - exception_error("redraw_feedlist", e); - } -} - -function parse_feeds(transport) { - try { - var reply = JSON.parse(transport.responseText); - - if (!reply) return; - - var feeds = reply['feeds']; - - if (feeds) { - - feeds.sort( function (a,b) - { - if (b.unread != a.unread) - return (b.unread - a.unread); - else - if (a.title > b.title) - return 1; - else if (a.title < b.title) - return -1; - else - return 0; - }); - - var all_articles = find_feed(feeds, -4); - - update_title(all_articles.unread); - - last_feeds = feeds; - - redraw_feedlist(feeds); - } - - } catch (e) { - exception_error("parse_feeds", e); - } -} - -function parse_headlines(transport, replace, no_effects) { - try { - var reply = JSON.parse(transport.responseText); - if (!reply) return; - - var seq = reply['seq']; - - if (seq) { - if (seq != _update_seq) { - console.log("parse_headlines: wrong sequence received."); - return; - } - } else { - return; - } - - var headlines = reply['headlines']['content']; - var headlines_title = reply['headlines']['title']; - - if (headlines && headlines_title) { - - if (replace) { - $('headlines-content').innerHTML = ''; - } - - var pr = $('H-MORE-PROMPT'); - - if (pr) pr.parentNode.removeChild(pr); - - var inserted = false; - - for (var i = 0; i < headlines.length; i++) { - - if (!$('A-' + headlines[i].id)) { - add_headline_entry(headlines[i], - find_feed(last_feeds, headlines[i].feed_id), !no_effects); - - } - } - - console.log(inserted.id); - - var ids = get_visible_article_ids(); - - if (ids.length > 0) { - if (pr) { - $('headlines-content').appendChild(pr); - - } else { - $('headlines-content').innerHTML += "
  • " + - "
  • "; - } - } else { - // FIXME : display some kind of "nothing to see here" prompt here - } - -// if (replace && !no_effects) -// new Effect.Appear('headlines-content', {duration : 0.3}); - - //new Effect.Appear('headlines-content'); - } - - } catch (e) { - exception_error("parse_headlines", e); - } -} - -function init_second_stage() { - try { - new Ajax.Request("backend.php", { - parameters: "backend.php?op=rpc&subop=digest-init", - onComplete: function(transport) { - parse_feeds(transport); - Element.hide("overlay"); - - window.setTimeout('viewfeed(-4)', 100); - _update_timeout = window.setTimeout('update()', 5*1000); - } }); - - } catch (e) { - exception_error("init_second_stage", e); - } -} - -function init() { - try { - dojo.require("dijit.Dialog"); - - new Ajax.Request("backend.php", { - parameters: "?op=rpc&subop=sanityCheck", - onComplete: function(transport) { - backend_sanity_check_callback(transport); - } }); - - } catch (e) { - exception_error("digest_init", e); - } -} - -function toggle_mark(img, id) { - - try { - - var query = "?op=rpc&id=" + id + "&subop=mark"; - - if (!img) return; - - if (img.src.match("mark_unset")) { - img.src = img.src.replace("mark_unset", "mark_set"); - img.alt = __("Unstar article"); - query = query + "&mark=1"; - } else { - img.src = img.src.replace("mark_set", "mark_unset"); - img.alt = __("Star article"); - query = query + "&mark=0"; - } - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - update(); - } }); - - } catch (e) { - exception_error("toggle_mark", e); - } -} - -function toggle_pub(img, id, note) { - - try { - - var query = "?op=rpc&id=" + id + "&subop=publ"; - - if (note != undefined) { - query = query + "¬e=" + param_escape(note); - } else { - query = query + "¬e=undefined"; - } - - if (!img) return; - - if (img.src.match("pub_unset") || note != undefined) { - img.src = img.src.replace("pub_unset", "pub_set"); - img.alt = __("Unpublish article"); - query = query + "&pub=1"; - - } else { - img.src = img.src.replace("pub_set", "pub_unset"); - img.alt = __("Publish article"); - query = query + "&pub=0"; - } - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - update(); - } }); - - } catch (e) { - exception_error("toggle_pub", e); - } -} - -function fatal_error(code, msg) { - try { - - if (code == 6) { - window.location.href = "digest.php"; - } else if (code == 5) { - window.location.href = "db-updater.php"; - } else { - - if (msg == "") msg = "Unknown error"; - - console.error("Fatal error: " + code + "\n" + - msg); - - } - - } catch (e) { - exception_error("fatalError", e); - } -} - -function fatal_error_check(transport) { - try { - if (transport.responseXML) { - var error = transport.responseXML.getElementsByTagName("error")[0]; - - if (error) { - var code = error.getAttribute("error-code"); - var msg = error.getAttribute("error-msg"); - if (code != 0) { - fatal_error(code, msg); - return false; - } - } - } - } catch (e) { - exception_error("fatal_error_check", e); - } - return true; -} - -function update_title(unread) { - try { - document.title = "Tiny Tiny RSS"; - - if (unread > 0) - document.title += " (" + unread + ")"; - - } catch (e) { - exception_error("update_title", e); - } -} - -function tweet_article(id) { - try { - - var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id); - - console.log(query); - - var d = new Date(); - var ts = d.getTime(); - - var w = window.open('backend.php?op=loading', 'ttrss_tweet', - "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0"); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - var ti = JSON.parse(transport.responseText); - - var share_url = "http://twitter.com/share?_=" + ts + - "&text=" + param_escape(ti.title) + - "&url=" + param_escape(ti.link); - - w.location.href = share_url; - - } }); - - } catch (e) { - exception_error("tweet_article", e); - } -} - -function toggle_select_article(elem) { - try { - var article = elem.parentNode; - - if (article.hasClassName("selected")) - article.removeClassName("selected"); - else - article.addClassName("selected"); - - } catch (e) { - exception_error("toggle_select_article", e); - } -} diff --git a/digest.php b/digest.php index f3879ace2..74a0c566d 100644 --- a/digest.php +++ b/digest.php @@ -1,4 +1,6 @@ - - + + "; - } - } - } - - function get_all_themes() { - $themes = glob("themes/*"); - - asort($themes); - - $rv = array(); - - foreach ($themes as $t) { - if (is_file("$t/theme.ini")) { - $ini = parse_ini_file("$t/theme.ini", true); - if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED && - !$ini['theme']['disabled']) { - $entry = array(); - $entry["path"] = $t; - $entry["base"] = basename($t); - $entry["name"] = $ini['theme']['name']; - $entry["version"] = $ini['theme']['version']; - $entry["author"] = $ini['theme']['author']; - $entry["options"] = $ini['theme']['options']; - array_push($rv, $entry); - } - } - } - - return $rv; - } - - function convert_timestamp($timestamp, $source_tz, $dest_tz) { - - try { - $source_tz = new DateTimeZone($source_tz); - } catch (Exception $e) { - $source_tz = new DateTimeZone('UTC'); - } - - try { - $dest_tz = new DateTimeZone($dest_tz); - } catch (Exception $e) { - $dest_tz = new DateTimeZone('UTC'); - } - - $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz); - return $dt->format('U') + $dest_tz->getOffset($dt); - } - - function make_local_datetime($link, $timestamp, $long, $owner_uid = false, - $no_smart_dt = false) { - - if (!$owner_uid) $owner_uid = $_SESSION['uid']; - if (!$timestamp) $timestamp = '1970-01-01 0:00'; - - global $utc_tz; - global $tz_offset; - - # We store date in UTC internally - $dt = new DateTime($timestamp, $utc_tz); - - if ($tz_offset == -1) { - - $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid); - - try { - $user_tz = new DateTimeZone($user_tz_string); - } catch (Exception $e) { - $user_tz = $utc_tz; - } - - $tz_offset = $user_tz->getOffset($dt); - } - - $user_timestamp = $dt->format('U') + $tz_offset; - - if (!$no_smart_dt) { - return smart_date_time($link, $user_timestamp, - $tz_offset, $owner_uid); - } else { - if ($long) - $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid); - else - $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid); - - return date($format, $user_timestamp); - } - } - - function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) { - if (!$owner_uid) $owner_uid = $_SESSION['uid']; - - if (date("Y.m.d", $timestamp) == date("Y.m.d", time() + $tz_offset)) { - return date("G:i", $timestamp); - } else if (date("Y", $timestamp) == date("Y", time() + $tz_offset)) { - $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid); - return date($format, $timestamp); - } else { - $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid); - return date($format, $timestamp); - } - } - - function sql_bool_to_bool($s) { - if ($s == "t" || $s == "1" || $s == "true") { - return true; - } else { - return false; - } - } - - function bool_to_sql_bool($s) { - if ($s) { - return "true"; - } else { - return "false"; - } - } - - // Session caching removed due to causing wrong redirects to upgrade - // script when get_schema_version() is called on an obsolete session - // created on a previous schema version. - function get_schema_version($link, $nocache = false) { - global $schema_version; - - if (!$schema_version) { - $result = db_query($link, "SELECT schema_version FROM ttrss_version"); - $version = db_fetch_result($result, 0, "schema_version"); - $schema_version = $version; - return $version; - } else { - return $schema_version; - } - } - - function sanity_check($link) { - require_once 'errors.php'; - - $error_code = 0; - $schema_version = get_schema_version($link, true); - - if ($schema_version != SCHEMA_VERSION) { - $error_code = 5; - } - - if (DB_TYPE == "mysql") { - $result = db_query($link, "SELECT true", false); - if (db_num_rows($result) != 1) { - $error_code = 10; - } - } - - if (db_escape_string("testTEST") != "testTEST") { - $error_code = 12; - } - - return array("code" => $error_code, "message" => $ERRORS[$error_code]); - } - - function file_is_locked($filename) { - if (function_exists('flock')) { - $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r"); - if ($fp) { - if (flock($fp, LOCK_EX | LOCK_NB)) { - flock($fp, LOCK_UN); - fclose($fp); - return false; - } - fclose($fp); - return true; - } else { - return false; - } - } - return true; // consider the file always locked and skip the test - } - - function make_lockfile($filename) { - $fp = fopen(LOCK_DIRECTORY . "/$filename", "w"); - - if (flock($fp, LOCK_EX | LOCK_NB)) { - if (function_exists('posix_getpid')) { - fwrite($fp, posix_getpid() . "\n"); - } - return $fp; - } else { - return false; - } - } - - function make_stampfile($filename) { - $fp = fopen(LOCK_DIRECTORY . "/$filename", "w"); - - if (flock($fp, LOCK_EX | LOCK_NB)) { - fwrite($fp, time() . "\n"); - flock($fp, LOCK_UN); - fclose($fp); - return true; - } else { - return false; - } - } - - function sql_random_function() { - if (DB_TYPE == "mysql") { - return "RAND()"; - } else { - return "RANDOM()"; - } - } - - function catchup_feed($link, $feed, $cat_view, $owner_uid = false) { - - if (!$owner_uid) $owner_uid = $_SESSION['uid']; - - //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { - - if (is_numeric($feed)) { - if ($cat_view) { - - if ($feed >= 0) { - - if ($feed > 0) { - $cat_qpart = "cat_id = '$feed'"; - } else { - $cat_qpart = "cat_id IS NULL"; - } - - $tmp_result = db_query($link, "SELECT id - FROM ttrss_feeds WHERE $cat_qpart AND owner_uid = $owner_uid"); - - while ($tmp_line = db_fetch_assoc($tmp_result)) { - - $tmp_feed = $tmp_line["id"]; - - db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE feed_id = '$tmp_feed' AND owner_uid = $owner_uid"); - } - } 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 unread = true 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 owner_uid = $owner_uid"); - - } else if ($feed < 0 && $feed > -10) { // special, like starred - - if ($feed == -1) { - db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE marked = true AND owner_uid = $owner_uid"); - } - - if ($feed == -2) { - db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE published = true AND owner_uid = $owner_uid"); - } - - if ($feed == -3) { - - $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE"); - - if (DB_TYPE == "pgsql") { - $match_part = "updated > NOW() - INTERVAL '$intl hour' "; - } else { - $match_part = "updated > 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); - } - - if ($feed == -4) { - db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE owner_uid = $owner_uid"); - } - - } else if ($feed < -10) { // label - - $label_id = -$feed - 11; - - 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 owner_uid = '$owner_uid' AND ref_id = article_id"); - - } - - ccache_update($link, $feed, $owner_uid, $cat_view); - - } else { // tag - db_query($link, "BEGIN"); - - $tag_name = db_escape_string($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 int_id = " . $line["post_int_id"]); - } - db_query($link, "COMMIT"); - } - } - - function getAllCounters($link, $omode = "flc", $active_feed = false) { - - if (!$omode) $omode = "flc"; - - $data = getGlobalCounters($link); - - $data = array_merge($data, getVirtCounters($link)); - - if (strchr($omode, "l")) $data = array_merge($data, getLabelCounters($link)); - if (strchr($omode, "f")) $data = array_merge($data, getFeedCounters($link, $active_feed)); - if (strchr($omode, "t")) $data = array_merge($data, getTagCounters($link)); - if (strchr($omode, "c")) $data = array_merge($data, getCategoryCounters($link)); - - return $data; - } - - function getCategoryCounters($link) { - $ret_arr = array(); - - /* Labels category */ - - $cv = array("id" => -2, "kind" => "cat", - "counter" => getCategoryUnread($link, -2)); - - array_push($ret_arr, $cv); - - $age_qpart = getMaxAgeSubquery(); - - $result = db_query($link, "SELECT id AS cat_id, value AS unread - FROM ttrss_feed_categories, ttrss_cat_counters_cache - WHERE ttrss_cat_counters_cache.feed_id = id AND - ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]); - - while ($line = db_fetch_assoc($result)) { - $line["cat_id"] = (int) $line["cat_id"]; - - $cv = array("id" => $line["cat_id"], "kind" => "cat", - "counter" => $line["unread"]); - - array_push($ret_arr, $cv); - } - - /* Special case: NULL category doesn't actually exist in the DB */ - - $cv = array("id" => 0, "kind" => "cat", - "counter" => ccache_find($link, 0, $_SESSION["uid"], true)); - - array_push($ret_arr, $cv); - - return $ret_arr; - } - - function getCategoryUnread($link, $cat, $owner_uid = false) { - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - if ($cat >= 0) { - - if ($cat != 0) { - $cat_query = "cat_id = '$cat'"; - } else { - $cat_query = "cat_id IS NULL"; - } - - $age_qpart = getMaxAgeSubquery(); - - $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query - AND owner_uid = " . $owner_uid); - - $cat_feeds = array(); - while ($line = db_fetch_assoc($result)) { - array_push($cat_feeds, "feed_id = " . $line["id"]); - } - - if (count($cat_feeds) == 0) return 0; - - $match_part = implode(" OR ", $cat_feeds); - - $result = db_query($link, "SELECT COUNT(int_id) AS unread - FROM ttrss_user_entries,ttrss_entries - WHERE unread = true AND ($match_part) AND id = ref_id - AND $age_qpart AND owner_uid = " . $owner_uid); - - $unread = 0; - - # this needs to be rewritten - while ($line = db_fetch_assoc($result)) { - $unread += $line["unread"]; - } - - return $unread; - } else if ($cat == -1) { - return getFeedUnread($link, -1) + getFeedUnread($link, -2) + getFeedUnread($link, -3) + getFeedUnread($link, 0); - } else if ($cat == -2) { - - $result = db_query($link, " - SELECT COUNT(unread) AS unread FROM - ttrss_user_entries, ttrss_labels2, ttrss_user_labels2, ttrss_feeds - WHERE label_id = ttrss_labels2.id AND article_id = ref_id AND - ttrss_labels2.owner_uid = '$owner_uid' - AND unread = true AND feed_id = ttrss_feeds.id - AND ttrss_user_entries.owner_uid = '$owner_uid'"); - - $unread = db_fetch_result($result, 0, "unread"); - - return $unread; - - } - } - - function getMaxAgeSubquery($days = COUNTERS_MAX_AGE) { - if (DB_TYPE == "pgsql") { - return "ttrss_entries.date_updated > - NOW() - INTERVAL '$days days'"; - } else { - return "ttrss_entries.date_updated > - DATE_SUB(NOW(), INTERVAL $days DAY)"; - } - } - - function getFeedUnread($link, $feed, $is_cat = false) { - return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]); - } - - function getLabelUnread($link, $label_id, $owner_uid = false) { - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $result = db_query($link, " - SELECT COUNT(unread) AS unread FROM - ttrss_user_entries, ttrss_labels2, ttrss_user_labels2, ttrss_feeds - WHERE label_id = ttrss_labels2.id AND article_id = ref_id AND - ttrss_labels2.owner_uid = '$owner_uid' AND ttrss_labels2.id = '$label_id' - AND unread = true AND feed_id = ttrss_feeds.id - AND ttrss_user_entries.owner_uid = '$owner_uid'"); - - if (db_num_rows($result) != 0) { - return db_fetch_result($result, 0, "unread"); - } else { - return 0; - } - } - - function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false, - $owner_uid = false) { - - $n_feed = (int) $feed; - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - if ($unread_only) { - $unread_qpart = "unread = true"; - } else { - $unread_qpart = "true"; - } - - $age_qpart = getMaxAgeSubquery(); - - if ($is_cat) { - return getCategoryUnread($link, $n_feed, $owner_uid); - } if ($feed != "0" && $n_feed == 0) { - - $feed = db_escape_string($feed); - - $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id) - FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id - AND ref_id = id AND $age_qpart - AND $unread_qpart)) AS count FROM ttrss_tags - WHERE owner_uid = $owner_uid AND tag_name = '$feed'"); - return db_fetch_result($result, 0, "count"); - - } else if ($n_feed == -1) { - $match_part = "marked = true"; - } else if ($n_feed == -2) { - $match_part = "published = true"; - } else if ($n_feed == -3) { - $match_part = "unread = true AND score >= 0"; - - $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid); - - if (DB_TYPE == "pgsql") { - $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' "; - } else { - $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) "; - } - } else if ($n_feed == -4) { - $match_part = "true"; - } else if ($n_feed >= 0) { - - if ($n_feed != 0) { - $match_part = "feed_id = '$n_feed'"; - } else { - $match_part = "feed_id IS NULL"; - } - - } else if ($feed < -10) { - - $label_id = -$feed - 11; - - return getLabelUnread($link, $label_id, $owner_uid); - - } - - if ($match_part) { - - if ($n_feed != 0) { - $from_qpart = "ttrss_user_entries,ttrss_feeds,ttrss_entries"; - $feeds_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND"; - } else { - $from_qpart = "ttrss_user_entries,ttrss_entries"; - $feeds_qpart = ''; - } - - $query = "SELECT count(int_id) AS unread - FROM $from_qpart WHERE - ttrss_user_entries.ref_id = ttrss_entries.id AND - $age_qpart AND - $feeds_qpart - $unread_qpart AND ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid"; - - $result = db_query($link, $query); - - } else { - - $result = db_query($link, "SELECT COUNT(post_int_id) AS unread - FROM ttrss_tags,ttrss_user_entries,ttrss_entries - WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id - AND $unread_qpart AND $age_qpart AND - ttrss_tags.owner_uid = " . $owner_uid); - } - - $unread = db_fetch_result($result, 0, "unread"); - - return $unread; - } - - function getGlobalUnread($link, $user_id = false) { - - if (!$user_id) { - $user_id = $_SESSION["uid"]; - } - - $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache - WHERE owner_uid = '$user_id' AND feed_id > 0"); - - $c_id = db_fetch_result($result, 0, "c_id"); - - return $c_id; - } - - function getGlobalCounters($link, $global_unread = -1) { - $ret_arr = array(); - - if ($global_unread == -1) { - $global_unread = getGlobalUnread($link); - } - - $cv = array("id" => "global-unread", - "counter" => $global_unread); - - array_push($ret_arr, $cv); - - $result = db_query($link, "SELECT COUNT(id) AS fn FROM - ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); - - $subscribed_feeds = db_fetch_result($result, 0, "fn"); - - $cv = array("id" => "subscribed-feeds", - "counter" => $subscribed_feeds); - - array_push($ret_arr, $cv); - - return $ret_arr; - } - - function getTagCounters($link) { - - $ret_arr = array(); - - $age_qpart = getMaxAgeSubquery(); - - $result = db_query($link, "SELECT tag_name,SUM((SELECT COUNT(int_id) - FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id - AND ref_id = id AND $age_qpart - AND unread = true)) AS count FROM ttrss_tags - WHERE owner_uid = ".$_SESSION['uid']." GROUP BY tag_name - ORDER BY count DESC LIMIT 55"); - - $tags = array(); - - while ($line = db_fetch_assoc($result)) { - $tags[$line["tag_name"]] += $line["count"]; - } - - foreach (array_keys($tags) as $tag) { - $unread = $tags[$tag]; - $tag = htmlspecialchars($tag); - - $cv = array("id" => $tag, - "kind" => "tag", - "counter" => $unread); - - array_push($ret_arr, $cv); - } - - return $ret_arr; - } - - function getVirtCounters($link) { - - $ret_arr = array(); - - for ($i = 0; $i >= -4; $i--) { - - $count = getFeedUnread($link, $i); - - $cv = array("id" => $i, - "counter" => $count); - -// if (get_pref($link, 'EXTENDED_FEEDLIST')) -// $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total"); - - array_push($ret_arr, $cv); - } - - return $ret_arr; - } - - function getLabelCounters($link, $descriptions = false) { - - $ret_arr = array(); - - $age_qpart = getMaxAgeSubquery(); - - $owner_uid = $_SESSION["uid"]; - - $result = db_query($link, "SELECT id, caption FROM ttrss_labels2 - WHERE owner_uid = '$owner_uid'"); - - while ($line = db_fetch_assoc($result)) { - - $id = -$line["id"] - 11; - - $label_name = $line["caption"]; - $count = getFeedUnread($link, $id); - - $cv = array("id" => $id, - "counter" => $count); - - if ($descriptions) - $cv["description"] = $label_name; - -// if (get_pref($link, 'EXTENDED_FEEDLIST')) -// $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total"); - - array_push($ret_arr, $cv); - } - - return $ret_arr; - } - - function getFeedCounters($link, $active_feed = false) { - - $ret_arr = array(); - - $age_qpart = getMaxAgeSubquery(); - - $query = "SELECT ttrss_feeds.id, - ttrss_feeds.title, - ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated, - last_error, value AS count - FROM ttrss_feeds, ttrss_counters_cache - WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]." - AND ttrss_counters_cache.feed_id = id"; - - $result = db_query($link, $query); - $fctrs_modified = false; - - while ($line = db_fetch_assoc($result)) { - - $id = $line["id"]; - $count = $line["count"]; - $last_error = htmlspecialchars($line["last_error"]); - - $last_updated = make_local_datetime($link, $line['last_updated'], false); - - $has_img = feed_has_icon($id); - - if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2) - $last_updated = ''; - - $cv = array("id" => $id, - "updated" => $last_updated, - "counter" => $count, - "has_img" => (int) $has_img); - - if ($last_error) - $cv["error"] = $last_error; - -// if (get_pref($link, 'EXTENDED_FEEDLIST')) -// $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total"); - - if ($active_feed && $id == $active_feed) - $cv["title"] = truncate_string($line["title"], 30); - - array_push($ret_arr, $cv); - - } - - return $ret_arr; - } - - function get_pgsql_version($link) { - $result = db_query($link, "SELECT version() AS version"); - $version = explode(" ", db_fetch_result($result, 0, "version")); - return $version[1]; - } - - /** - * Subscribes the user to the given feed - * - * @param resource $link Database connection - * @param string $url Feed URL to subscribe to - * @param integer $cat_id Category ID the feed shall be added to - * @param string $auth_login (optional) Feed username - * @param string $auth_pass (optional) Feed password - * - * @return integer Status code: - * 0 - OK, Feed already exists - * 1 - OK, Feed added - * 2 - Invalid URL - * 3 - URL content is HTML, no feeds available - * 4 - URL content is HTML which contains multiple feeds. - * Here you should call extractfeedurls in rpc-backend - * to get all possible feeds. - * 5 - Couldn't download the URL content. - */ - function subscribe_to_feed($link, $url, $cat_id = 0, - $auth_login = '', $auth_pass = '') { - - $url = fix_url($url); - - if (!$url || !validate_feed_url($url)) return 2; - - $update_method = 0; - - $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users - WHERE id = ".$_SESSION['uid']); - - $has_oauth = db_fetch_result($result, 0, 'twitter_oauth'); - - if (!$has_oauth || strpos($url, '://api.twitter.com') === false) { - if (!fetch_file_contents($url, false, $auth_login, $auth_pass)) return 5; - - if (url_is_html($url, $auth_login, $auth_pass)) { - $feedUrls = get_feeds_from_html($url, $auth_login, $auth_pass); - if (count($feedUrls) == 0) { - return 3; - } else if (count($feedUrls) > 1) { - return 4; - } - //use feed url as new URL - $url = key($feedUrls); - } - - } else { - if (!fetch_twitter_rss($link, $url, $_SESSION['uid'])) - return 5; - - $update_method = 3; - } - if ($cat_id == "0" || !$cat_id) { - $cat_qpart = "NULL"; - } else { - $cat_qpart = "'$cat_id'"; - } - - $result = db_query($link, - "SELECT id FROM ttrss_feeds - WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]); - - if (db_num_rows($result) == 0) { - $result = db_query($link, - "INSERT INTO ttrss_feeds - (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method) - VALUES ('".$_SESSION["uid"]."', '$url', - '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', '$update_method')"); - - $result = db_query($link, - "SELECT id FROM ttrss_feeds WHERE feed_url = '$url' - AND owner_uid = " . $_SESSION["uid"]); - - $feed_id = db_fetch_result($result, 0, "id"); - - if ($feed_id) { - update_rss_feed($link, $feed_id, true); - } - - return 1; - } else { - return 0; - } - } - - function print_feed_select($link, $id, $default_id = "", - $attributes = "", $include_all_feeds = true) { - - print ""; - } - - function print_feed_cat_select($link, $id, $default_id = "", - $attributes = "", $include_all_cats = true) { - - print ""; - } - - function checkbox_to_sql_bool($val) { - return ($val == "on") ? "true" : "false"; - } - - function getFeedCatTitle($link, $id) { - if ($id == -1) { - return __("Special"); - } else if ($id < -10) { - return __("Labels"); - } else if ($id > 0) { - $result = db_query($link, "SELECT ttrss_feed_categories.title - FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND - cat_id = ttrss_feed_categories.id"); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "title"); - } else { - return __("Uncategorized"); - } - } else { - return "getFeedCatTitle($id) failed"; - } - - } - - function getFeedIcon($id) { - switch ($id) { - case 0: - return "images/archive.png"; - break; - case -1: - return "images/mark_set.png"; - break; - case -2: - return "images/pub_set.png"; - break; - case -3: - return "images/fresh.png"; - break; - case -4: - return "images/tag.png"; - break; - default: - if ($id < -10) { - return "images/label.png"; - } else { - if (file_exists(ICONS_DIR . "/$id.ico")) - return ICONS_URL . "/$id.ico"; - } - break; - } - } - - function getFeedTitle($link, $id) { - if ($id == -1) { - return __("Starred articles"); - } else if ($id == -2) { - return __("Published articles"); - } else if ($id == -3) { - return __("Fresh articles"); - } else if ($id == -4) { - return __("All articles"); - } else if ($id === 0 || $id === "0") { - return __("Archived articles"); - } else if ($id < -10) { - $label_id = -$id - 11; - $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'"); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "caption"); - } else { - return "Unknown label ($label_id)"; - } - - } else if (is_numeric($id) && $id > 0) { - $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'"); - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "title"); - } else { - return "Unknown feed ($id)"; - } - } else { - return $id; - } - } - - function make_init_params($link) { - $params = array(); - - $params["theme"] = get_user_theme($link); - $params["theme_options"] = get_user_theme_options($link); - - $params["sign_progress"] = theme_image($link, "images/indicator_white.gif"); - $params["sign_progress_tiny"] = theme_image($link, "images/indicator_tiny.gif"); - $params["sign_excl"] = theme_image($link, "images/sign_excl.png"); - $params["sign_info"] = theme_image($link, "images/sign_info.png"); - - 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", - "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) { - - $params[strtolower($param)] = (int) get_pref($link, $param); - } - - $params["icons_url"] = ICONS_URL; - $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME; - $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE"); - $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT"); - $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY"); - $params["bw_limit"] = (int) $_SESSION["bw_limit"]; - - $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM - ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); - - $max_feed_id = db_fetch_result($result, 0, "mid"); - $num_feeds = db_fetch_result($result, 0, "nf"); - - $params["max_feed_id"] = (int) $max_feed_id; - $params["num_feeds"] = (int) $num_feeds; - - $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST"); - - return $params; - } - - function make_runtime_info($link) { - $data = array(); - - $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM - ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); - - $max_feed_id = db_fetch_result($result, 0, "mid"); - $num_feeds = db_fetch_result($result, 0, "nf"); - - $data["max_feed_id"] = (int) $max_feed_id; - $data["num_feeds"] = (int) $num_feeds; - - $data['last_article_id'] = getLastArticleId($link); - $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED'); - - if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) { - - $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock"); - - if (time() - $_SESSION["daemon_stamp_check"] > 30) { - - $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp"); - - if ($stamp) { - $stamp_delta = time() - $stamp; - - if ($stamp_delta > 1800) { - $stamp_check = 0; - } else { - $stamp_check = 1; - $_SESSION["daemon_stamp_check"] = time(); - } - - $data['daemon_stamp_ok'] = $stamp_check; - - $stamp_fmt = date("Y.m.d, G:i", $stamp); - - $data['daemon_stamp'] = $stamp_fmt; - } - } - } - - if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) { - $new_version_details = @check_for_update($link); - - $data['new_version_available'] = (int) ($new_version_details != false); - - $_SESSION["last_version_check"] = time(); - } - - return $data; - } - - function search_to_sql($link, $search, $match_on) { - - $search_query_part = ""; - - $keywords = explode(" ", $search); - $query_keywords = array(); - - foreach ($keywords as $k) { - if (strpos($k, "-") === 0) { - $k = substr($k, 1); - $not = "NOT"; - } else { - $not = ""; - } - - $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]) { - - if ($commandpair[1] == "true") - array_push($query_keywords, "($not (published = true))"); - else - array_push($query_keywords, "($not (published = false))"); - - } else 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')); - - //$k = date("Y-m-d", strtotime(substr($k, 1))); - - array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')"); - } else if ($match_on == "both") { - array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%') - OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))"); - } else if ($match_on == "title") { - array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%'))"); - } else if ($match_on == "content") { - array_push($query_keywords, "(UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))"); - } - } - - $search_query_part = implode("AND", $query_keywords); - - return $search_query_part; - } - - - function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $match_on, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0) { - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $ext_tables_part = ""; - - if ($search) { - - if (SPHINX_ENABLED) { - $ids = join(",", @sphinx_search($search, 0, 500)); - - if ($ids) - $search_query_part = "ref_id IN ($ids) AND "; - else - $search_query_part = "ref_id = -1 AND "; - - } else { - $search_query_part = search_to_sql($link, $search, $match_on); - $search_query_part .= " AND "; - } - - } else { - $search_query_part = ""; - } - - if ($filter) { - $filter_query_part = filter_to_sql($filter); - } else { - $filter_query_part = ""; - } - - if ($since_id) { - $since_id_part = "ttrss_entries.id > $since_id AND "; - } else { - $since_id_part = ""; - } - - $view_query_part = ""; - - if ($view_mode == "adaptive" || $view_query_part == "noscores") { - if ($search) { - $view_query_part = " "; - } else if ($feed != -1) { - $unread = getFeedUnread($link, $feed, $cat_view); - if ($unread > 0) { - $view_query_part = " unread = true AND "; - } - } - } - - if ($view_mode == "marked") { - $view_query_part = " marked = true AND "; - } - - if ($view_mode == "published") { - $view_query_part = " published = true AND "; - } - - if ($view_mode == "unread") { - $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; - } - - $vfeed_query_part = ""; - - // override query strategy and enable feed display when searching globally - if ($search && $search_mode == "all_feeds") { - $query_strategy_part = "ttrss_entries.id > 0"; - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - /* tags */ - } else if (preg_match("/^-?[0-9][0-9]*$/", $feed) == false) { - $query_strategy_part = "ttrss_entries.id > 0"; - $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE - id = feed_id) as feed_title,"; - } else if ($feed > 0 && $search && $search_mode == "this_cat") { - - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - - $tmp_result = false; - - if ($cat_view) { - $tmp_result = db_query($link, "SELECT id - FROM ttrss_feeds WHERE cat_id = '$feed'"); - } else { - $tmp_result = db_query($link, "SELECT id - FROM ttrss_feeds WHERE cat_id = (SELECT cat_id FROM ttrss_feeds - WHERE id = '$feed') AND id != '$feed'"); - } - - $cat_siblings = array(); - - if (db_num_rows($tmp_result) > 0) { - while ($p = db_fetch_assoc($tmp_result)) { - array_push($cat_siblings, "feed_id = " . $p["id"]); - } - - $query_strategy_part = sprintf("(feed_id = %d OR %s)", - $feed, implode(" OR ", $cat_siblings)); - - } else { - $query_strategy_part = "ttrss_entries.id > 0"; - } - - } else if ($feed > 0) { - - if ($cat_view) { - - if ($feed > 0) { - $query_strategy_part = "cat_id = '$feed'"; - } else { - $query_strategy_part = "cat_id IS NULL"; - } - - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - - } else { - $query_strategy_part = "feed_id = '$feed'"; - } - } else if ($feed == 0 && !$cat_view) { // archive virtual feed - $query_strategy_part = "feed_id IS NULL"; - } else if ($feed == 0 && $cat_view) { // uncategorized - $query_strategy_part = "cat_id IS NULL"; - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - } else if ($feed == -1) { // starred virtual feed - $query_strategy_part = "marked = true"; - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - } else if ($feed == -2) { // published virtual feed OR labels category - - if (!$cat_view) { - $query_strategy_part = "published = true"; - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - } else { - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - - $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2"; - - $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND - ttrss_user_labels2.article_id = ref_id"; - - } - - } else if ($feed == -3) { // fresh virtual feed - $query_strategy_part = "unread = true AND score >= 0"; - - $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid); - - if (DB_TYPE == "pgsql") { - $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' "; - } else { - $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) "; - } - - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - } else if ($feed == -4) { // all articles virtual feed - $query_strategy_part = "true"; - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - } else if ($feed <= -10) { // labels - $label_id = -$feed - 11; - - $query_strategy_part = "label_id = '$label_id' AND - ttrss_labels2.id = ttrss_user_labels2.label_id AND - ttrss_user_labels2.article_id = ref_id"; - - $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; - $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2"; - - } else { - $query_strategy_part = "id > 0"; // dumb - } - - 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"; - } - - if ($override_order) { - $order_by = $override_order; - } - - $feed_title = ""; - - if ($search) { - $feed_title = "Search results"; - } else { - if ($cat_view) { - $feed_title = getCategoryTitle($link, $feed); - } else { - if (is_numeric($feed) && $feed > 0) { - $result = db_query($link, "SELECT title,site_url,last_error - FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid"); - - $feed_title = db_fetch_result($result, 0, "title"); - $feed_site_url = db_fetch_result($result, 0, "site_url"); - $last_error = db_fetch_result($result, 0, "last_error"); - } else { - $feed_title = getFeedTitle($link, $feed); - } - } - } - - $content_query_part = "content as content_preview,"; - - if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { - - if ($feed >= 0) { - $feed_kind = "Feeds"; - } else { - $feed_kind = "Labels"; - } - - if ($limit_query_part) { - $offset_query_part = "OFFSET $offset"; - } - - if ($vfeed_query_part && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) { - if (!$override_order) { - $order_by = "ttrss_feeds.title, $order_by"; - } - } - - if ($feed != "0") { - $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part"; - $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND"; - - } else { - $from_qpart = "ttrss_entries,ttrss_user_entries$ext_tables_part - LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)"; - } - - $query = "SELECT DISTINCT - date_entered, - guid, - ttrss_entries.id,ttrss_entries.title, - updated, - label_cache, - tag_cache, - always_display_enclosures, - site_url, - note, - num_comments, - comments, - int_id, - unread,feed_id,marked,published,link,last_read,orig_feed_id, - ".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 - WHERE - $feed_check_qpart - ttrss_user_entries.ref_id = ttrss_entries.id AND - ttrss_user_entries.owner_uid = '$owner_uid' AND - $search_query_part - $filter_query_part - $view_query_part - $since_id_part - $query_strategy_part ORDER BY $order_by - $limit_query_part $offset_query_part"; - - if ($_REQUEST["debug"]) print $query; - - $result = db_query($link, $query); - - } else { - // browsing by tag - - $select_qpart = "SELECT DISTINCT " . - "date_entered," . - "guid," . - "note," . - "ttrss_entries.id as id," . - "title," . - "updated," . - "unread," . - "feed_id," . - "orig_feed_id," . - "site_url," . - "always_display_enclosures, ". - "marked," . - "num_comments, " . - "comments, " . - "tag_cache," . - "label_cache," . - "link," . - "last_read," . - 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"; - $all_tags = explode(",", $feed); - if ($search_mode == 'any') { - $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")"; - $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags "; - $where_qpart = " WHERE " . - "ref_id = ttrss_entries.id AND " . - "ttrss_user_entries.owner_uid = $owner_uid AND " . - "post_int_id = int_id AND $tag_sql AND " . - $view_query_part . - $search_query_part . - $query_strategy_part . " ORDER BY $order_by " . - $limit_query_part; - - } else { - $i = 1; - $sub_selects = array(); - $sub_ands = array(); - foreach ($all_tags as $term) { - array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i"); - $i++; - } - if ($i > 2) { - $x = 1; - $y = 2; - do { - array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id"); - $x++; - $y++; - } while ($y < $i); - } - array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid"); - array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id"); - $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries"; - $where_qpart = " WHERE " . implode(" AND ", $sub_ands); - } - // error_log("TAG SQL: " . $tag_sql); - // $tag_sql = "tag_name = '$feed'"; DEFAULT way - - // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]"); - $result = db_query($link, $select_qpart . $from_qpart . $where_qpart); - } - - return array($result, $feed_title, $feed_site_url, $last_error); - - } - - function generate_syndicated_feed($link, $owner_uid, $feed, $is_cat, - $limit, $search, $search_mode, $match_on, $view_mode = false) { - - require_once "lib/MiniTemplator.class.php"; - - $note_style = "background-color : #fff7d5; - border-width : 1px; ". - "padding : 5px; border-style : dashed; border-color : #e7d796;". - "margin-bottom : 1em; color : #9a8c59;"; - - if (!$limit) $limit = 30; - - if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) { - $date_sort_field = "updated"; - } else { - $date_sort_field = "date_entered"; - } - - $qfh_ret = queryFeedHeadlines($link, $feed, - $limit, $view_mode, $is_cat, $search, $search_mode, - $match_on, "$date_sort_field DESC", 0, $owner_uid); - - $result = $qfh_ret[0]; - $feed_title = htmlspecialchars($qfh_ret[1]); - $feed_site_url = $qfh_ret[2]; - $last_error = $qfh_ret[3]; - - $feed_self_url = get_self_url_prefix() . - "/public.php?op=rss&id=-2&key=" . - get_feed_access_key($link, -2, false); - - if (!$feed_site_url) $feed_site_url = get_self_url_prefix(); - - $tpl = new MiniTemplator; - - $tpl->readTemplateFromFile("templates/generated_feed.txt"); - - $tpl->setVariable('FEED_TITLE', $feed_title); - $tpl->setVariable('VERSION', VERSION); - $tpl->setVariable('FEED_URL', htmlspecialchars($feed_self_url)); - - if (PUBSUBHUBBUB_HUB && $feed == -2) { - $tpl->setVariable('HUB_URL', htmlspecialchars(PUBSUBHUBBUB_HUB)); - $tpl->addBlock('feed_hub'); - } - - $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix())); - - while ($line = db_fetch_assoc($result)) { - $tpl->setVariable('ARTICLE_ID', htmlspecialchars($line['link'])); - $tpl->setVariable('ARTICLE_LINK', htmlspecialchars($line['link'])); - $tpl->setVariable('ARTICLE_TITLE', htmlspecialchars($line['title'])); - $tpl->setVariable('ARTICLE_EXCERPT', - truncate_string(strip_tags($line["content_preview"]), 100, '...')); - - $content = sanitize_rss($link, $line["content_preview"], false, $owner_uid); - - if ($line['note']) { - $content = "
    Article note: " . $line['note'] . "
    " . - $content; - } - - $tpl->setVariable('ARTICLE_CONTENT', $content); - - $tpl->setVariable('ARTICLE_UPDATED', date('c', strtotime($line["updated"]))); - $tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author'])); - - $tags = get_article_tags($link, $line["id"], $owner_uid); - - foreach ($tags as $tag) { - $tpl->setVariable('ARTICLE_CATEGORY', htmlspecialchars($tag)); - $tpl->addBlock('category'); - } - - $enclosures = get_article_enclosures($link, $line["id"]); - - foreach ($enclosures as $e) { - $type = htmlspecialchars($e['content_type']); - $url = htmlspecialchars($e['content_url']); - $length = $e['duration']; - - $tpl->setVariable('ARTICLE_ENCLOSURE_URL', $url); - $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', $type); - $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', $length); - - $tpl->addBlock('enclosure'); - } - - $tpl->addBlock('entry'); - } - - $tmp = ""; - - $tpl->addBlock('feed'); - $tpl->generateOutputToString($tmp); - - print $tmp; - } - - function getCategoryTitle($link, $cat_id) { - - if ($cat_id == -1) { - return __("Special"); - } else if ($cat_id == -2) { - return __("Labels"); - } else { - - $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE - id = '$cat_id'"); - - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "title"); - } else { - return "Uncategorized"; - } - } - } - - function sanitize_rss($link, $str, $force_strip_tags = false, $owner = false, $site_url = false) { - global $purifier; - - if (!$owner) $owner = $_SESSION["uid"]; - - $res = trim($str); if (!$res) return ''; - - // create global Purifier object if needed - if (!$purifier) { - require_once 'lib/htmlpurifier/library/HTMLPurifier.auto.php'; - - $config = HTMLPurifier_Config::createDefault(); - - $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title],ul,ol,li,h1,h2,h3,h4,s,object[classid|type|id|name|width|height|codebase],param[name|value],table,tr,td"; - - $config->set('HTML.SafeObject', true); - @$config->set('HTML', 'Allowed', $allowed); - $config->set('Output.FlashCompat', true); - $config->set('Attr.EnableID', true); - if (!defined('MOBILE_VERSION')) { - @$config->set('Cache', 'SerializerPath', CACHE_DIR . "/htmlpurifier"); - } else { - @$config->set('Cache', 'SerializerPath', "../" . CACHE_DIR . "/htmlpurifier"); - } - - $purifier = new HTMLPurifier($config); - } - - $res = $purifier->purify($res); - - if (get_pref($link, "STRIP_IMAGES", $owner)) { - $res = preg_replace('/]+>/is', '', $res); - } - - if (strpos($res, "href=") === false) - $res = rewrite_urls($res); - - $charset_hack = ' - - '; - - $res = trim($res); if (!$res) return ''; - - libxml_use_internal_errors(true); - - $doc = new DOMDocument(); - $doc->loadHTML($charset_hack . $res); - $xpath = new DOMXPath($doc); - - $entries = $xpath->query('(//a[@href]|//img[@src])'); - $br_inserted = 0; - - foreach ($entries as $entry) { - - if ($site_url) { - - if ($entry->hasAttribute('href')) - $entry->setAttribute('href', - rewrite_relative_url($site_url, $entry->getAttribute('href'))); - - if ($entry->hasAttribute('src')) - if (preg_match('/^image.php\?i=[a-z0-9]+$/', $entry->getAttribute('src')) == 0) - $entry->setAttribute('src', - rewrite_relative_url($site_url, $entry->getAttribute('src'))); - } - - if (strtolower($entry->nodeName) == "a") { - $entry->setAttribute("target", "_blank"); - } - - if (strtolower($entry->nodeName) == "img" && !$br_inserted) { - $br = $doc->createElement("br"); - - if ($entry->parentNode->nextSibling) { - $entry->parentNode->insertBefore($br, $entry->nextSibling); - $br_inserted = 1; - } - - } - } - - $node = $doc->getElementsByTagName('body')->item(0); - - return $doc->saveXML($node); - } - - /** - * Send by mail a digest of last articles. - * - * @param mixed $link The database connection. - * @param integer $limit The maximum number of articles by digest. - * @return boolean Return false if digests are not enabled. - */ - function send_headlines_digests($link, $limit = 100) { - - require_once 'lib/phpmailer/class.phpmailer.php'; - - if (!DIGEST_ENABLE) return false; - - $user_limit = DIGEST_EMAIL_LIMIT; - $days = 1; - - print "Sending digests, batch of max $user_limit users, days = $days, headline limit = $limit\n\n"; - - if (DB_TYPE == "pgsql") { - $interval_query = "last_digest_sent < NOW() - INTERVAL '$days days'"; - } else if (DB_TYPE == "mysql") { - $interval_query = "last_digest_sent < DATE_SUB(NOW(), INTERVAL $days DAY)"; - } - - $result = db_query($link, "SELECT id,email FROM ttrss_users - WHERE email != '' AND (last_digest_sent IS NULL OR $interval_query)"); - - while ($line = db_fetch_assoc($result)) { - - if (get_pref($link, 'DIGEST_ENABLE', $line['id'], false)) { - print "Sending digest for UID:" . $line['id'] . " - " . $line["email"] . " ... "; - - $do_catchup = get_pref($link, 'DIGEST_CATCHUP', $line['id'], false); - - $tuple = prepare_headlines_digest($link, $line["id"], $days, $limit); - $digest = $tuple[0]; - $headlines_count = $tuple[1]; - $affected_ids = $tuple[2]; - $digest_text = $tuple[3]; - - if ($headlines_count > 0) { - - $mail = new PHPMailer(); - - $mail->PluginDir = "lib/phpmailer/"; - $mail->SetLanguage("en", "lib/phpmailer/language/"); - - $mail->CharSet = "UTF-8"; - - $mail->From = DIGEST_FROM_ADDRESS; - $mail->FromName = DIGEST_FROM_NAME; - $mail->AddAddress($line["email"], $line["login"]); - - if (DIGEST_SMTP_HOST) { - $mail->Host = DIGEST_SMTP_HOST; - $mail->Mailer = "smtp"; - $mail->SMTPAuth = DIGEST_SMTP_LOGIN != ''; - $mail->Username = DIGEST_SMTP_LOGIN; - $mail->Password = DIGEST_SMTP_PASSWORD; - } - - $mail->IsHTML(true); - $mail->Subject = DIGEST_SUBJECT; - $mail->Body = $digest; - $mail->AltBody = $digest_text; - - $rc = $mail->Send(); - - if (!$rc) print "ERROR: " . $mail->ErrorInfo; - - print "RC=$rc\n"; - - if ($rc && $do_catchup) { - print "Marking affected articles as read...\n"; - catchupArticlesById($link, $affected_ids, 0, $line["id"]); - } - } else { - print "No headlines\n"; - } - - db_query($link, "UPDATE ttrss_users SET last_digest_sent = NOW() - WHERE id = " . $line["id"]); - } - } - - print "All done.\n"; - - } - - function prepare_headlines_digest($link, $user_id, $days = 1, $limit = 100) { - - require_once "lib/MiniTemplator.class.php"; - - $tpl = new MiniTemplator; - $tpl_t = new MiniTemplator; - - $tpl->readTemplateFromFile("templates/digest_template_html.txt"); - $tpl_t->readTemplateFromFile("templates/digest_template.txt"); - - $tpl->setVariable('CUR_DATE', date('Y/m/d')); - $tpl->setVariable('CUR_TIME', date('G:i')); - - $tpl_t->setVariable('CUR_DATE', date('Y/m/d')); - $tpl_t->setVariable('CUR_TIME', date('G:i')); - - $affected_ids = array(); - - if (DB_TYPE == "pgsql") { - $interval_query = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'"; - } else if (DB_TYPE == "mysql") { - $interval_query = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)"; - } - - $result = db_query($link, "SELECT ttrss_entries.title, - ttrss_feeds.title AS feed_title, - date_updated, - ttrss_user_entries.ref_id, - link, - SUBSTRING(content, 1, 120) AS excerpt, - ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM - ttrss_user_entries,ttrss_entries,ttrss_feeds - WHERE - ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id - AND include_in_digest = true - AND $interval_query - AND ttrss_user_entries.owner_uid = $user_id - AND unread = true - ORDER BY ttrss_feeds.title, date_updated DESC - LIMIT $limit"); - - $cur_feed_title = ""; - - $headlines_count = db_num_rows($result); - - $headlines = array(); - - while ($line = db_fetch_assoc($result)) { - array_push($headlines, $line); - } - - for ($i = 0; $i < sizeof($headlines); $i++) { - - $line = $headlines[$i]; - - array_push($affected_ids, $line["ref_id"]); - - $updated = make_local_datetime($link, $line['last_updated'], false, - $user_id); - - $tpl->setVariable('FEED_TITLE', $line["feed_title"]); - $tpl->setVariable('ARTICLE_TITLE', $line["title"]); - $tpl->setVariable('ARTICLE_LINK', $line["link"]); - $tpl->setVariable('ARTICLE_UPDATED', $updated); - $tpl->setVariable('ARTICLE_EXCERPT', - truncate_string(strip_tags($line["excerpt"]), 100)); - - $tpl->addBlock('article'); - - $tpl_t->setVariable('FEED_TITLE', $line["feed_title"]); - $tpl_t->setVariable('ARTICLE_TITLE', $line["title"]); - $tpl_t->setVariable('ARTICLE_LINK', $line["link"]); - $tpl_t->setVariable('ARTICLE_UPDATED', $updated); -// $tpl_t->setVariable('ARTICLE_EXCERPT', -// truncate_string(strip_tags($line["excerpt"]), 100)); - - $tpl_t->addBlock('article'); - - if ($headlines[$i]['feed_title'] != $headlines[$i+1]['feed_title']) { - $tpl->addBlock('feed'); - $tpl_t->addBlock('feed'); - } - - } - - $tpl->addBlock('digest'); - $tpl->generateOutputToString($tmp); - - $tpl_t->addBlock('digest'); - $tpl_t->generateOutputToString($tmp_t); - - return array($tmp, $headlines_count, $affected_ids, $tmp_t); - } - - function check_for_update($link) { - if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) { - $version_url = "http://tt-rss.org/version.php?ver=" . VERSION; - - $version_data = @fetch_file_contents($version_url); - - if ($version_data) { - $version_data = json_decode($version_data, true); - if ($version_data && $version_data['version']) { - - if (version_compare(VERSION, $version_data['version']) == -1) { - return $version_data; - } - } - } - } - return false; - } - - function markArticlesById($link, $ids, $cmode) { - - $tmp_ids = array(); - - foreach ($ids as $id) { - array_push($tmp_ids, "ref_id = '$id'"); - } - - $ids_qpart = join(" OR ", $tmp_ids); - - if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET - marked = false,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); - } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET - marked = true - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); - } else { - db_query($link, "UPDATE ttrss_user_entries SET - marked = NOT marked,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); - } - } - - function publishArticlesById($link, $ids, $cmode) { - - $tmp_ids = array(); - - foreach ($ids as $id) { - array_push($tmp_ids, "ref_id = '$id'"); - } - - $ids_qpart = join(" OR ", $tmp_ids); - - if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET - published = false,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); - } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET - published = true - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); - } else { - db_query($link, "UPDATE ttrss_user_entries SET - published = NOT published,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); - } - - if (PUBSUBHUBBUB_HUB) { - $rss_link = get_self_url_prefix() . - "/public.php?op=rss&id=-2&key=" . - get_feed_access_key($link, -2, false); - - $p = new Publisher(PUBSUBHUBBUB_HUB); - - $pubsub_result = $p->publish_update($rss_link); - } - } - - function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) { - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - if (count($ids) == 0) return; - - $tmp_ids = array(); - - foreach ($ids as $id) { - array_push($tmp_ids, "ref_id = '$id'"); - } - - $ids_qpart = join(" OR ", $tmp_ids); - - if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET - unread = false,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); - } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET - unread = true - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); - } else { - db_query($link, "UPDATE ttrss_user_entries SET - unread = NOT unread,last_read = NOW() - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); - } - - /* update ccache */ - - $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries - WHERE ($ids_qpart) AND owner_uid = $owner_uid"); - - while ($line = db_fetch_assoc($result)) { - ccache_update($link, $line["feed_id"], $owner_uid); - } - } - - function catchupArticleById($link, $id, $cmode) { - - if ($cmode == 0) { - db_query($link, "UPDATE ttrss_user_entries SET - unread = false,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - } else if ($cmode == 1) { - db_query($link, "UPDATE ttrss_user_entries SET - unread = true - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - } else { - db_query($link, "UPDATE ttrss_user_entries SET - unread = NOT unread,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - } - - $feed_id = getArticleFeed($link, $id); - ccache_update($link, $feed_id, $_SESSION["uid"]); - } - - function make_guid_from_title($title) { - return preg_replace("/[ \"\',.:;]/", "-", - mb_strtolower(strip_tags($title), 'utf-8')); - } - - function format_headline_subtoolbar($link, $feed_site_url, $feed_title, - $feed_id, $is_cat, $search, $match_on, - $search_mode, $view_mode, $error) { - - $page_prev_link = "viewFeedGoPage(-1)"; - $page_next_link = "viewFeedGoPage(1)"; - $page_first_link = "viewFeedGoPage(0)"; - - $catchup_page_link = "catchupPage()"; - $catchup_feed_link = "catchupCurrentFeed()"; - $catchup_sel_link = "catchupSelection()"; - - $archive_sel_link = "archiveSelection()"; - $delete_sel_link = "deleteSelection()"; - - $sel_all_link = "selectArticles('all')"; - $sel_unread_link = "selectArticles('unread')"; - $sel_none_link = "selectArticles('none')"; - $sel_inv_link = "selectArticles('invert')"; - - $tog_unread_link = "selectionToggleUnread()"; - $tog_marked_link = "selectionToggleMarked()"; - $tog_published_link = "selectionTogglePublished()"; - - $reply = "
    "; - - $reply .= __('Select:')." - ".__('All').", - ".__('Unread').", - ".__('Invert').", - ".__('None').""; - - $reply .= " "; - - $reply .= ""; - - $reply .= "
    "; - - $reply .= "
    "; - - if ($feed_site_url) { - $target = "target=\"_blank\""; - $reply .= "". - truncate_string($feed_title,30).""; - - if ($error) { - $reply .= " (Error)"; - } - - } else { - if ($feed_id < -10) { - $label_id = -11-$feed_id; - - $result = db_query($link, "SELECT fg_color, bg_color - FROM ttrss_labels2 WHERE id = '$label_id' AND owner_uid = " . - $_SESSION["uid"]); - - if (db_num_rows($result) != 0) { - $fg_color = db_fetch_result($result, 0, "fg_color"); - $bg_color = db_fetch_result($result, 0, "bg_color"); - - $reply .= ""; - $reply .= $feed_title; - $reply .= ""; - } else { - $reply .= $feed_title; - } - - } else { - $reply .= $feed_title; - } - } - - $reply .= " - - "; - - $reply .= "
    "; - - return $reply; - } - - function outputFeedList($link, $special = true) { - - $feedlist = array(); - - $enable_cats = get_pref($link, 'ENABLE_FEED_CATS'); - - $feedlist['identifier'] = 'id'; - $feedlist['label'] = 'name'; - $feedlist['items'] = array(); - - $owner_uid = $_SESSION["uid"]; - - /* virtual feeds */ - - if ($special) { - - if ($enable_cats) { - $cat_hidden = get_pref($link, "_COLLAPSED_SPECIAL"); - $cat = feedlist_init_cat($link, -1, $cat_hidden); - } else { - $cat['items'] = array(); - } - - foreach (array(-4, -3, -1, -2, 0) as $i) { - array_push($cat['items'], feedlist_init_feed($link, $i)); - } - - if ($enable_cats) { - array_push($feedlist['items'], $cat); - } else { - $feedlist['items'] = array_merge($feedlist['items'], $cat['items']); - } - - $result = db_query($link, "SELECT * FROM - ttrss_labels2 WHERE owner_uid = '$owner_uid' ORDER by caption"); - - if (db_num_rows($result) > 0) { - - if (get_pref($link, 'ENABLE_FEED_CATS')) { - $cat_hidden = get_pref($link, "_COLLAPSED_LABELS"); - $cat = feedlist_init_cat($link, -2, $cat_hidden); - } else { - $cat['items'] = array(); - } - - while ($line = db_fetch_assoc($result)) { - - $label_id = -$line['id'] - 11; - $count = getFeedUnread($link, $label_id); - - $feed = feedlist_init_feed($link, $label_id, false, $count); - - $feed['fg_color'] = $line['fg_color']; - $feed['bg_color'] = $line['bg_color']; - - array_push($cat['items'], $feed); - } - - if ($enable_cats) { - array_push($feedlist['items'], $cat); - } else { - $feedlist['items'] = array_merge($feedlist['items'], $cat['items']); - } - } - } - -/* if (get_pref($link, 'ENABLE_FEED_CATS')) { - if (get_pref($link, "FEEDS_SORT_BY_UNREAD")) { - $order_by_qpart = "order_id,category,unread DESC,title"; - } else { - $order_by_qpart = "order_id,category,title"; - } - } else { - if (get_pref($link, "FEEDS_SORT_BY_UNREAD")) { - $order_by_qpart = "unread DESC,title"; - } else { - $order_by_qpart = "title"; - } - } */ - - /* real feeds */ - - if ($enable_cats) - $order_by_qpart = "ttrss_feed_categories.order_id,category, - ttrss_feeds.order_id,title"; - else - $order_by_qpart = "title"; - - $age_qpart = getMaxAgeSubquery(); - - $query = "SELECT ttrss_feeds.id, ttrss_feeds.title, - ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated_noms, - cat_id,last_error, - ttrss_feed_categories.title AS category, - ttrss_feed_categories.collapsed, - value AS unread - FROM ttrss_feeds LEFT JOIN ttrss_feed_categories - ON (ttrss_feed_categories.id = cat_id) - LEFT JOIN ttrss_counters_cache - ON - (ttrss_feeds.id = feed_id) - WHERE - ttrss_feeds.owner_uid = '$owner_uid' - ORDER BY $order_by_qpart"; - - $result = db_query($link, $query); - - $actid = $_REQUEST["actid"]; - - if (db_num_rows($result) > 0) { - - $category = ""; - - if (!$enable_cats) - $cat['items'] = array(); - else - $cat = false; - - while ($line = db_fetch_assoc($result)) { - - $feed = htmlspecialchars(trim($line["title"])); - - if (!$feed) $feed = "[Untitled]"; - - $feed_id = $line["id"]; - $unread = $line["unread"]; - - $cat_id = $line["cat_id"]; - $tmp_category = $line["category"]; - if (!$tmp_category) $tmp_category = __("Uncategorized"); - - if ($category != $tmp_category && $enable_cats) { - - $category = $tmp_category; - - $collapsed = sql_bool_to_bool($line["collapsed"]); - - // workaround for NULL category - if ($category == __("Uncategorized")) { - $collapsed = get_pref($link, "_COLLAPSED_UNCAT"); - } - - if ($cat) array_push($feedlist['items'], $cat); - - $cat = feedlist_init_cat($link, $cat_id, $collapsed); - } - - $updated = make_local_datetime($link, $line["updated_noms"], false); - - array_push($cat['items'], feedlist_init_feed($link, $feed_id, - $feed, $unread, $line['last_error'], $updated)); - } - - if ($enable_cats) { - array_push($feedlist['items'], $cat); - } else { - $feedlist['items'] = array_merge($feedlist['items'], $cat['items']); - } - - } - - return $feedlist; - } - - function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) { - - global $memcache; - - $a_id = db_escape_string($id); - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $query = "SELECT DISTINCT tag_name, - owner_uid as owner FROM - ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE - ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name"; - - $obj_id = md5("TAGS:$owner_uid:$id"); - $tags = array(); - - if ($memcache && $obj = $memcache->get($obj_id)) { - $tags = $obj; - } else { - /* check cache first */ - - if ($tag_cache === false) { - $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - - $tag_cache = db_fetch_result($result, 0, "tag_cache"); - } - - if ($tag_cache) { - $tags = explode(",", $tag_cache); - } else { - - /* do it the hard way */ - - $tmp_result = db_query($link, $query); - - while ($tmp_line = db_fetch_assoc($tmp_result)) { - array_push($tags, $tmp_line["tag_name"]); - } - - /* update the cache */ - - $tags_str = db_escape_string(join(",", $tags)); - - db_query($link, "UPDATE ttrss_user_entries - SET tag_cache = '$tags_str' WHERE ref_id = '$id' - AND owner_uid = " . $_SESSION["uid"]); - } - - if ($memcache) $memcache->add($obj_id, $tags, 0, 3600); - } - - return $tags; - } - - function trim_array($array) { - $tmp = $array; - array_walk($tmp, 'trim'); - return $tmp; - } - - function tag_is_valid($tag) { - if ($tag == '') return false; - if (preg_match("/^[0-9]*$/", $tag)) return false; - if (mb_strlen($tag) > 250) return false; - - if (function_exists('iconv')) { - $tag = iconv("utf-8", "utf-8", $tag); - } - - if (!$tag) return false; - - return true; - } - - function render_login_form($link, $mobile = 0) { - switch ($mobile) { - case 0: - require_once "login_form.php"; - break; - case 1: - require_once "mobile/login_form.php"; - break; - case 2: - require_once "mobile/classic/login_form.php"; - } - } - - // from http://developer.apple.com/internet/safari/faq.html - function no_cache_incantation() { - header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :) - header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified - header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1 - header("Cache-Control: post-check=0, pre-check=0", false); - header("Pragma: no-cache"); // HTTP/1.0 - } - - function format_warning($msg, $id = "") { - global $link; - return "
    - $msg
    "; - } - - function format_notice($msg, $id = "") { - global $link; - return "
    - $msg
    "; - } - - function format_error($msg, $id = "") { - global $link; - return "
    - $msg
    "; - } - - function print_notice($msg) { - return print format_notice($msg); - } - - function print_warning($msg) { - return print format_warning($msg); - } - - function print_error($msg) { - return print format_error($msg); - } - - - function T_sprintf() { - $args = func_get_args(); - return vsprintf(__(array_shift($args)), $args); - } - - function format_inline_player($link, $url, $ctype) { - - $entry = ""; - - if (strpos($ctype, "audio/") === 0) { - - if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false || - strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false || - strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) { - - $id = 'AUDIO-' . uniqid(); - - $entry .= ""; - - $entry .= "".__("Play").""; - - } else { - - $entry .= " - - "; - } - } - - $filename = substr($url, strrpos($url, "/")+1); - - $entry .= " " . - $filename . " (" . $ctype . ")" . ""; - - return $entry; - } - - function format_article($link, $id, $mark_as_read = true, $zoom_mode = false) { - - $rv = array(); - - $rv['id'] = $id; - - /* we can figure out feed_id from article id anyway, why do we - * pass feed_id here? let's ignore the argument :( */ - - $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries - WHERE ref_id = '$id'"); - - $feed_id = (int) db_fetch_result($result, 0, "feed_id"); - - $rv['feed_id'] = $feed_id; - - //if (!$zoom_mode) { print "
    "; - } else { - $feed_icon = " "; - } - - $feed_site_url = $line['site_url']; - - $num_comments = $line["num_comments"]; - $entry_comments = ""; - - if ($num_comments > 0) { - if ($line["comments"]) { - $comments_url = $line["comments"]; - } else { - $comments_url = $line["link"]; - } - $entry_comments = "$num_comments comments"; - } else { - if ($line["comments"] && $line["link"] != $line["comments"]) { - $entry_comments = "comments"; - } - } - - if ($zoom_mode) { - header("Content-Type: text/html"); - $rv['content'] .= " - - Tiny Tiny RSS - ".$line["title"]." - - "; - } - - $rv['content'] .= "
    " . - truncate_string(strip_tags($line['title']), 15) . "
    "; - - $rv['content'] .= "
    "; - - $rv['content'] .= "
    "; - - $entry_author = $line["author"]; - - if ($entry_author) { - $entry_author = __(" - ") . $entry_author; - } - - $parsed_updated = make_local_datetime($link, $line["updated"], true, - false, true); - - $rv['content'] .= "
    $parsed_updated
    "; - - if ($line["link"]) { - $rv['content'] .= ""; - } else { - $rv['content'] .= "
    " . $line["title"] . "$entry_author
    "; - } - - $tag_cache = $line["tag_cache"]; - - if (!$tag_cache) - $tags = get_article_tags($link, $id); - else - $tags = explode(",", $tag_cache); - - $tags_str = format_tags_string($tags, $id); - $tags_str_full = join(", ", $tags); - - if (!$tags_str_full) $tags_str_full = __("no tags"); - - if (!$entry_comments) $entry_comments = " "; # placeholder - - $rv['content'] .= "
    - Tags "; - - if (!$zoom_mode) { - $rv['content'] .= "$tags_str - (+)"; - - $rv['content'] .= "
    $tags_str_full
    "; - - $rv['content'] .= "Zoom"; - - //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES); - - $rv['content'] .= "PubNote"; - - if (DIGEST_ENABLE) { - $rv['content'] .= "Zoom"; - } - - if (ENABLE_TWEET_BUTTON) { - $rv['content'] .= "Zoom"; - } - - $rv['content'] .= "Zoom"; - - $rv['content'] .= "Zoom"; - - } else { - $tags_str = strip_tags($tags_str); - $rv['content'] .= "$tags_str"; - } - $rv['content'] .= "
    "; - $rv['content'] .= "
    $entry_comments
    "; - - if ($line["orig_feed_id"]) { - - $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds - WHERE id = ".$line["orig_feed_id"]); - - if (db_num_rows($tmp_result) != 0) { - - $rv['content'] .= "
    "; - $rv['content'] .= __("Originally from:"); - - $rv['content'] .= " "; - - $tmp_line = db_fetch_assoc($tmp_result); - - $rv['content'] .= "" . - $tmp_line['title'] . ""; - - $rv['content'] .= " "; - - $rv['content'] .= ""; - $rv['content'] .= ""; - - $rv['content'] .= "
    "; - } - } - - $rv['content'] .= "
    "; - - $rv['content'] .= "
    "; - if ($line['note']) { - $rv['content'] .= format_article_note($id, $line['note']); - } - $rv['content'] .= "
    "; - - $rv['content'] .= ""; - - $rv['content'] .= "
    "; - - $article_content = sanitize_rss($link, $line["content"], false, false, - $feed_site_url); - - $rv['content'] .= $article_content; - - $rv['content'] .= format_article_enclosures($link, $id, - $always_display_enclosures, $article_content); - - $rv['content'] .= "
    "; - - $rv['content'] .= "
    "; - - } - - if ($zoom_mode) { - $rv['content'] .= " -
    -
    "; - $rv['content'] .= ""; - } - - return $rv; - - } - - function format_headlines_list($link, $feed, $subop, $view_mode, $limit, $cat_view, - $next_unread_feed, $offset, $vgr_last_feed = false, - $override_order = false) { - - $disable_cache = false; - - $reply = array(); - - $timing_info = getmicrotime(); - - $topmost_article_ids = array(); - - if (!$offset) $offset = 0; - if ($subop == "undefined") $subop = ""; - - $subop_split = explode(":", $subop); - -/* if ($subop == "CatchupSelected") { - $ids = explode(",", db_escape_string($_REQUEST["ids"])); - $cmode = sprintf("%d", $_REQUEST["cmode"]); - - catchupArticlesById($link, $ids, $cmode); - } */ - - if ($subop == "ForceUpdate" && $feed && is_numeric($feed) > 0) { - update_rss_feed($link, $feed, true); - } - - if ($subop == "MarkAllRead") { - catchup_feed($link, $feed, $cat_view); - - if (get_pref($link, 'ON_CATCHUP_SHOW_NEXT_FEED')) { - if ($next_unread_feed) { - $feed = $next_unread_feed; - } - } - } - - if ($subop_split[0] == "MarkAllReadGR") { - catchup_feed($link, $subop_split[1], false); - } - - // FIXME: might break tag display? - - if (is_numeric($feed) && $feed > 0 && !$cat_view) { - $result = db_query($link, - "SELECT id FROM ttrss_feeds WHERE id = '$feed' LIMIT 1"); - - if (db_num_rows($result) == 0) { - $reply['content'] = "
    ".__('Feed not found.')."
    "; - } - } - - if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { - - $result = db_query($link, "SELECT rtl_content FROM ttrss_feeds - WHERE id = '$feed' AND owner_uid = " . $_SESSION["uid"]); - - if (db_num_rows($result) == 1) { - $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content")); - } else { - $rtl_content = false; - } - - if ($rtl_content) { - $rtl_tag = "dir=\"RTL\""; - } else { - $rtl_tag = ""; - } - } else { - $rtl_tag = ""; - $rtl_content = false; - } - - @$search = db_escape_string($_REQUEST["query"]); - - if ($search) { - $disable_cache = true; - } - - @$search_mode = db_escape_string($_REQUEST["search_mode"]); - @$match_on = db_escape_string($_REQUEST["match_on"]); - - if (!$match_on) { - $match_on = "both"; - } - - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H0", $timing_info); - -// error_log("format_headlines_list: [" . $feed . "] subop [" . $subop . "]"); - if( $search_mode == '' && $subop != '' ){ - $search_mode = $subop; - } -// error_log("search_mode: " . $search_mode); - $qfh_ret = queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, - $search, $search_mode, $match_on, $override_order, $offset); - - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H1", $timing_info); - - $result = $qfh_ret[0]; - $feed_title = $qfh_ret[1]; - $feed_site_url = $qfh_ret[2]; - $last_error = $qfh_ret[3]; - - $vgroup_last_feed = $vgr_last_feed; - -// if (!$offset) { - - if (db_num_rows($result) > 0) { - $reply['toolbar'] = format_headline_subtoolbar($link, $feed_site_url, - $feed_title, - $feed, $cat_view, $search, $match_on, $search_mode, $view_mode, - $last_error); - } -// } - - $headlines_count = db_num_rows($result); - - if (db_num_rows($result) > 0) { - - $lnum = $offset; - - $num_unread = 0; - $cur_feed_title = ''; - - $fresh_intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE") * 60 * 60; - - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PS", $timing_info); - - while ($line = db_fetch_assoc($result)) { - - $class = ($lnum % 2) ? "even" : "odd"; - - $id = $line["id"]; - $feed_id = $line["feed_id"]; - $label_cache = $line["label_cache"]; - $labels = false; - - if ($label_cache) { - $label_cache = json_decode($label_cache, true); - - if ($label_cache) { - if ($label_cache["no-labels"] == 1) - $labels = array(); - else - $labels = $label_cache; - } - } - - if (!is_array($labels)) $labels = get_article_labels($link, $id); - - $labels_str = ""; - $labels_str .= format_article_labels($labels, $id); - $labels_str .= ""; - - if (count($topmost_article_ids) < 3) { - array_push($topmost_article_ids, $id); - } - - if ($line["last_read"] == "" && !sql_bool_to_bool($line["unread"])) { - - $update_pic = "\"Updated\""; - } else { - $update_pic = "\"Updated\""; - } - - if (sql_bool_to_bool($line["unread"]) && - time() - strtotime($line["updated_noms"]) < $fresh_intl) { - - $update_pic = "\"Fresh\""; - } - - if ($line["unread"] == "t" || $line["unread"] == "1") { - $class .= " Unread"; - ++$num_unread; - $is_unread = true; - } else { - $is_unread = false; - } - - if ($line["marked"] == "t" || $line["marked"] == "1") { - $marked_pic = "\"Unstar"; - } else { - $marked_pic = "\"Star"; - } - - if ($line["published"] == "t" || $line["published"] == "1") { - $published_pic = "\"Unpublish"; - } else { - $published_pic = "\"Publish"; - } - -# $content_link = "" . -# $line["title"] . ""; - -# $content_link = "" . -# $line["title"] . ""; - -# $content_link = "" . -# $line["title"] . ""; - - $updated_fmt = make_local_datetime($link, $line["updated_noms"], false); - - if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) { - $content_preview = truncate_string(strip_tags($line["content_preview"]), - 100); - } - - $score = $line["score"]; - - $score_pic = theme_image($link, - "images/" . get_score_pic($score)); - -/* $score_title = __("(Click to change)"); - $score_pic = ""; */ - - $score_pic = ""; - - if ($score > 500) { - $hlc_suffix = "H"; - } else if ($score < -100) { - $hlc_suffix = "L"; - } else { - $hlc_suffix = ""; - } - - $entry_author = $line["author"]; - - if ($entry_author) { - $entry_author = " - $entry_author"; - } - - $has_feed_icon = feed_has_icon($feed_id); - - if ($has_feed_icon) { - $feed_icon_img = "\"\""; - } else { - $feed_icon_img = "\"\""; - } - - if (!get_pref($link, 'COMBINED_DISPLAY_MODE')) { - - if (get_pref($link, 'VFEED_GROUP_BY_FEED')) { - if ($feed_id != $vgroup_last_feed && $line["feed_title"]) { - - $cur_feed_title = $line["feed_title"]; - $vgroup_last_feed = $feed_id; - - $cur_feed_title = htmlspecialchars($cur_feed_title); - - $vf_catchup_link = "(".__('mark as read').")"; - - $reply['content'] .= "
    ". - "
    $feed_icon_img
    ". - "". - $line["feed_title"]." $vf_catchup_link
    "; - - } - } - - $mouseover_attrs = "onmouseover='postMouseIn($id)' - onmouseout='postMouseOut($id)'"; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    $update_pic
    "; - - $reply['content'] .= "
    "; - - $reply['content'] .= ""; - - $reply['content'] .= "$marked_pic"; - $reply['content'] .= "$published_pic"; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - $reply['content'] .= "" . - truncate_string($line["title"], 200); - - if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) { - if ($content_preview) { - $reply['content'] .= " - $content_preview"; - } - } - - $reply['content'] .= ""; - - $reply['content'] .= $labels_str; - - if (!get_pref($link, 'VFEED_GROUP_BY_FEED') && - defined('_SHOW_FEED_TITLE_IN_VFEEDS')) { - if (@$line["feed_title"]) { - $reply['content'] .= " - (". - $line["feed_title"].") - "; - } - } - - $reply['content'] .= "
    "; - - $reply['content'] .= "$updated_fmt"; - $reply['content'] .= "
    "; - - $reply['content'] .= $score_pic; - - if ($line["feed_title"] && !get_pref($link, 'VFEED_GROUP_BY_FEED')) { - - $reply['content'] .= " - $feed_icon_img"; - } - - $reply['content'] .= "
    "; - $reply['content'] .= "
    "; - - } else { - - if (get_pref($link, 'VFEED_GROUP_BY_FEED') && $line["feed_title"]) { - if ($feed_id != $vgroup_last_feed) { - - $cur_feed_title = $line["feed_title"]; - $vgroup_last_feed = $feed_id; - - $cur_feed_title = htmlspecialchars($cur_feed_title); - - $vf_catchup_link = "(".__('mark as read').")"; - - $has_feed_icon = feed_has_icon($feed_id); - - if ($has_feed_icon) { - $feed_icon_img = "\"\""; - } else { - //$feed_icon_img = "\"\""; - } - - $reply['content'] .= "
    ". - "
    $feed_icon_img
    ". - "". - $line["feed_title"]." $vf_catchup_link
    "; - } - } - - $expand_cdm = get_pref($link, 'CDM_EXPANDED'); - - $mouseover_attrs = "onmouseover='postMouseIn($id)' - onmouseout='postMouseOut($id)'"; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - $reply['content'] .= ""; - - $reply['content'] .= "$marked_pic"; - $reply['content'] .= "$published_pic"; - - $reply['content'] .= "
    "; - - $reply['content'] .= " - ". - truncate_string($line["title"], 100) . - " $entry_author"; - - $reply['content'] .= $labels_str; - - if (!get_pref($link, 'VFEED_GROUP_BY_FEED') && - defined('_SHOW_FEED_TITLE_IN_VFEEDS')) { - if (@$line["feed_title"]) { - $reply['content'] .= " - (". - $line["feed_title"].") - "; - } - } - - if (!$expand_cdm) - $content_hidden = "style=\"display : none\""; - else - $excerpt_hidden = "style=\"display : none\""; - - $reply['content'] .= " - $content_preview"; - - $reply['content'] .= ""; - - $reply['content'] .= "
    "; - $reply['content'] .= "$updated_fmt"; - $reply['content'] .= "$score_pic"; - - if (!get_pref($link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) { - $reply['content'] .= "$feed_icon_img"; - } - $reply['content'] .= "
    $update_pic
    "; - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - if ($line["orig_feed_id"]) { - - $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds - WHERE id = ".$line["orig_feed_id"]); - - if (db_num_rows($tmp_result) != 0) { - - $reply['content'] .= "
    "; - $reply['content'] .= __("Originally from:"); - - $reply['content'] .= " "; - - $tmp_line = db_fetch_assoc($tmp_result); - - $reply['content'] .= "" . - $tmp_line['title'] . ""; - - $reply['content'] .= " "; - - $reply['content'] .= ""; - $reply['content'] .= ""; - - $reply['content'] .= "
    "; - } - } - - $feed_site_url = $line["site_url"]; - - $article_content = sanitize_rss($link, $line["content_preview"], - false, false, $feed_site_url); - - $reply['content'] .= "
    "; - if ($line['note']) { - $reply['content'] .= format_article_note($id, $line['note']); - } - $reply['content'] .= "
    "; - - $reply['content'] .= ""; - $reply['content'] .= $expand_cdm ? $article_content : ''; - $reply['content'] .= ""; - -/* $tmp_result = db_query($link, "SELECT always_display_enclosures FROM - ttrss_feeds WHERE id = ". - (($line['feed_id'] == null) ? $line['orig_feed_id'] : - $line['feed_id'])." AND owner_uid = ".$_SESSION["uid"]); - - $always_display_enclosures = sql_bool_to_bool(db_fetch_result($tmp_result, - 0, "always_display_enclosures")); */ - - $always_display_enclosures = sql_bool_to_bool($line["always_display_enclosures"]); - - $reply['content'] .= format_article_enclosures($link, $id, $always_display_enclosures, - $article_content); - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - $tag_cache = $line["tag_cache"]; - - $tags_str = format_tags_string( - get_article_tags($link, $id, $_SESSION["uid"], $tag_cache), - $id); - - $reply['content'] .= "Tags - $tags_str - (+)"; - - $num_comments = $line["num_comments"]; - $entry_comments = ""; - - if ($num_comments > 0) { - if ($line["comments"]) { - $comments_url = $line["comments"]; - } else { - $comments_url = $line["link"]; - } - $entry_comments = "$num_comments comments"; - } else { - if ($line["comments"] && $line["link"] != $line["comments"]) { - $entry_comments = "comments"; - } - } - - if ($entry_comments) $reply['content'] .= " ($entry_comments)"; - - $reply['content'] .= "
    "; - - $reply['content'] .= "Zoom"; - - //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES); - - $reply['content'] .= "PubNote"; - - if (DIGEST_ENABLE) { - $reply['content'] .= "Zoom"; - } - - if (ENABLE_TWEET_BUTTON) { - $reply['content'] .= "Zoom"; - } - - $reply['content'] .= "Zoom"; - - $reply['content'] .= "Dismiss"; - - $reply['content'] .= "
    "; - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - $reply['content'] .= "
    "; - - } - - ++$lnum; - } - - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PE", $timing_info); - - } else { - $message = ""; - - switch ($view_mode) { - case "unread": - $message = __("No unread articles found to display."); - break; - case "updated": - $message = __("No updated articles found to display."); - break; - case "marked": - $message = __("No starred articles found to display."); - break; - default: - if ($feed < -10) { - $message = __("No articles found to display. You can assign articles to labels manually (see the Actions menu above) or use a filter."); - } else { - $message = __("No articles found to display."); - } - } - - if (!$offset && $message) { - $reply['content'] .= "
    $message"; - - $reply['content'] .= "

    "; - - $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds - WHERE owner_uid = " . $_SESSION['uid']); - - $last_updated = db_fetch_result($result, 0, "last_updated"); - $last_updated = make_local_datetime($link, $last_updated, false); - - $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); - - $result = db_query($link, "SELECT COUNT(id) AS num_errors - FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); - - $num_errors = db_fetch_result($result, 0, "num_errors"); - - if ($num_errors > 0) { - $reply['content'] .= "
    "; - $reply['content'] .= "". - __('Some feeds have update errors (click for details)').""; - } - $reply['content'] .= "

    "; - } - } - - if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H2", $timing_info); - - return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, - $vgroup_last_feed, $reply); - } - -// from here: http://www.roscripts.com/Create_tag_cloud-71.html - - function printTagCloud($link) { - - $query = "SELECT tag_name, COUNT(post_int_id) AS count - FROM ttrss_tags WHERE owner_uid = ".$_SESSION["uid"]." - GROUP BY tag_name ORDER BY count DESC LIMIT 50"; - - $result = db_query($link, $query); - - $tags = array(); - - while ($line = db_fetch_assoc($result)) { - $tags[$line["tag_name"]] = $line["count"]; - } - - if( count($tags) == 0 ){ return; } - - ksort($tags); - - $max_size = 32; // max font size in pixels - $min_size = 11; // min font size in pixels - - // largest and smallest array values - $max_qty = max(array_values($tags)); - $min_qty = min(array_values($tags)); - - // find the range of values - $spread = $max_qty - $min_qty; - if ($spread == 0) { // we don't want to divide by zero - $spread = 1; - } - - // set the font-size increment - $step = ($max_size - $min_size) / ($spread); - - // loop through the tag array - foreach ($tags as $key => $value) { - // calculate font-size - // find the $value in excess of $min_qty - // multiply by the font-size increment ($size) - // and add the $min_size set above - $size = round($min_size + (($value - $min_qty) * $step)); - - $key_escaped = str_replace("'", "\\'", $key); - - echo "' . $key . ' '; - } - } - - function print_checkpoint($n, $s) { - $ts = getmicrotime(); - echo sprintf("", $ts - $s); - return $ts; - } - - function sanitize_tag($tag) { - $tag = trim($tag); - - $tag = mb_strtolower($tag, 'utf-8'); - - $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag); - -// $tag = str_replace('"', "", $tag); -// $tag = str_replace("+", " ", $tag); - $tag = str_replace("technorati tag: ", "", $tag); - - return $tag; - } - - function get_self_url_prefix() { - return SELF_URL_PATH; - } - - function opml_publish_url($link){ - - $url_path = get_self_url_prefix(); - $url_path .= "/opml.php?op=publish&key=" . - get_feed_access_key($link, 'OPML:Publish', false, $_SESSION["uid"]); - - return $url_path; - } - - /** - * Purge a feed contents, marked articles excepted. - * - * @param mixed $link The database connection. - * @param integer $id The id of the feed to purge. - * @return void - */ - function clear_feed_articles($link, $id) { - - if ($id != 0) { - $result = db_query($link, "DELETE FROM ttrss_user_entries - WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]); - } else { - $result = db_query($link, "DELETE FROM ttrss_user_entries - WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]); - } - - $result = db_query($link, "DELETE FROM ttrss_entries WHERE - (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0"); - - ccache_update($link, $id, $_SESSION['uid']); - } // function clear_feed_articles - - /** - * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI. - * - * @return string The Mozilla Firefox feed adding URL. - */ - function add_feed_url() { - //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); - - $url_path = get_self_url_prefix() . - "/backend.php?op=pref-feeds&quiet=1&subop=add&feed_url=%s"; - return $url_path; - } // function add_feed_url - - /** - * Encrypt a password in SHA1. - * - * @param string $pass The password to encrypt. - * @param string $login A optionnal login. - * @return string The encrypted password. - */ - function encrypt_password($pass, $login = '') { - if ($login) { - return "SHA1X:" . sha1("$login:$pass"); - } else { - return "SHA1:" . sha1($pass); - } - } // function encrypt_password - - /** - * Update a feed batch. - * Used by daemons to update n feeds by run. - * Only update feed needing a update, and not being processed - * by another process. - * - * @param mixed $link Database link - * @param integer $limit Maximum number of feeds in update batch. Default to DAEMON_FEED_LIMIT. - * @param boolean $from_http Set to true if you call this function from http to disable cli specific code. - * @param boolean $debug Set to false to disable debug output. Default to true. - * @return void - */ - function update_daemon_common($link, $limit = DAEMON_FEED_LIMIT, $from_http = false, $debug = true) { - // Process all other feeds using last_updated and interval parameters - - // Test if the user has loggued in recently. If not, it does not update its feeds. - if (DAEMON_UPDATE_LOGIN_LIMIT > 0) { - if (DB_TYPE == "pgsql") { - $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".DAEMON_UPDATE_LOGIN_LIMIT." days'"; - } else { - $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL ".DAEMON_UPDATE_LOGIN_LIMIT." DAY)"; - } - } else { - $login_thresh_qpart = ""; - } - - // Test if the feed need a update (update interval exceded). - if (DB_TYPE == "pgsql") { - $update_limit_qpart = "AND (( - ttrss_feeds.update_interval = 0 - AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL) - ) OR ( - ttrss_feeds.update_interval > 0 - AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL) - ) OR ttrss_feeds.last_updated IS NULL)"; - } else { - $update_limit_qpart = "AND (( - ttrss_feeds.update_interval = 0 - AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE) - ) OR ( - ttrss_feeds.update_interval > 0 - AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE) - ) OR ttrss_feeds.last_updated IS NULL)"; - } - - // Test if feed is currently being updated by another process. - if (DB_TYPE == "pgsql") { - $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '5 minutes')"; - } else { - $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))"; - } - - // Test if there is a limit to number of updated feeds - $query_limit = ""; - if($limit) $query_limit = sprintf("LIMIT %d", $limit); - - $random_qpart = sql_random_function(); - - // We search for feed needing update. - $result = db_query($link, "SELECT ttrss_feeds.feed_url,ttrss_feeds.id, ttrss_feeds.owner_uid, - ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated, - ttrss_feeds.update_interval - FROM - ttrss_feeds, ttrss_users, ttrss_user_prefs - WHERE - ttrss_feeds.owner_uid = ttrss_users.id - AND ttrss_users.id = ttrss_user_prefs.owner_uid - AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL' - $login_thresh_qpart $update_limit_qpart - $updstart_thresh_qpart - ORDER BY $random_qpart $query_limit"); - - $user_prefs_cache = array(); - - if($debug) _debug(sprintf("Scheduled %d feeds to update...\n", db_num_rows($result))); - - // Here is a little cache magic in order to minimize risk of double feed updates. - $feeds_to_update = array(); - while ($line = db_fetch_assoc($result)) { - $feeds_to_update[$line['id']] = $line; - } - - // We update the feed last update started date before anything else. - // There is no lag due to feed contents downloads - // It prevent an other process to update the same feed. - $feed_ids = array_keys($feeds_to_update); - if($feed_ids) { - db_query($link, sprintf("UPDATE ttrss_feeds SET last_update_started = NOW() - WHERE id IN (%s)", implode(',', $feed_ids))); - } - - // For each feed, we call the feed update function. - while ($line = array_pop($feeds_to_update)) { - - if($debug) _debug("Feed: " . $line["feed_url"] . ", " . $line["last_updated"]); - - update_rss_feed($link, $line["id"], true); - - sleep(1); // prevent flood (FIXME make this an option?) - } - - // Send feed digests by email if needed. - if (DAEMON_SENDS_DIGESTS) send_headlines_digests($link); - - } // function update_daemon_common - - function sanitize_article_content($text) { - # we don't support CDATA sections in articles, they break our own escaping - $text = preg_replace("/\[\[CDATA/", "", $text); - $text = preg_replace("/\]\]\>/", "", $text); - return $text; - } - - function load_filters($link, $feed, $owner_uid, $action_id = false) { - $filters = array(); - - global $memcache; - - $obj_id = md5("FILTER:$feed:$owner_uid:$action_id"); - - if ($memcache && $obj = $memcache->get($obj_id)) { - - return $obj; - - } else { - - if ($action_id) $ftype_query_part = "action_id = '$action_id' AND"; - - $result = db_query($link, "SELECT reg_exp, - ttrss_filter_types.name AS name, - ttrss_filter_actions.name AS action, - inverse, - action_param, - filter_param - FROM ttrss_filters,ttrss_filter_types,ttrss_filter_actions WHERE - enabled = true AND - $ftype_query_part - owner_uid = $owner_uid AND - ttrss_filter_types.id = filter_type AND - ttrss_filter_actions.id = action_id AND - (feed_id IS NULL OR feed_id = '$feed') ORDER BY reg_exp"); - - while ($line = db_fetch_assoc($result)) { - if (!$filters[$line["name"]]) $filters[$line["name"]] = array(); - $filter["reg_exp"] = $line["reg_exp"]; - $filter["action"] = $line["action"]; - $filter["action_param"] = $line["action_param"]; - $filter["filter_param"] = $line["filter_param"]; - $filter["inverse"] = sql_bool_to_bool($line["inverse"]); - - array_push($filters[$line["name"]], $filter); - } - - if ($memcache) $memcache->add($obj_id, $filters, 0, 3600*8); - - return $filters; - } - } - - function get_score_pic($score) { - if ($score > 100) { - return "score_high.png"; - } else if ($score > 0) { - return "score_half_high.png"; - } else if ($score < -100) { - return "score_low.png"; - } else if ($score < 0) { - return "score_half_low.png"; - } else { - return "score_neutral.png"; - } - } - - function feed_has_icon($id) { - return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0; - } - - function init_connection($link) { - if (DB_TYPE == "pgsql") { - pg_query($link, "set client_encoding = 'UTF-8'"); - pg_set_client_encoding("UNICODE"); - pg_query($link, "set datestyle = 'ISO, european'"); - pg_query($link, "set TIME ZONE 0"); - } else { - db_query($link, "SET time_zone = '+0:0'"); - - if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) { - db_query($link, "SET NAMES " . MYSQL_CHARSET); - // db_query($link, "SET CHARACTER SET " . MYSQL_CHARSET); - } - } - } - - function update_feedbrowser_cache($link) { - - $result = db_query($link, "SELECT feed_url, site_url, title, COUNT(id) AS subscribers - FROM ttrss_feeds WHERE (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf - WHERE tf.feed_url = ttrss_feeds.feed_url - AND (private IS true OR auth_login != '' OR auth_pass != '' OR feed_url LIKE '%:%@%/%')) - GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT 1000"); - - db_query($link, "BEGIN"); - - db_query($link, "DELETE FROM ttrss_feedbrowser_cache"); - - $count = 0; - - while ($line = db_fetch_assoc($result)) { - $subscribers = db_escape_string($line["subscribers"]); - $feed_url = db_escape_string($line["feed_url"]); - $title = db_escape_string($line["title"]); - $site_url = db_escape_string($line["site_url"]); - - $tmp_result = db_query($link, "SELECT subscribers FROM - ttrss_feedbrowser_cache WHERE feed_url = '$feed_url'"); - - if (db_num_rows($tmp_result) == 0) { - - db_query($link, "INSERT INTO ttrss_feedbrowser_cache - (feed_url, site_url, title, subscribers) VALUES ('$feed_url', - '$site_url', '$title', '$subscribers')"); - - ++$count; - - } - - } - - db_query($link, "COMMIT"); - - return $count; - - } - - /* function ccache_zero($link, $feed_id, $owner_uid) { - db_query($link, "UPDATE ttrss_counters_cache SET - value = 0, updated = NOW() WHERE - feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); - } */ - - function ccache_zero_all($link, $owner_uid) { - db_query($link, "UPDATE ttrss_counters_cache SET - value = 0 WHERE owner_uid = '$owner_uid'"); - - db_query($link, "UPDATE ttrss_cat_counters_cache SET - value = 0 WHERE owner_uid = '$owner_uid'"); - } - - function ccache_remove($link, $feed_id, $owner_uid, $is_cat = false) { - - if (!$is_cat) { - $table = "ttrss_counters_cache"; - } else { - $table = "ttrss_cat_counters_cache"; - } - - db_query($link, "DELETE FROM $table WHERE - feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); - - } - - function ccache_update_all($link, $owner_uid) { - - if (get_pref($link, 'ENABLE_FEED_CATS', $owner_uid)) { - - $result = db_query($link, "SELECT feed_id FROM ttrss_cat_counters_cache - WHERE feed_id > 0 AND owner_uid = '$owner_uid'"); - - while ($line = db_fetch_assoc($result)) { - ccache_update($link, $line["feed_id"], $owner_uid, true); - } - - /* We have to manually include category 0 */ - - ccache_update($link, 0, $owner_uid, true); - - } else { - $result = db_query($link, "SELECT feed_id FROM ttrss_counters_cache - WHERE feed_id > 0 AND owner_uid = '$owner_uid'"); - - while ($line = db_fetch_assoc($result)) { - print ccache_update($link, $line["feed_id"], $owner_uid); - - } - - } - } - - function ccache_find($link, $feed_id, $owner_uid, $is_cat = false, - $no_update = false) { - - if (!is_numeric($feed_id)) return; - - if (!$is_cat) { - $table = "ttrss_counters_cache"; - if ($feed_id > 0) { - $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds - WHERE id = '$feed_id'"); - $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid"); - } - } else { - $table = "ttrss_cat_counters_cache"; - } - - if (DB_TYPE == "pgsql") { - $date_qpart = "updated > NOW() - INTERVAL '15 minutes'"; - } else if (DB_TYPE == "mysql") { - $date_qpart = "updated > DATE_SUB(NOW(), INTERVAL 15 MINUTE)"; - } - - $result = db_query($link, "SELECT value FROM $table - WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' - LIMIT 1"); - - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "value"); - } else { - if ($no_update) { - return -1; - } else { - return ccache_update($link, $feed_id, $owner_uid, $is_cat); - } - } - - } - - function ccache_update($link, $feed_id, $owner_uid, $is_cat = false, - $update_pcat = true) { - - if (!is_numeric($feed_id)) return; - - if (!$is_cat && $feed_id > 0) { - $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds - WHERE id = '$feed_id'"); - $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid"); - } - - $prev_unread = ccache_find($link, $feed_id, $owner_uid, $is_cat, true); - - /* When updating a label, all we need to do is recalculate feed counters - * because labels are not cached */ - - if ($feed_id < 0) { - ccache_update_all($link, $owner_uid); - return; - } - - if (!$is_cat) { - $table = "ttrss_counters_cache"; - } else { - $table = "ttrss_cat_counters_cache"; - } - - if ($is_cat && $feed_id >= 0) { - if ($feed_id != 0) { - $cat_qpart = "cat_id = '$feed_id'"; - } else { - $cat_qpart = "cat_id IS NULL"; - } - - /* Recalculate counters for child feeds */ - - $result = db_query($link, "SELECT id FROM ttrss_feeds - WHERE owner_uid = '$owner_uid' AND $cat_qpart"); - - while ($line = db_fetch_assoc($result)) { - ccache_update($link, $line["id"], $owner_uid, false, false); - } - - $result = db_query($link, "SELECT SUM(value) AS sv - FROM ttrss_counters_cache, ttrss_feeds - WHERE id = feed_id AND $cat_qpart AND - ttrss_feeds.owner_uid = '$owner_uid'"); - - $unread = (int) db_fetch_result($result, 0, "sv"); - - } else { - $unread = (int) getFeedArticles($link, $feed_id, $is_cat, true, $owner_uid); - } - - db_query($link, "BEGIN"); - - $result = db_query($link, "SELECT feed_id FROM $table - WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' LIMIT 1"); - - if (db_num_rows($result) == 1) { - db_query($link, "UPDATE $table SET - value = '$unread', updated = NOW() WHERE - feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); - - } else { - db_query($link, "INSERT INTO $table - (feed_id, value, owner_uid, updated) - VALUES - ($feed_id, $unread, $owner_uid, NOW())"); - } - - db_query($link, "COMMIT"); - - if ($feed_id > 0 && $prev_unread != $unread) { - - if (!$is_cat) { - - /* Update parent category */ - - if ($update_pcat) { - - $result = db_query($link, "SELECT cat_id FROM ttrss_feeds - WHERE owner_uid = '$owner_uid' AND id = '$feed_id'"); - - $cat_id = (int) db_fetch_result($result, 0, "cat_id"); - - ccache_update($link, $cat_id, $owner_uid, true); - - } - } - } else if ($feed_id < 0) { - ccache_update_all($link, $owner_uid); - } - - return $unread; - } - - /* function ccache_cleanup($link, $owner_uid) { - - if (DB_TYPE == "pgsql") { - db_query($link, "DELETE FROM ttrss_counters_cache AS c1 WHERE - (SELECT count(*) FROM ttrss_counters_cache AS c2 - WHERE c1.feed_id = c2.feed_id AND c2.owner_uid = c1.owner_uid) > 1 - AND owner_uid = '$owner_uid'"); - - db_query($link, "DELETE FROM ttrss_cat_counters_cache AS c1 WHERE - (SELECT count(*) FROM ttrss_cat_counters_cache AS c2 - WHERE c1.feed_id = c2.feed_id AND c2.owner_uid = c1.owner_uid) > 1 - AND owner_uid = '$owner_uid'"); - } else { - db_query($link, "DELETE c1 FROM - ttrss_counters_cache AS c1, - ttrss_counters_cache AS c2 - WHERE - c1.owner_uid = '$owner_uid' AND - c1.owner_uid = c2.owner_uid AND - c1.feed_id = c2.feed_id"); - - db_query($link, "DELETE c1 FROM - ttrss_cat_counters_cache AS c1, - ttrss_cat_counters_cache AS c2 - WHERE - c1.owner_uid = '$owner_uid' AND - c1.owner_uid = c2.owner_uid AND - c1.feed_id = c2.feed_id"); - - } - } */ - - function label_find_id($link, $label, $owner_uid) { - $result = db_query($link, - "SELECT id FROM ttrss_labels2 WHERE caption = '$label' - AND owner_uid = '$owner_uid' LIMIT 1"); - - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "id"); - } else { - return 0; - } - } - - function get_article_labels($link, $id) { - global $memcache; - - $obj_id = md5("LABELS:$id:" . $_SESSION["uid"]); - - $rv = array(); - - if ($memcache && $obj = $memcache->get($obj_id)) { - return $obj; - } else { - - $result = db_query($link, "SELECT label_cache FROM - ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " . - $_SESSION["uid"]); - - $label_cache = db_fetch_result($result, 0, "label_cache"); - - if ($label_cache) { - - $label_cache = json_decode($label_cache, true); - - if ($label_cache["no-labels"] == 1) - return $rv; - else - return $label_cache; - } - - $result = db_query($link, - "SELECT DISTINCT label_id,caption,fg_color,bg_color - FROM ttrss_labels2, ttrss_user_labels2 - WHERE id = label_id - AND article_id = '$id' - AND owner_uid = ".$_SESSION["uid"] . " - ORDER BY caption"); - - while ($line = db_fetch_assoc($result)) { - $rk = array($line["label_id"], $line["caption"], $line["fg_color"], - $line["bg_color"]); - array_push($rv, $rk); - } - if ($memcache) $memcache->add($obj_id, $rv, 0, 3600); - - if (count($rv) > 0) - label_update_cache($link, $id, $rv); - else - label_update_cache($link, $id, array("no-labels" => 1)); - } - - return $rv; - } - - - function label_find_caption($link, $label, $owner_uid) { - $result = db_query($link, - "SELECT caption FROM ttrss_labels2 WHERE id = '$label' - AND owner_uid = '$owner_uid' LIMIT 1"); - - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "caption"); - } else { - return ""; - } - } - - function label_update_cache($link, $id, $labels = false, $force = false) { - - if ($force) - label_clear_cache($link, $id); - - if (!$labels) - $labels = get_article_labels($link, $id); - - $labels = db_escape_string(json_encode($labels)); - - db_query($link, "UPDATE ttrss_user_entries SET - label_cache = '$labels' WHERE ref_id = '$id'"); - - } - - function label_clear_cache($link, $id) { - - db_query($link, "UPDATE ttrss_user_entries SET - label_cache = '' WHERE ref_id = '$id'"); - - } - - function label_remove_article($link, $id, $label, $owner_uid) { - - $label_id = label_find_id($link, $label, $owner_uid); - - if (!$label_id) return; - - $result = db_query($link, - "DELETE FROM ttrss_user_labels2 - WHERE - label_id = '$label_id' AND - article_id = '$id'"); - - label_clear_cache($link, $id); - } - - function label_add_article($link, $id, $label, $owner_uid) { - - global $memcache; - - if ($memcache) { - $obj_id = md5("LABELS:$id:$owner_uid"); - $memcache->delete($obj_id); - } - - $label_id = label_find_id($link, $label, $owner_uid); - - if (!$label_id) return; - - $result = db_query($link, - "SELECT - article_id FROM ttrss_labels2, ttrss_user_labels2 - WHERE - label_id = id AND - label_id = '$label_id' AND - article_id = '$id' AND owner_uid = '$owner_uid' - LIMIT 1"); - - if (db_num_rows($result) == 0) { - db_query($link, "INSERT INTO ttrss_user_labels2 - (label_id, article_id) VALUES ('$label_id', '$id')"); - } - - label_clear_cache($link, $id); - - } - - function label_remove($link, $id, $owner_uid) { - global $memcache; - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - if ($memcache) { - $obj_id = md5("LABELS:$id:$owner_uid"); - $memcache->delete($obj_id); - } - - db_query($link, "BEGIN"); - - $result = db_query($link, "SELECT caption FROM ttrss_labels2 - WHERE id = '$id'"); - - $caption = db_fetch_result($result, 0, "caption"); - - $result = db_query($link, "DELETE FROM ttrss_labels2 WHERE id = '$id' - AND owner_uid = " . $owner_uid); - - if (db_affected_rows($link, $result) != 0 && $caption) { - - /* Remove access key for the label */ - - $ext_id = -11 - $id; - - db_query($link, "DELETE FROM ttrss_access_keys WHERE - feed_id = '$ext_id' AND owner_uid = $owner_uid"); - - /* Disable filters that reference label being removed */ - - db_query($link, "UPDATE ttrss_filters SET - enabled = false WHERE action_param = '$caption' - AND action_id = 7 - AND owner_uid = " . $owner_uid); - - /* Remove cached data */ - - db_query($link, "UPDATE ttrss_user_entries SET label_cache = '' - WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $owner_uid); - - } - - db_query($link, "COMMIT"); - } - - function label_create($link, $caption) { - - db_query($link, "BEGIN"); - - $result = false; - - $result = db_query($link, "SELECT id FROM ttrss_labels2 - WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]); - - if (db_num_rows($result) == 0) { - $result = db_query($link, - "INSERT INTO ttrss_labels2 (caption,owner_uid) - VALUES ('$caption', '".$_SESSION["uid"]."')"); - - $result = db_affected_rows($link, $result) != 0; - } - - db_query($link, "COMMIT"); - - return $result; - } - - function format_tags_string($tags, $id) { - - $tags_str = ""; - $tags_nolinks_str = ""; - - $num_tags = 0; - - $tag_limit = 6; - - $formatted_tags = array(); - - foreach ($tags as $tag) { - $num_tags++; - $tag_escaped = str_replace("'", "\\'", $tag); - - if (mb_strlen($tag) > 30) { - $tag = truncate_string($tag, 30); - } - - $tag_str = "$tag"; - - array_push($formatted_tags, $tag_str); - - $tmp_tags_str = implode(", ", $formatted_tags); - - if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) { - break; - } - } - - $tags_str = implode(", ", $formatted_tags); - - if ($num_tags < count($tags)) { - $tags_str .= ", …"; - } - - if ($num_tags == 0) { - $tags_str = __("no tags"); - } - - return $tags_str; - - } - - function format_article_labels($labels, $id) { - - $labels_str = ""; - - foreach ($labels as $l) { - $labels_str .= sprintf("%s", - $l[2], $l[3], $l[1]); - } - - return $labels_str; - - } - - function format_article_note($id, $note) { - - $str = "
    -
    ". - __('(edit note)')."
    $note
    "; - - return $str; - } - - function toggle_collapse_cat($link, $cat_id, $mode) { - if ($cat_id > 0) { - $mode = bool_to_sql_bool($mode); - - db_query($link, "UPDATE ttrss_feed_categories SET - collapsed = $mode WHERE id = '$cat_id' AND owner_uid = " . - $_SESSION["uid"]); - } else { - $pref_name = ''; - - switch ($cat_id) { - case -1: - $pref_name = '_COLLAPSED_SPECIAL'; - break; - case -2: - $pref_name = '_COLLAPSED_LABELS'; - break; - case 0: - $pref_name = '_COLLAPSED_UNCAT'; - break; - } - - if ($pref_name) { - if ($mode) { - set_pref($link, $pref_name, 'true'); - } else { - set_pref($link, $pref_name, 'false'); - } - } - } - } - - function remove_feed($link, $id, $owner_uid) { - - if ($id > 0) { - - /* save starred articles in Archived feed */ - - db_query($link, "BEGIN"); - - /* prepare feed if necessary */ - - $result = db_query($link, "SELECT id FROM ttrss_archived_feeds - WHERE id = '$id'"); - - if (db_num_rows($result) == 0) { - db_query($link, "INSERT INTO ttrss_archived_feeds - (id, owner_uid, title, feed_url, site_url) - SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds - WHERE id = '$id'"); - } - - db_query($link, "UPDATE ttrss_user_entries SET feed_id = NULL, - orig_feed_id = '$id' WHERE feed_id = '$id' AND - marked = true AND owner_uid = $owner_uid"); - - /* Remove access key for the feed */ - - db_query($link, "DELETE FROM ttrss_access_keys WHERE - feed_id = '$id' AND owner_uid = $owner_uid"); - - /* remove the feed */ - - db_query($link, "DELETE FROM ttrss_feeds - WHERE id = '$id' AND owner_uid = $owner_uid"); - - db_query($link, "COMMIT"); - - if (file_exists(ICONS_DIR . "/$id.ico")) { - unlink(ICONS_DIR . "/$id.ico"); - } - - ccache_remove($link, $id, $owner_uid); - - } else { - label_remove($link, -11-$id, $owner_uid); - ccache_remove($link, -11-$id, $owner_uid); - } - } - - function add_feed_category($link, $feed_cat) { - - if (!$feed_cat) return false; - - db_query($link, "BEGIN"); - - $result = db_query($link, - "SELECT id FROM ttrss_feed_categories - WHERE title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]); - - if (db_num_rows($result) == 0) { - - $result = db_query($link, - "INSERT INTO ttrss_feed_categories (owner_uid,title) - VALUES ('".$_SESSION["uid"]."', '$feed_cat')"); - - db_query($link, "COMMIT"); - - return true; - } - - return false; - } - - function remove_feed_category($link, $id, $owner_uid) { - - db_query($link, "DELETE FROM ttrss_feed_categories - WHERE id = '$id' AND owner_uid = $owner_uid"); - - ccache_remove($link, $id, $owner_uid, true); - } - - function archive_article($link, $id, $owner_uid) { - db_query($link, "BEGIN"); - - $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries - WHERE ref_id = '$id' AND owner_uid = $owner_uid"); - - if (db_num_rows($result) != 0) { - - /* prepare the archived table */ - - $feed_id = (int) db_fetch_result($result, 0, "feed_id"); - - if ($feed_id) { - $result = db_query($link, "SELECT id FROM ttrss_archived_feeds - WHERE id = '$feed_id'"); - - if (db_num_rows($result) == 0) { - db_query($link, "INSERT INTO ttrss_archived_feeds - (id, owner_uid, title, feed_url, site_url) - SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds - WHERE id = '$feed_id'"); - } - - db_query($link, "UPDATE ttrss_user_entries - SET orig_feed_id = feed_id, feed_id = NULL - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - } - } - - db_query($link, "COMMIT"); - } - - function getArticleFeed($link, $id) { - $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - - if (db_num_rows($result) != 0) { - return db_fetch_result($result, 0, "feed_id"); - } else { - return 0; - } - } - - /** - * Fixes incomplete URLs by prepending "http://". - * Also replaces feed:// with http://, and - * prepends a trailing slash if the url is a domain name only. - * - * @param string $url Possibly incomplete URL - * - * @return string Fixed URL. - */ - function fix_url($url) { - if (strpos($url, '://') === false) { - $url = 'http://' . $url; - } else if (substr($url, 0, 5) == 'feed:') { - $url = 'http:' . substr($url, 5); - } - - //prepend slash if the URL has no slash in it - // "http://www.example" -> "http://www.example/" - if (strpos($url, '/', strpos($url, ':') + 3) === false) { - $url .= '/'; - } - - if ($url != "http:///") - return $url; - else - return ''; - } - - function validate_feed_url($url) { - $parts = parse_url($url); - - return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https'); - - } - - function get_article_enclosures($link, $id) { - - global $memcache; - - $query = "SELECT * FROM ttrss_enclosures - WHERE post_id = '$id' AND content_url != ''"; - - $obj_id = md5("ENCLOSURES:$id"); - - $rv = array(); - - if ($memcache && $obj = $memcache->get($obj_id)) { - $rv = $obj; - } else { - $result = db_query($link, $query); - - if (db_num_rows($result) > 0) { - while ($line = db_fetch_assoc($result)) { - array_push($rv, $line); - } - if ($memcache) $memcache->add($obj_id, $rv, 0, 3600); - } - } - - return $rv; - } - - function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset) { - - $feeds = array(); - - /* Labels */ - - if ($cat_id == -4 || $cat_id == -2) { - $counters = getLabelCounters($link, true); - - foreach (array_values($counters) as $cv) { - - $unread = $cv["counter"]; - - if ($unread || !$unread_only) { - - $row = array( - "id" => $cv["id"], - "title" => $cv["description"], - "unread" => $cv["counter"], - "cat_id" => -2, - ); - - array_push($feeds, $row); - } - } - } - - /* Virtual feeds */ - - if ($cat_id == -4 || $cat_id == -1) { - foreach (array(-1, -2, -3, -4, 0) as $i) { - $unread = getFeedUnread($link, $i); - - if ($unread || !$unread_only) { - $title = getFeedTitle($link, $i); - - $row = array( - "id" => $i, - "title" => $title, - "unread" => $unread, - "cat_id" => -1, - ); - array_push($feeds, $row); - } - - } - } - - /* Real feeds */ - - if ($limit) { - $limit_qpart = "LIMIT $limit OFFSET $offset"; - } else { - $limit_qpart = ""; - } - - if ($cat_id == -4 || $cat_id == -3) { - $result = db_query($link, "SELECT - id, feed_url, cat_id, title, ". - SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"] . - " ORDER BY cat_id, title " . $limit_qpart); - } else { - - if ($cat_id) - $cat_qpart = "cat_id = '$cat_id'"; - else - $cat_qpart = "cat_id IS NULL"; - - $result = db_query($link, "SELECT - id, feed_url, cat_id, title, ". - SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE - $cat_qpart AND owner_uid = " . $_SESSION["uid"] . - " ORDER BY cat_id, title " . $limit_qpart); - } - - while ($line = db_fetch_assoc($result)) { - - $unread = getFeedUnread($link, $line["id"]); - - $has_icon = feed_has_icon($line['id']); - - if ($unread || !$unread_only) { - - $row = array( - "feed_url" => $line["feed_url"], - "title" => $line["title"], - "id" => (int)$line["id"], - "unread" => (int)$unread, - "has_icon" => $has_icon, - "cat_id" => (int)$line["cat_id"], - "last_updated" => strtotime($line["last_updated"]) - ); - - array_push($feeds, $row); - } - } - - return $feeds; - } - - function api_get_headlines($link, $feed_id, $limit, $offset, - $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order, - $include_attachments, $since_id) { - - /* do not rely on params below */ - - $search = db_escape_string($_REQUEST["search"]); - $search_mode = db_escape_string($_REQUEST["search_mode"]); - $match_on = db_escape_string($_REQUEST["match_on"]); - - $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit, - $view_mode, $is_cat, $search, $search_mode, $match_on, - $order, $offset, 0, false, $since_id); - - $result = $qfh_ret[0]; - $feed_title = $qfh_ret[1]; - - $headlines = array(); - - while ($line = db_fetch_assoc($result)) { - $is_updated = ($line["last_read"] == "" && - ($line["unread"] != "t" && $line["unread"] != "1")); - - $tags = explode(",", $line["tag_cache"]); - $labels = json_decode($line["label_cache"], true); - - //if (!$tags) $tags = get_article_tags($link, $line["id"]); - //if (!$labels) $labels = get_article_labels($link, $line["id"]); - - $headline_row = array( - "id" => (int)$line["id"], - "unread" => sql_bool_to_bool($line["unread"]), - "marked" => sql_bool_to_bool($line["marked"]), - "published" => sql_bool_to_bool($line["published"]), - "updated" => strtotime($line["updated"]), - "is_updated" => $is_updated, - "title" => $line["title"], - "link" => $line["link"], - "feed_id" => $line["feed_id"], - "tags" => $tags, - ); - - if ($include_attachments) - $headline_row['attachments'] = get_article_enclosures($link, - $line['id']); - - if ($show_excerpt) { - $excerpt = truncate_string(strip_tags($line["content_preview"]), 100); - $headline_row["excerpt"] = $excerpt; - } - - if ($show_content) { - $headline_row["content"] = $line["content_preview"]; - } - - // unify label output to ease parsing - if ($labels["no-labels"] == 1) $labels = array(); - - $headline_row["labels"] = $labels; - - array_push($headlines, $headline_row); - } - - return $headlines; - } - - function generate_error_feed($link, $error) { - $reply = array(); - - $reply['headlines']['id'] = -6; - $reply['headlines']['is_cat'] = false; - - $reply['headlines']['toolbar'] = ''; - $reply['headlines']['content'] = "
    ". $error . "
    "; - - $reply['headlines-info'] = array("count" => 0, - "vgroup_last_feed" => '', - "unread" => 0, - "disable_cache" => true); - - return $reply; - } - - - function generate_dashboard_feed($link) { - $reply = array(); - - $reply['headlines']['id'] = -5; - $reply['headlines']['is_cat'] = false; - - $reply['headlines']['toolbar'] = ''; - $reply['headlines']['content'] = "
    ".__('No feed selected.'); - - $reply['headlines']['content'] .= "

    "; - - $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds - WHERE owner_uid = " . $_SESSION['uid']); - - $last_updated = db_fetch_result($result, 0, "last_updated"); - $last_updated = make_local_datetime($link, $last_updated, false); - - $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); - - $result = db_query($link, "SELECT COUNT(id) AS num_errors - FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); - - $num_errors = db_fetch_result($result, 0, "num_errors"); - - if ($num_errors > 0) { - $reply['headlines']['content'] .= "
    "; - $reply['headlines']['content'] .= "". - __('Some feeds have update errors (click for details)').""; - } - $reply['headlines']['content'] .= "

    "; - - $reply['headlines-info'] = array("count" => 0, - "vgroup_last_feed" => '', - "unread" => 0, - "disable_cache" => true); - - return $reply; - } - - function save_email_address($link, $email) { - // FIXME: implement persistent storage of emails - - if (!$_SESSION['stored_emails']) - $_SESSION['stored_emails'] = array(); - - if (!in_array($email, $_SESSION['stored_emails'])) - array_push($_SESSION['stored_emails'], $email); - } - - function update_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) { - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $sql_is_cat = bool_to_sql_bool($is_cat); - - $result = db_query($link, "SELECT access_key FROM ttrss_access_keys - WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat - AND owner_uid = " . $owner_uid); - - if (db_num_rows($result) == 1) { - $key = db_escape_string(sha1(uniqid(rand(), true))); - - db_query($link, "UPDATE ttrss_access_keys SET access_key = '$key' - WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat - AND owner_uid = " . $owner_uid); - - return $key; - - } else { - return get_feed_access_key($link, $feed_id, $is_cat, $owner_uid); - } - } - - function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) { - - if (!$owner_uid) $owner_uid = $_SESSION["uid"]; - - $sql_is_cat = bool_to_sql_bool($is_cat); - - $result = db_query($link, "SELECT access_key FROM ttrss_access_keys - WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat - AND owner_uid = " . $owner_uid); - - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "access_key"); - } else { - $key = db_escape_string(sha1(uniqid(rand(), true))); - - $result = db_query($link, "INSERT INTO ttrss_access_keys - (access_key, feed_id, is_cat, owner_uid) - VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')"); - - return $key; - } - return false; - } - - /** - * Extracts RSS/Atom feed URLs from the given HTML URL. - * - * @param string $url HTML page URL - * - * @return array Array of feeds. Key is the full URL, value the title - */ - function get_feeds_from_html($url, $login = false, $pass = false) - { - $url = fix_url($url); - $baseUrl = substr($url, 0, strrpos($url, '/') + 1); - - libxml_use_internal_errors(true); - - $content = @fetch_file_contents($url, false, $login, $pass); - - $doc = new DOMDocument(); - $doc->loadHTML($content); - $xpath = new DOMXPath($doc); - $entries = $xpath->query('/html/head/link[@rel="alternate"]'); - $feedUrls = array(); - foreach ($entries as $entry) { - if ($entry->hasAttribute('href')) { - $title = $entry->getAttribute('title'); - if ($title == '') { - $title = $entry->getAttribute('type'); - } - $feedUrl = rewrite_relative_url( - $baseUrl, $entry->getAttribute('href') - ); - $feedUrls[$feedUrl] = $title; - } - } - return $feedUrls; - } - - /** - * Checks if the content behind the given URL is a HTML file - * - * @param string $url URL to check - * - * @return boolean True if the URL contains HTML content - */ - function url_is_html($url, $login = false, $pass = false) { - $content = substr(fetch_file_contents($url, false, $login, $pass), 0, 1000); - - if (stripos($content, '') === false - && stripos($content, '"; - - while ($line = db_fetch_assoc($result)) { - - $issel = ($line["caption"] == $value) ? "selected=\"1\"" : ""; - - print ""; - - } - -# print ""; - - print ""; - - - } - - function format_article_enclosures($link, $id, $always_display_enclosures, - $article_content) { - - $result = get_article_enclosures($link, $id); - $rv = ''; - - if (count($result) > 0) { - - $entries_html = array(); - $entries = array(); - - foreach ($result as $line) { - - $url = $line["content_url"]; - $ctype = $line["content_type"]; - - if (!$ctype) $ctype = __("unknown type"); - -# $filename = substr($url, strrpos($url, "/")+1); - - $entry = format_inline_player($link, $url, $ctype); - -# $entry .= " " . -# $filename . " (" . $ctype . ")" . ""; - - array_push($entries_html, $entry); - - $entry = array(); - - $entry["type"] = $ctype; - $entry["filename"] = $filename; - $entry["url"] = $url; - - array_push($entries, $entry); - } - - $rv .= "
    "; - - if (!get_pref($link, "STRIP_IMAGES")) { - if ($always_display_enclosures || - !preg_match("/

    "; - } - } - } - } - - if (count($entries) == 1) { - $rv .= __("Attachment:") . " "; - } else { - $rv .= __("Attachments:") . " "; - } - - $rv .= join(", ", $entries_html); - - $rv .= "
    "; - } - - return $rv; - } - - function getLastArticleId($link) { - $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries - WHERE owner_uid = " . $_SESSION["uid"]); - - if (db_num_rows($result) == 1) { - return db_fetch_result($result, 0, "id"); - } else { - return -1; - } - } - - function build_url($parts) { - return $parts['scheme'] . "://" . $parts['host'] . $parts['path']; - } - - /** - * Converts a (possibly) relative URL to a absolute one. - * - * @param string $url Base URL (i.e. from where the document is) - * @param string $rel_url Possibly relative URL in the document - * - * @return string Absolute URL - */ - function rewrite_relative_url($url, $rel_url) { - if (strpos($rel_url, "://") !== false) { - return $rel_url; - } else if (strpos($rel_url, "/") === 0) - { - $parts = parse_url($url); - $parts['path'] = $rel_url; - - return build_url($parts); - - } else { - $parts = parse_url($url); - if (!isset($parts['path'])) { - $parts['path'] = '/'; - } - $dir = $parts['path']; - if (substr($dir, -1) !== '/') { - $dir = dirname($parts['path']); - $dir !== '/' && $dir .= '/'; - } - $parts['path'] = $dir . $rel_url; - - return build_url($parts); - } - } - - function sphinx_search($query, $offset = 0, $limit = 30) { - require_once 'lib/sphinxapi.php'; - - $sphinxClient = new SphinxClient(); - - $sphinxClient->SetServer('localhost', 9312); - $sphinxClient->SetConnectTimeout(1); - - $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30, - 'feed_title' => 20)); - - $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2); - $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25); - $sphinxClient->SetLimits($offset, $limit, 1000); - $sphinxClient->SetArrayResult(false); - $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid'])); - - $result = $sphinxClient->Query($query, SPHINX_INDEX); - - $ids = array(); - - if (is_array($result['matches'])) { - foreach (array_keys($result['matches']) as $int_id) { - $ref_id = $result['matches'][$int_id]['attrs']['ref_id']; - array_push($ids, $ref_id); - } - } - - return $ids; - } - - function cleanup_tags($link, $days = 14, $limit = 1000) { - - if (DB_TYPE == "pgsql") { - $interval_query = "date_updated < NOW() - INTERVAL '$days days'"; - } else if (DB_TYPE == "mysql") { - $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)"; - } - - $tags_deleted = 0; - - while ($limit > 0) { - $limit_part = 500; - - $query = "SELECT ttrss_tags.id AS id - FROM ttrss_tags, ttrss_user_entries, ttrss_entries - WHERE post_int_id = int_id AND $interval_query AND - ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part"; - - $result = db_query($link, $query); - - $ids = array(); - - while ($line = db_fetch_assoc($result)) { - array_push($ids, $line['id']); - } - - 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); - } else { - break; - } - - $limit -= $limit_part; - } - - print "\n"; - - return $tags_deleted; - } - - function feedlist_init_cat($link, $cat_id, $hidden = false) { - $obj = array(); - $cat_id = (int) $cat_id; - - if ($cat_id > 0) { - $cat_unread = ccache_find($link, $cat_id, $_SESSION["uid"], true); - } else if ($cat_id == 0 || $cat_id == -2) { - $cat_unread = getCategoryUnread($link, $cat_id); - } - - $obj['id'] = 'CAT:' . $cat_id; - $obj['items'] = array(); - $obj['name'] = getCategoryTitle($link, $cat_id); - $obj['type'] = 'feed'; - $obj['unread'] = (int) $cat_unread; - $obj['hidden'] = $hidden; - $obj['bare_id'] = $cat_id; - - return $obj; - } - - function feedlist_init_feed($link, $feed_id, $title = false, $unread = false, $error = '', $updated = '') { - $obj = array(); - $feed_id = (int) $feed_id; - - if (!$title) - $title = getFeedTitle($link, $feed_id, false); - - if ($unread === false) - $unread = getFeedUnread($link, $feed_id, false); - - $obj['id'] = 'FEED:' . $feed_id; - $obj['name'] = $title; - $obj['unread'] = (int) $unread; - $obj['type'] = 'feed'; - $obj['error'] = $error; - $obj['updated'] = $updated; - $obj['icon'] = getFeedIcon($feed_id); - $obj['bare_id'] = $feed_id; - - return $obj; - } - - - function fetch_twitter_rss($link, $url, $owner_uid) { - - require_once 'lib/tmhoauth/tmhOAuth.php'; - - $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users - WHERE id = $owner_uid"); - - $access_token = json_decode(db_fetch_result($result, 0, 'twitter_oauth'), true); - $url_escaped = db_escape_string($url); - - if ($access_token) { - - $tmhOAuth = new tmhOAuth(array( - 'consumer_key' => CONSUMER_KEY, - 'consumer_secret' => CONSUMER_SECRET, - 'user_token' => $access_token['oauth_token'], - 'user_secret' => $access_token['oauth_token_secret'], - )); - - $code = $tmhOAuth->request('GET', $url); - - if ($code == 200) { - - $content = $tmhOAuth->response['response']; - - define('MAGPIE_CACHE_ON', false); - - $rss = new MagpieRSS($content, MAGPIE_OUTPUT_ENCODING, - MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING ); - - return $rss; - - } else { - - db_query($link, "UPDATE ttrss_feeds - SET last_error = 'OAuth authorization failed ($code).' - WHERE feed_url = '$url_escaped' AND owner_uid = $owner_uid"); - } - - } else { - - db_query($link, "UPDATE ttrss_feeds - SET last_error = 'OAuth information not found.' - WHERE feed_url = '$url_escaped' AND owner_uid = $owner_uid"); - - return false; - } - } - - function print_user_stylesheet($link) { - $value = get_pref($link, 'USER_STYLESHEET'); - - if ($value) { - print ""; - } - - } - - function rewrite_urls($line) { - global $url_regex; - - $urls = null; - - $result = preg_replace("/((?\\1", $line); - - return $result; - } - - function filter_to_sql($filter) { - $query = ""; - - if (DB_TYPE == "pgsql") - $reg_qpart = "~"; - else - $reg_qpart = "REGEXP"; - - switch ($filter["type"]) { - case "title": - $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "content": - $query = "LOWER(ttrss_entries.content) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "both": - $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". - $filter['reg_exp'] . "') OR LOWER(" . - "ttrss_entries.content) $reg_qpart LOWER('" . $filter['reg_exp'] . "')"; - break; - case "tag": - $query = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "link": - $query = "LOWER(ttrss_entries.link) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - case "date": - - if ($filter["filter_param"] == "before") - $cmp_qpart = "<"; - else - $cmp_qpart = ">="; - - $timestamp = date("Y-m-d H:N:s", strtotime($filter["reg_exp"])); - $query = "ttrss_entries.date_entered $cmp_qpart '$timestamp'"; - break; - case "author": - $query = "LOWER(ttrss_entries.author) $reg_qpart LOWER('". - $filter['reg_exp'] . "')"; - break; - } - - if ($filter["inverse"]) - $query = "NOT ($query)"; - - if ($query) { - if (DB_TYPE == "pgsql") { - $query = " ($query) AND ttrss_entries.date_entered > NOW() - INTERVAL '14 days'"; - } else { - $query = " ($query) AND ttrss_entries.date_entered > DATE_SUB(NOW(), INTERVAL 14 DAY)"; - } - $query .= " AND "; - } - - - return $query; - } - - // Status codes: - // -1 - never connected - // 0 - no data received - // 1 - data received successfully - // 2 - did not receive valid data - // >10 - server error, code + 10 (e.g. 16 means server error 6) - - function get_linked_feeds($link, $instance_id = false) { - if ($instance_id) - $instance_qpart = "id = '$instance_id' AND "; - else - $instance_qpart = ""; - - if (DB_TYPE == "pgsql") { - $date_qpart = "last_connected < NOW() - INTERVAL '6 hours'"; - } else { - $date_qpart = "last_connected < DATE_SUB(NOW(), INTERVAL 6 HOUR)"; - } - - $result = db_query($link, "SELECT id, access_key, access_url FROM ttrss_linked_instances - WHERE $instance_qpart $date_qpart ORDER BY last_connected"); - - while ($line = db_fetch_assoc($result)) { - $id = $line['id']; - - _debug("Updating: " . $line['access_url'] . " ($id)"); - - $fetch_url = $line['access_url'] . '/public.php?op=fbexport'; - $post_query = 'key=' . $line['access_key']; - - $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query); - - // try doing it the old way - if (!$feeds) { - $fetch_url = $line['access_url'] . '/backend.php?op=fbexport'; - $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query); - } - - if ($feeds) { - $feeds = json_decode($feeds, true); - - if ($feeds) { - if ($feeds['error']) { - $status = $feeds['error']['code'] + 10; - } else { - $status = 1; - - if (count($feeds['feeds']) > 0) { - - db_query($link, "DELETE FROM ttrss_linked_feeds - WHERE instance_id = '$id'"); - - foreach ($feeds['feeds'] as $feed) { - $feed_url = db_escape_string($feed['feed_url']); - $title = db_escape_string($feed['title']); - $subscribers = db_escape_string($feed['subscribers']); - $site_url = db_escape_string($feed['site_url']); - - db_query($link, "INSERT INTO ttrss_linked_feeds - (feed_url, site_url, title, subscribers, instance_id, created, updated) - VALUES - ('$feed_url', '$site_url', '$title', '$subscribers', '$id', NOW(), NOW())"); - } - } else { - // received 0 feeds, this might indicate that - // the instance on the other hand is rebuilding feedbrowser cache - // we will try again later - - // TODO: maybe perform expiration based on updated here? - } - - _debug("Processed " . count($feeds['feeds']) . " feeds."); - } - } else { - $status = 2; - } - - } else { - $status = 0; - } - - _debug("Status: $status"); - - db_query($link, "UPDATE ttrss_linked_instances SET - last_status_out = '$status', last_connected = NOW() WHERE id = '$id'"); - - } - } - - function handle_public_request($link, $op) { - switch ($op) { - - case "getUnread": - $login = db_escape_string($_REQUEST["login"]); - $fresh = $_REQUEST["fresh"] == "1"; - - $result = db_query($link, "SELECT id FROM ttrss_users WHERE login = '$login'"); - - if (db_num_rows($result) == 1) { - $uid = db_fetch_result($result, 0, "id"); - - print getGlobalUnread($link, $uid); - - if ($fresh) { - print ";"; - print getFeedArticles($link, -3, false, true, $uid); - } - - } else { - print "-1;User not found"; - } - - break; // getUnread - - case "getProfiles": - $login = db_escape_string($_REQUEST["login"]); - $password = db_escape_string($_REQUEST["password"]); - - if (authenticate_user($link, $login, $password)) { - $result = db_query($link, "SELECT * FROM ttrss_settings_profiles - WHERE owner_uid = " . $_SESSION["uid"] . " ORDER BY title"); - - print ""; - - $_SESSION = array(); - } - break; // getprofiles - - case "pubsub": - $mode = db_escape_string($_REQUEST['hub_mode']); - $feed_id = (int) db_escape_string($_REQUEST['id']); - $feed_url = db_escape_string($_REQUEST['hub_topic']); - - if (!PUBSUBHUBBUB_ENABLED) { - header('HTTP/1.0 404 Not Found'); - echo "404 Not found"; - return; - } - - // TODO: implement hub_verifytoken checking - - $result = db_query($link, "SELECT feed_url FROM ttrss_feeds - WHERE id = '$feed_id'"); - - if (db_num_rows($result) != 0) { - - $check_feed_url = db_fetch_result($result, 0, "feed_url"); - - if ($check_feed_url && ($check_feed_url == $feed_url || !$feed_url)) { - if ($mode == "subscribe") { - - db_query($link, "UPDATE ttrss_feeds SET pubsub_state = 2 - WHERE id = '$feed_id'"); - - print $_REQUEST['hub_challenge']; - return; - - } else if ($mode == "unsubscribe") { - - db_query($link, "UPDATE ttrss_feeds SET pubsub_state = 0 - WHERE id = '$feed_id'"); - - print $_REQUEST['hub_challenge']; - return; - - } else if (!$mode) { - - // Received update ping, schedule feed update. - //update_rss_feed($link, $feed_id, true, true); - - db_query($link, "UPDATE ttrss_feeds SET - last_update_started = '1970-01-01', - last_updated = '1970-01-01' WHERE id = '$feed_id'"); - - } - } else { - header('HTTP/1.0 404 Not Found'); - echo "404 Not found"; - } - } else { - header('HTTP/1.0 404 Not Found'); - echo "404 Not found"; - } - - break; // pubsub - - case "logout": - logout_user(); - header("Location: tt-rss.php"); - break; // logout - - case "fbexport": - - $access_key = db_escape_string($_POST["key"]); - - // TODO: rate limit checking using last_connected - $result = db_query($link, "SELECT id FROM ttrss_linked_instances - WHERE access_key = '$access_key'"); - - if (db_num_rows($result) == 1) { - - $instance_id = db_fetch_result($result, 0, "id"); - - $result = db_query($link, "SELECT feed_url, site_url, title, subscribers - FROM ttrss_feedbrowser_cache ORDER BY subscribers DESC LIMIT 100"); - - $feeds = array(); - - while ($line = db_fetch_assoc($result)) { - array_push($feeds, $line); - } - - db_query($link, "UPDATE ttrss_linked_instances SET - last_status_in = 1 WHERE id = '$instance_id'"); - - print json_encode(array("feeds" => $feeds)); - } else { - print json_encode(array("error" => array("code" => 6))); - } - break; // fbexport - - case "share": - $uuid = db_escape_string($_REQUEST["key"]); - - $result = db_query($link, "SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE - uuid = '$uuid'"); - - if (db_num_rows($result) != 0) { - header("Content-Type: text/html"); - - $id = db_fetch_result($result, 0, "ref_id"); - $owner_uid = db_fetch_result($result, 0, "owner_uid"); - - $_SESSION["uid"] = $owner_uid; - $article = format_article($link, $id, false, true); - $_SESSION["uid"] = ""; - - print_r($article['content']); - - } else { - print "Article not found."; - } - - break; - - case "rss": - $feed = db_escape_string($_REQUEST["id"]); - $key = db_escape_string($_REQUEST["key"]); - $is_cat = $_REQUEST["is_cat"] != false; - $limit = (int)db_escape_string($_REQUEST["limit"]); - - $search = db_escape_string($_REQUEST["q"]); - $match_on = db_escape_string($_REQUEST["m"]); - $search_mode = db_escape_string($_REQUEST["smode"]); - $view_mode = db_escape_string($_REQUEST["view-mode"]); - - if (SINGLE_USER_MODE) { - authenticate_user($link, "admin", null); - } - - $owner_id = false; - - if ($key) { - $result = db_query($link, "SELECT owner_uid FROM - ttrss_access_keys WHERE access_key = '$key' AND feed_id = '$feed'"); - - if (db_num_rows($result) == 1) - $owner_id = db_fetch_result($result, 0, "owner_uid"); - } - - if ($owner_id) { - $_SESSION['uid'] = $owner_id; - - generate_syndicated_feed($link, 0, $feed, $is_cat, $limit, - $search, $search_mode, $match_on, $view_mode); - } else { - header('HTTP/1.1 403 Forbidden'); - } - break; // rss - - - case "globalUpdateFeeds": - // Update all feeds needing a update. - update_daemon_common($link, 0, true, true); - break; // globalUpdateFeeds - - - default: - header("Content-Type: text/plain"); - print json_encode(array("error" => array("code" => 7))); - break; // fallback - - } - } -?> diff --git a/image.php b/image.php index 45e1358aa..210bbc2f7 100644 --- a/image.php +++ b/image.php @@ -1,4 +1,6 @@ 0) { + $value = db_fetch_result($result, 0, "value"); + $type_name = db_fetch_result($result, 0, "type_name"); + + if (!defined('DISABLE_SESSIONS')) { + if ($user_id == $_SESSION["uid"]) { + $_SESSION["prefs_cache"][$pref_name]["type"] = $type_name; + $_SESSION["prefs_cache"][$pref_name]["value"] = $value; + } + } + + return convert_pref_type($value, $type_name); + + } else { + if ($die_on_error) { + die("Fatal error, unknown preferences key: $pref_name"); + } else { + return null; + } + } + } + + function convert_pref_type($value, $type_name) { + if ($type_name == "bool") { + return $value == "true"; + } else if ($type_name == "integer") { + return sprintf("%d", $value); + } else { + return $value; + } + } + + function set_pref($link, $pref_name, $value, $user_id = false) { + $pref_name = db_escape_string($pref_name); + $value = db_escape_string($value); + + if (!$user_id) { + $user_id = $_SESSION["uid"]; + @$profile = $_SESSION["profile"]; + } else { + $user_id = sprintf("%d", $user_id); + $prefs_cache = false; + } + + if ($profile) { + $profile_qpart = "AND profile = '$profile'"; + } else { + $profile_qpart = "AND profile IS NULL"; + } + + if (get_schema_version($link) < 63) $profile_qpart = ""; + + $type_name = ""; + $current_value = ""; + + if (!defined('DISABLE_SESSIONS')) { + if ($_SESSION["prefs_cache"] && @$_SESSION["prefs_cache"][$pref_name]) { + $type_name = $_SESSION["prefs_cache"][$pref_name]["type"]; + $current_value = $_SESSION["prefs_cache"][$pref_name]["value"]; + } + } + + if (!$type_name) { + $result = db_query($link, "SELECT type_name + FROM ttrss_prefs,ttrss_prefs_types + WHERE pref_name = '$pref_name' AND type_id = ttrss_prefs_types.id"); + + if (db_num_rows($result) > 0) + $type_name = db_fetch_result($result, 0, "type_name"); + } else if ($current_value == $value) { + return; + } + + if ($type_name) { + if ($type_name == "bool") { + if ($value == "1" || $value == "true") { + $value = "true"; + } else { + $value = "false"; + } + } else if ($type_name == "integer") { + $value = sprintf("%d", $value); + } + + if ($pref_name == 'DEFAULT_ARTICLE_LIMIT' && $value == 0) { + $value = 30; + } + + if ($pref_name == 'USER_TIMEZONE' && $value == '') { + $value = 'UTC'; + } + + db_query($link, "UPDATE ttrss_user_prefs SET + value = '$value' WHERE pref_name = '$pref_name' + $profile_qpart + AND owner_uid = " . $_SESSION["uid"]); + + if (!defined('DISABLE_SESSIONS')) { + if ($user_id == $_SESSION["uid"]) { + $_SESSION["prefs_cache"][$pref_name]["type"] = $type_name; + $_SESSION["prefs_cache"][$pref_name]["value"] = $value; + } + } + } + } +?> diff --git a/include/db.php b/include/db.php new file mode 100644 index 000000000..0682b58f8 --- /dev/null +++ b/include/db.php @@ -0,0 +1,142 @@ +$query failed [$result]: " . pg_last_error($link)); + } + } + return $result; + } else if (DB_TYPE == "mysql") { + $result = mysql_query($query, $link); + if (!$result) { + $query = htmlspecialchars($query); + if ($die_on_error) { + die("Query $query failed: " . mysql_error($link)); + } + } + return $result; + } +} + +function db_fetch_assoc($result) { + if (DB_TYPE == "pgsql") { + return pg_fetch_assoc($result); + } else if (DB_TYPE == "mysql") { + return mysql_fetch_assoc($result); + } +} + + +function db_num_rows($result) { + if (DB_TYPE == "pgsql") { + return pg_num_rows($result); + } else if (DB_TYPE == "mysql") { + return mysql_num_rows($result); + } +} + +function db_fetch_result($result, $row, $param) { + if (DB_TYPE == "pgsql") { + return pg_fetch_result($result, $row, $param); + } else if (DB_TYPE == "mysql") { + // I hate incoherent naming of PHP functions + return mysql_result($result, $row, $param); + } +} + +function db_unescape_string($str) { + $tmp = str_replace("\\\"", "\"", $str); + $tmp = str_replace("\\'", "'", $tmp); + return $tmp; +} + +function db_close($link) { + if (DB_TYPE == "pgsql") { + + return pg_close($link); + + } else if (DB_TYPE == "mysql") { + return mysql_close($link); + } +} + +function db_affected_rows($link, $result) { + if (DB_TYPE == "pgsql") { + return pg_affected_rows($result); + } else if (DB_TYPE == "mysql") { + return mysql_affected_rows($link); + } +} + +function db_last_error($link) { + if (DB_TYPE == "pgsql") { + return pg_last_error($link); + } else if (DB_TYPE == "mysql") { + return mysql_error($link); + } +} + +function db_quote($str){ + return("'$str'"); +} + +?> diff --git a/include/functions.php b/include/functions.php new file mode 100644 index 000000000..8f46c295c --- /dev/null +++ b/include/functions.php @@ -0,0 +1,7647 @@ + "Detect automatically", + "ca_CA" => "Català", + "en_US" => "English", + "es_ES" => "Español", + "de_DE" => "Deutsch", + "fr_FR" => "Français", + "hu_HU" => "Magyar (Hungarian)", + "it_IT" => "Italiano", + "ja_JP" => "日本語 (Japanese)", + "nb_NO" => "Norwegian bokmål", + "ru_RU" => "Русский", + "pt_BR" => "Portuguese/Brazil", + "zh_CN" => "Simplified Chinese"); + + return $tr; + } + + require_once "lib/accept-to-gettext.php"; + require_once "lib/gettext/gettext.inc"; + + function startup_gettext() { + + # Get locale from Accept-Language header + $lang = al2gt(array_keys(get_translations()), "text/html"); + + if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) { + $lang = _TRANSLATION_OVERRIDE_DEFAULT; + } + + if ($_COOKIE["ttrss_lang"] && $_COOKIE["ttrss_lang"] != "auto") { + $lang = $_COOKIE["ttrss_lang"]; + } + + /* In login action of mobile version */ + if ($_POST["language"] && defined('MOBILE_VERSION')) { + $lang = $_POST["language"]; + $_COOKIE["ttrss_lang"] = $lang; + } + + if ($lang) { + if (defined('LC_MESSAGES')) { + _setlocale(LC_MESSAGES, $lang); + } else if (defined('LC_ALL')) { + _setlocale(LC_ALL, $lang); + } + + if (defined('MOBILE_VERSION')) { + _bindtextdomain("messages", "../locale"); + } else { + _bindtextdomain("messages", "locale"); + } + + _textdomain("messages"); + _bind_textdomain_codeset("messages", "UTF-8"); + } + } + + startup_gettext(); + + if (defined('MEMCACHE_SERVER')) { + $memcache = new Memcache; + $memcache->connect(MEMCACHE_SERVER, 11211); + } + + require_once 'db-prefs.php'; + require_once 'version.php'; + + define('MAGPIE_OUTPUT_ENCODING', 'UTF-8'); + + define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)'); + define('MAGPIE_USER_AGENT', SELF_USER_AGENT); + + ini_set('user_agent', SELF_USER_AGENT); + + require_once 'lib/pubsubhubbub/publisher.php'; + + $purifier = false; + + $tz_offset = -1; + $utc_tz = new DateTimeZone('UTC'); + $schema_version = false; + + /** + * Print a timestamped debug message. + * + * @param string $msg The debug message. + * @return void + */ + function _debug($msg) { + $ts = strftime("%H:%M:%S", time()); + if (function_exists('posix_getpid')) { + $ts = "$ts/" . posix_getpid(); + } + print "[$ts] $msg\n"; + } // function _debug + + /** + * Purge a feed old posts. + * + * @param mixed $link A database connection. + * @param mixed $feed_id The id of the purged feed. + * @param mixed $purge_interval Olderness of purged posts. + * @param boolean $debug Set to True to enable the debug. False by default. + * @access public + * @return void + */ + function purge_feed($link, $feed_id, $purge_interval, $debug = false) { + + if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id); + + $rows = -1; + + $result = db_query($link, + "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'"); + + $owner_uid = false; + + if (db_num_rows($result) == 1) { + $owner_uid = db_fetch_result($result, 0, "owner_uid"); + } + + if ($purge_interval == -1 || !$purge_interval) { + if ($owner_uid) { + ccache_update($link, $feed_id, $owner_uid); + } + return; + } + + if (!$owner_uid) return; + + if (FORCE_ARTICLE_PURGE == 0) { + $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES", + $owner_uid, false); + } else { + $purge_unread = true; + $purge_interval = FORCE_ARTICLE_PURGE; + } + + if (!$purge_unread) $query_limit = " unread = false AND "; + + if (DB_TYPE == "pgsql") { + $pg_version = get_pgsql_version($link); + + if (preg_match("/^7\./", $pg_version) || preg_match("/^8\.0/", $pg_version)) { + + $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE + ttrss_entries.id = ref_id AND + marked = false AND + feed_id = '$feed_id' AND + $query_limit + ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'"); + + } else { + + $result = db_query($link, "DELETE FROM ttrss_user_entries + USING ttrss_entries + WHERE ttrss_entries.id = ref_id AND + marked = false AND + feed_id = '$feed_id' AND + $query_limit + ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'"); + } + + $rows = pg_affected_rows($result); + + } else { + +/* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE + marked = false AND feed_id = '$feed_id' AND + (SELECT date_updated FROM ttrss_entries WHERE + id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */ + + $result = db_query($link, "DELETE FROM ttrss_user_entries + USING ttrss_user_entries, ttrss_entries + WHERE ttrss_entries.id = ref_id AND + marked = false AND + feed_id = '$feed_id' AND + $query_limit + ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); + + $rows = mysql_affected_rows($link); + + } + + ccache_update($link, $feed_id, $owner_uid); + + if ($debug) { + _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles"); + } + } // function purge_feed + + function feed_purge_interval($link, $feed_id) { + + $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds + WHERE id = '$feed_id'"); + + if (db_num_rows($result) == 1) { + $purge_interval = db_fetch_result($result, 0, "purge_interval"); + $owner_uid = db_fetch_result($result, 0, "owner_uid"); + + if ($purge_interval == 0) $purge_interval = get_pref($link, + 'PURGE_OLD_DAYS', $owner_uid); + + return $purge_interval; + + } else { + return -1; + } + } + + function purge_orphans($link, $do_output = false) { + + // purge orphaned posts in main content table + $result = db_query($link, "DELETE FROM ttrss_entries WHERE + (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0"); + + if ($do_output) { + $rows = db_affected_rows($link, $result); + _debug("Purged $rows orphaned posts."); + } + } + + function get_feed_update_interval($link, $feed_id) { + $result = db_query($link, "SELECT owner_uid, update_interval FROM + ttrss_feeds WHERE id = '$feed_id'"); + + if (db_num_rows($result) == 1) { + $update_interval = db_fetch_result($result, 0, "update_interval"); + $owner_uid = db_fetch_result($result, 0, "owner_uid"); + + if ($update_interval != 0) { + return $update_interval; + } else { + return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false); + } + + } else { + return -1; + } + } + + function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false) { + $login = urlencode($login); + $pass = urlencode($pass); + + if (function_exists('curl_init') && !ini_get("open_basedir")) { + $ch = curl_init($url); + + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + curl_setopt($ch, CURLOPT_TIMEOUT, 45); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, 20); + curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT); + curl_setopt($ch, CURLOPT_ENCODING , "gzip"); + + if ($post_query) { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query); + } + + if ($login && $pass) + curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass"); + + $contents = @curl_exec($ch); + + if ($contents === false) { + curl_close($ch); + return false; + } + + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + curl_close($ch); + + if ($http_code != 200 || $type && strpos($content_type, "$type") === false) { + return false; + } + + return $contents; + } else { + if ($login && $pass ){ + $url_parts = array(); + + preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts); + + if ($url_parts[1] && $url_parts[2]) { + $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2]; + } + } + + return @file_get_contents($url); + } + + } + + /** + * Try to determine the favicon URL for a feed. + * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/) + * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php + * + * @param string $url A feed or page URL + * @access public + * @return mixed The favicon URL, or false if none was found. + */ + function get_favicon_url($url) { + + $favicon_url = false; + + if ($html = @fetch_file_contents($url)) { + + libxml_use_internal_errors(true); + + $doc = new DOMDocument(); + $doc->loadHTML($html); + $xpath = new DOMXPath($doc); + + $base = $xpath->query('/html/head/base'); + foreach ($base as $b) { + $url = $b->getAttribute("href"); + break; + } + + $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]'); + if (count($entries) > 0) { + foreach ($entries as $entry) { + $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href")); + break; + } + } + } + + if (!$favicon_url) + $favicon_url = rewrite_relative_url($url, "/favicon.ico"); + + return $favicon_url; + } // function get_favicon_url + + function check_feed_favicon($site_url, $feed, $link) { +# print "FAVICON [$site_url]: $favicon_url\n"; + + $icon_file = ICONS_DIR . "/$feed.ico"; + + if (!file_exists($icon_file)) { + $favicon_url = get_favicon_url($site_url); + + if ($favicon_url) { + $contents = fetch_file_contents($favicon_url, "image"); + + if ($contents) { + $fp = @fopen($icon_file, "w"); + + if ($fp) { + fwrite($fp, $contents); + fclose($fp); + chmod($icon_file, 0644); + } + } + } + } + } + + function update_rss_feed($link, $feed, $ignore_daemon = false, $no_cache = false) { + + global $memcache; + + /* Update all feeds with the same URL to utilize memcache */ + + if ($memcache) { + $result = db_query($link, "SELECT f1.id + FROM ttrss_feeds AS f1, ttrss_feeds AS f2 + WHERE f2.feed_url = f1.feed_url AND f2.id = '$feed'"); + + while ($line = db_fetch_assoc($result)) { + update_rss_feed_real($link, $line["id"], $ignore_daemon, $no_cache); + } + } else { + update_rss_feed_real($link, $feed, $ignore_daemon, $no_cache); + } + } + + function update_rss_feed_real($link, $feed, $ignore_daemon = false, $no_cache = false, + $override_url = false) { + + require_once "lib/simplepie/simplepie.inc"; + require_once "lib/magpierss/rss_fetch.inc"; + require_once 'lib/magpierss/rss_utils.inc'; + + global $memcache; + + $debug_enabled = defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']; + + if (!$_REQUEST["daemon"] && !$ignore_daemon) { + return false; + } + + if ($debug_enabled) { + _debug("update_rss_feed: start"); + } + + if (!$ignore_daemon) { + + if (DB_TYPE == "pgsql") { + $updstart_thresh_qpart = "(ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '120 seconds')"; + } else { + $updstart_thresh_qpart = "(ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 120 SECOND))"; + } + + $result = db_query($link, "SELECT id,update_interval,auth_login, + auth_pass,cache_images,update_method + FROM ttrss_feeds WHERE id = '$feed' AND $updstart_thresh_qpart"); + + } else { + + $result = db_query($link, "SELECT id,update_interval,auth_login, + feed_url,auth_pass,cache_images,update_method,last_updated, + mark_unread_on_update, owner_uid, update_on_checksum_change, + pubsub_state + FROM ttrss_feeds WHERE id = '$feed'"); + + } + + if (db_num_rows($result) == 0) { + if ($debug_enabled) { + _debug("update_rss_feed: feed $feed NOT FOUND/SKIPPED"); + } + return false; + } + + $update_method = db_fetch_result($result, 0, "update_method"); + $last_updated = db_fetch_result($result, 0, "last_updated"); + $owner_uid = db_fetch_result($result, 0, "owner_uid"); + $mark_unread_on_update = sql_bool_to_bool(db_fetch_result($result, + 0, "mark_unread_on_update")); + $update_on_checksum_change = sql_bool_to_bool(db_fetch_result($result, + 0, "update_on_checksum_change")); + $pubsub_state = db_fetch_result($result, 0, "pubsub_state"); + + db_query($link, "UPDATE ttrss_feeds SET last_update_started = NOW() + WHERE id = '$feed'"); + + $auth_login = db_fetch_result($result, 0, "auth_login"); + $auth_pass = db_fetch_result($result, 0, "auth_pass"); + + if ($update_method == 0) + $update_method = DEFAULT_UPDATE_METHOD + 1; + + // 1 - Magpie + // 2 - SimplePie + // 3 - Twitter OAuth + + if ($update_method == 2) + $use_simplepie = true; + else + $use_simplepie = false; + + if ($debug_enabled) { + _debug("update method: $update_method (feed setting: $update_method) (use simplepie: $use_simplepie)\n"); + } + + if ($update_method == 1) { + $auth_login = urlencode($auth_login); + $auth_pass = urlencode($auth_pass); + } + + $update_interval = db_fetch_result($result, 0, "update_interval"); + $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images")); + $fetch_url = db_fetch_result($result, 0, "feed_url"); + + if ($update_interval < 0) { return false; } + + $feed = db_escape_string($feed); + + if ($auth_login && $auth_pass ){ + $url_parts = array(); + preg_match("/(^[^:]*):\/\/(.*)/", $fetch_url, $url_parts); + + if ($url_parts[1] && $url_parts[2]) { + $fetch_url = $url_parts[1] . "://$auth_login:$auth_pass@" . $url_parts[2]; + } + + } + + if ($override_url) + $fetch_url = $override_url; + + if ($debug_enabled) { + _debug("update_rss_feed: fetching [$fetch_url]..."); + } + + $obj_id = md5("FDATA:$use_simplepie:$fetch_url"); + + if ($memcache && $obj = $memcache->get($obj_id)) { + + if ($debug_enabled) { + _debug("update_rss_feed: data found in memcache."); + } + + $rss = $obj; + + } else { + + if ($update_method == 3) { + $rss = fetch_twitter_rss($link, $fetch_url, $owner_uid); + } else if ($update_method == 1) { + + define('MAGPIE_CACHE_AGE', get_feed_update_interval($link, $feed) * 60); + define('MAGPIE_CACHE_ON', !$no_cache); + define('MAGPIE_FETCH_TIME_OUT', 60); + define('MAGPIE_CACHE_DIR', CACHE_DIR . "/magpie"); + + $rss = @fetch_rss($fetch_url); + } else { + $simplepie_cache_dir = CACHE_DIR . "/simplepie"; + + if (!is_dir($simplepie_cache_dir)) { + mkdir($simplepie_cache_dir); + } + + $rss = new SimplePie(); + $rss->set_useragent(SELF_USER_AGENT); + # $rss->set_timeout(10); + $rss->set_feed_url($fetch_url); + $rss->set_output_encoding('UTF-8'); + $rss->force_feed(true); + + if (SIMPLEPIE_CACHE_IMAGES && $cache_images) { + + if ($debug_enabled) { + _debug("enabling image cache"); + } + + $rss->set_image_handler("image.php", 'i'); + } + + if ($debug_enabled) { + _debug("feed update interval (sec): " . + get_feed_update_interval($link, $feed)*60); + } + + $rss->enable_cache(!$no_cache); + + if (!$no_cache) { + $rss->set_cache_location($simplepie_cache_dir); + $rss->set_cache_duration(get_feed_update_interval($link, $feed) * 60); + } + + $rss->init(); + } + + if ($memcache && $rss) $memcache->add($obj_id, $rss, 0, 300); + } + +// print_r($rss); + + if ($debug_enabled) { + _debug("update_rss_feed: fetch done, parsing..."); + } + + $feed = db_escape_string($feed); + + if ($update_method == 2) { + $fetch_ok = !$rss->error(); + } else { + $fetch_ok = !!$rss; + } + + if ($fetch_ok) { + + if ($debug_enabled) { + _debug("update_rss_feed: processing feed data..."); + } + +// db_query($link, "BEGIN"); + + $result = db_query($link, "SELECT title,icon_url,site_url,owner_uid + FROM ttrss_feeds WHERE id = '$feed'"); + + $registered_title = db_fetch_result($result, 0, "title"); + $orig_icon_url = db_fetch_result($result, 0, "icon_url"); + $orig_site_url = db_fetch_result($result, 0, "site_url"); + + $owner_uid = db_fetch_result($result, 0, "owner_uid"); + + if ($use_simplepie) { + $site_url = $rss->get_link(); + } else { + $site_url = $rss->channel["link"]; + } + + $site_url = rewrite_relative_url($fetch_url, $site_url); + + if ($debug_enabled) { + _debug("update_rss_feed: checking favicon..."); + } + + check_feed_favicon($site_url, $feed, $link); + + if (!$registered_title || $registered_title == "[Unknown]") { + + if ($use_simplepie) { + $feed_title = db_escape_string($rss->get_title()); + } else { + $feed_title = db_escape_string($rss->channel["title"]); + } + + if ($debug_enabled) { + _debug("update_rss_feed: registering title: $feed_title"); + } + + db_query($link, "UPDATE ttrss_feeds SET + title = '$feed_title' WHERE id = '$feed'"); + } + + // weird, weird Magpie + if (!$use_simplepie) { + if (!$site_url) $site_url = db_escape_string($rss->channel["link_"]); + } + + if ($site_url && $orig_site_url != db_escape_string($site_url)) { + db_query($link, "UPDATE ttrss_feeds SET + site_url = '$site_url' WHERE id = '$feed'"); + } + +// print "I: " . $rss->channel["image"]["url"]; + + if (!$use_simplepie) { + $icon_url = db_escape_string($rss->image["url"]); + } else { + $icon_url = db_escape_string($rss->get_image_url()); + } + + $icon_url = substr($icon_url, 0, 250); + + if ($icon_url && $orig_icon_url != $icon_url) { + db_query($link, "UPDATE ttrss_feeds SET icon_url = '$icon_url' WHERE id = '$feed'"); + } + + if ($debug_enabled) { + _debug("update_rss_feed: loading filters..."); + } + + $filters = load_filters($link, $feed, $owner_uid); + +// if ($debug_enabled) { +// print_r($filters); +// } + + if ($use_simplepie) { + $iterator = $rss->get_items(); + } else { + $iterator = $rss->items; + if (!$iterator || !is_array($iterator)) $iterator = $rss->entries; + if (!$iterator || !is_array($iterator)) $iterator = $rss; + } + + if (!is_array($iterator)) { + /* db_query($link, "UPDATE ttrss_feeds + SET last_error = 'Parse error: can\'t find any articles.' + WHERE id = '$feed'"); */ + + // clear any errors and mark feed as updated if fetched okay + // even if it's blank + + if ($debug_enabled) { + _debug("update_rss_feed: entry iterator is not an array, no articles?"); + } + + db_query($link, "UPDATE ttrss_feeds + SET last_updated = NOW(), last_error = '' WHERE id = '$feed'"); + + return; // no articles + } + + if ($pubsub_state != 2 && PUBSUBHUBBUB_ENABLED) { + + if ($debug_enabled) _debug("update_rss_feed: checking for PUSH hub..."); + + $feed_hub_url = false; + if ($use_simplepie) { + $links = $rss->get_links('hub'); + + if ($links && is_array($links)) { + foreach ($links as $l) { + $feed_hub_url = $l; + break; + } + } + + } else { + $atom = $rss->channel['atom']; + + if ($atom) { + if ($atom['link@rel'] == 'hub') { + $feed_hub_url = $atom['link@href']; + } + + if (!$feed_hub_url && $atom['link#'] > 1) { + for ($i = 2; $i <= $atom['link#']; $i++) { + if ($atom["link#$i@rel"] == 'hub') { + $feed_hub_url = $atom["link#$i@href"]; + break; + } + } + } + } else { + $feed_hub_url = $rss->channel['link_hub']; + } + } + + if ($debug_enabled) _debug("update_rss_feed: feed hub url: $feed_hub_url"); + + if ($feed_hub_url && function_exists('curl_init') && + !ini_get("open_basedir")) { + + require_once 'lib/pubsubhubbub/subscriber.php'; + + $callback_url = get_self_url_prefix() . + "/public.php?op=pubsub&id=$feed"; + + $s = new Subscriber($feed_hub_url, $callback_url); + + $rc = $s->subscribe($fetch_url); + + if ($debug_enabled) + _debug("update_rss_feed: feed hub url found, subscribe request sent."); + + db_query($link, "UPDATE ttrss_feeds SET pubsub_state = 1 + WHERE id = '$feed'"); + } + } + + if ($debug_enabled) { + _debug("update_rss_feed: processing articles..."); + } + + foreach ($iterator as $item) { + + if ($_REQUEST['xdebug'] == 2) { + print_r($item); + } + + if ($use_simplepie) { + $entry_guid = $item->get_id(); + if (!$entry_guid) $entry_guid = $item->get_link(); + if (!$entry_guid) $entry_guid = make_guid_from_title($item->get_title()); + + } else { + + $entry_guid = $item["id"]; + + if (!$entry_guid) $entry_guid = $item["guid"]; + if (!$entry_guid) $entry_guid = $item["about"]; + if (!$entry_guid) $entry_guid = $item["link"]; + if (!$entry_guid) $entry_guid = make_guid_from_title($item["title"]); + } + + if ($debug_enabled) { + _debug("update_rss_feed: guid $entry_guid"); + } + + if (!$entry_guid) continue; + + $entry_timestamp = ""; + + if ($use_simplepie) { + $entry_timestamp = strtotime($item->get_date()); + } else { + $rss_2_date = $item['pubdate']; + $rss_1_date = $item['dc']['date']; + $atom_date = $item['issued']; + if (!$atom_date) $atom_date = $item['updated']; + + if ($atom_date != "") $entry_timestamp = parse_w3cdtf($atom_date); + if ($rss_1_date != "") $entry_timestamp = parse_w3cdtf($rss_1_date); + if ($rss_2_date != "") $entry_timestamp = strtotime($rss_2_date); + + } + + if ($entry_timestamp == "" || $entry_timestamp == -1 || !$entry_timestamp) { + $entry_timestamp = time(); + $no_orig_date = 'true'; + } else { + $no_orig_date = 'false'; + } + + $entry_timestamp_fmt = strftime("%Y/%m/%d %H:%M:%S", $entry_timestamp); + + if ($debug_enabled) { + _debug("update_rss_feed: date $entry_timestamp [$entry_timestamp_fmt]"); + } + + if ($use_simplepie) { + $entry_title = $item->get_title(); + } else { + $entry_title = trim(strip_tags($item["title"])); + } + + if ($use_simplepie) { + $entry_link = $item->get_link(); + } else { + // strange Magpie workaround + $entry_link = $item["link_"]; + if (!$entry_link) $entry_link = $item["link"]; + } + + $entry_link = rewrite_relative_url($site_url, $entry_link); + + if ($debug_enabled) { + _debug("update_rss_feed: title $entry_title"); + _debug("update_rss_feed: link $entry_link"); + } + + if (!$entry_title) $entry_title = date("Y-m-d H:i:s", $entry_timestamp);; + + $entry_link = strip_tags($entry_link); + + if ($use_simplepie) { + $entry_content = $item->get_content(); + if (!$entry_content) $entry_content = $item->get_description(); + } else { + $entry_content = $item["content:escaped"]; + + if (!$entry_content) $entry_content = $item["content:encoded"]; + if (!$entry_content) $entry_content = $item["content"]["encoded"]; + if (!$entry_content) $entry_content = $item["content"]; + + if (is_array($entry_content)) $entry_content = $entry_content[0]; + + // Magpie bugs are getting ridiculous + if (trim($entry_content) == "Array") $entry_content = false; + + if (!$entry_content) $entry_content = $item["atom_content"]; + if (!$entry_content) $entry_content = $item["summary"]; + + if (!$entry_content || + strlen($entry_content) < strlen($item["description"])) { + $entry_content = $item["description"]; + }; + + // WTF + if (is_array($entry_content)) { + $entry_content = $entry_content["encoded"]; + if (!$entry_content) $entry_content = $entry_content["escaped"]; + } + } + + if ($_REQUEST["xdebug"] == 2) { + print "update_rss_feed: content: "; + print_r(htmlspecialchars($entry_content)); + } + + $entry_content_unescaped = $entry_content; + + if ($use_simplepie) { + $entry_comments = strip_tags($item->data["comments"]); + if ($item->get_author()) { + $entry_author_item = $item->get_author(); + $entry_author = $entry_author_item->get_name(); + if (!$entry_author) $entry_author = $entry_author_item->get_email(); + + $entry_author = db_escape_string($entry_author); + } + } else { + $entry_comments = strip_tags($item["comments"]); + + $entry_author = db_escape_string(strip_tags($item['dc']['creator'])); + + if ($item['author']) { + + if (is_array($item['author'])) { + + if (!$entry_author) { + $entry_author = db_escape_string(strip_tags($item['author']['name'])); + } + + if (!$entry_author) { + $entry_author = db_escape_string(strip_tags($item['author']['email'])); + } + } + + if (!$entry_author) { + $entry_author = db_escape_string(strip_tags($item['author'])); + } + } + } + + if (preg_match('/^[\t\n\r ]*$/', $entry_author)) $entry_author = ''; + + $entry_guid = db_escape_string(strip_tags($entry_guid)); + $entry_guid = mb_substr($entry_guid, 0, 250); + + $result = db_query($link, "SELECT id FROM ttrss_entries + WHERE guid = '$entry_guid'"); + + $entry_content = db_escape_string($entry_content, false); + + $content_hash = "SHA1:" . sha1(strip_tags($entry_content)); + + $entry_title = db_escape_string($entry_title); + $entry_link = db_escape_string($entry_link); + $entry_comments = mb_substr(db_escape_string($entry_comments), 0, 250); + $entry_author = mb_substr($entry_author, 0, 250); + + if ($use_simplepie) { + $num_comments = 0; #FIXME# + } else { + $num_comments = db_escape_string($item["slash"]["comments"]); + } + + if (!$num_comments) $num_comments = 0; + + if ($debug_enabled) { + _debug("update_rss_feed: looking for tags [1]..."); + } + + // parse entries into tags + + $additional_tags = array(); + + if ($use_simplepie) { + + $additional_tags_src = $item->get_categories(); + + if (is_array($additional_tags_src)) { + foreach ($additional_tags_src as $tobj) { + array_push($additional_tags, $tobj->get_term()); + } + } + + if ($debug_enabled) { + _debug("update_rss_feed: category tags:"); + print_r($additional_tags); + } + + } else { + + $t_ctr = $item['category#']; + + if ($t_ctr == 0) { + $additional_tags = array(); + } else if ($t_ctr > 0) { + $additional_tags = array($item['category']); + + if ($item['category@term']) { + array_push($additional_tags, $item['category@term']); + } + + for ($i = 0; $i <= $t_ctr; $i++ ) { + if ($item["category#$i"]) { + array_push($additional_tags, $item["category#$i"]); + } + + if ($item["category#$i@term"]) { + array_push($additional_tags, $item["category#$i@term"]); + } + } + } + + // parse elements + + $t_ctr = $item['dc']['subject#']; + + if ($t_ctr > 0) { + array_push($additional_tags, $item['dc']['subject']); + + for ($i = 0; $i <= $t_ctr; $i++ ) { + if ($item['dc']["subject#$i"]) { + array_push($additional_tags, $item['dc']["subject#$i"]); + } + } + } + } + + if ($debug_enabled) { + _debug("update_rss_feed: looking for tags [2]..."); + } + + /* taaaags */ + // , // + + $entry_tags = null; + + preg_match_all("/([^<]+)<\/a>/i", + $entry_content_unescaped, $entry_tags); + + $entry_tags = $entry_tags[1]; + + $entry_tags = array_merge($entry_tags, $additional_tags); + $entry_tags = array_unique($entry_tags); + + for ($i = 0; $i < count($entry_tags); $i++) + $entry_tags[$i] = mb_strtolower($entry_tags[$i], 'utf-8'); + + if ($debug_enabled) { + _debug("update_rss_feed: unfiltered tags found:"); + print_r($entry_tags); + } + + # sanitize content + + $entry_content = sanitize_article_content($entry_content); + $entry_title = sanitize_article_content($entry_title); + + if ($debug_enabled) { + _debug("update_rss_feed: done collecting data [TITLE:$entry_title]"); + } + + db_query($link, "BEGIN"); + + if (db_num_rows($result) == 0) { + + if ($debug_enabled) { + _debug("update_rss_feed: base guid not found"); + } + + // base post entry does not exist, create it + + $result = db_query($link, + "INSERT INTO ttrss_entries + (title, + guid, + link, + updated, + content, + content_hash, + no_orig_date, + date_updated, + date_entered, + comments, + num_comments, + author) + VALUES + ('$entry_title', + '$entry_guid', + '$entry_link', + '$entry_timestamp_fmt', + '$entry_content', + '$content_hash', + $no_orig_date, + NOW(), + NOW(), + '$entry_comments', + '$num_comments', + '$entry_author')"); + } else { + // we keep encountering the entry in feeds, so we need to + // update date_updated column so that we don't get horrible + // dupes when the entry gets purged and reinserted again e.g. + // in the case of SLOW SLOW OMG SLOW updating feeds + + $base_entry_id = db_fetch_result($result, 0, "id"); + + db_query($link, "UPDATE ttrss_entries SET date_updated = NOW() + WHERE id = '$base_entry_id'"); + } + + // now it should exist, if not - bad luck then + + $result = db_query($link, "SELECT + id,content_hash,no_orig_date,title, + ".SUBSTRING_FOR_DATE."(date_updated,1,19) as date_updated, + ".SUBSTRING_FOR_DATE."(updated,1,19) as updated, + num_comments + FROM + ttrss_entries + WHERE guid = '$entry_guid'"); + + $entry_ref_id = 0; + $entry_int_id = 0; + + if (db_num_rows($result) == 1) { + + if ($debug_enabled) { + _debug("update_rss_feed: base guid found, checking for user record"); + } + + // this will be used below in update handler + $orig_content_hash = db_fetch_result($result, 0, "content_hash"); + $orig_title = db_fetch_result($result, 0, "title"); + $orig_num_comments = db_fetch_result($result, 0, "num_comments"); + $orig_date_updated = strtotime(db_fetch_result($result, + 0, "date_updated")); + + $ref_id = db_fetch_result($result, 0, "id"); + $entry_ref_id = $ref_id; + + // check for user post link to main table + + // do we allow duplicate posts with same GUID in different feeds? + if (get_pref($link, "ALLOW_DUPLICATE_POSTS", $owner_uid, false)) { + $dupcheck_qpart = "AND (feed_id = '$feed' OR feed_id IS NULL)"; + } else { + $dupcheck_qpart = ""; + } + + /* Collect article tags here so we could filter by them: */ + + $article_filters = get_article_filters($filters, $entry_title, + $entry_content, $entry_link, $entry_timestamp, $entry_author, + $entry_tags); + + if ($debug_enabled) { + _debug("update_rss_feed: article filters: "); + if (count($article_filters) != 0) { + print_r($article_filters); + } + } + + if (find_article_filter($article_filters, "filter")) { + db_query($link, "COMMIT"); // close transaction in progress + continue; + } + + $score = calculate_article_score($article_filters); + + if ($debug_enabled) { + _debug("update_rss_feed: initial score: $score"); + } + + $query = "SELECT ref_id, int_id FROM ttrss_user_entries WHERE + ref_id = '$ref_id' AND owner_uid = '$owner_uid' + $dupcheck_qpart"; + +// if ($_REQUEST["xdebug"]) print "$query\n"; + + $result = db_query($link, $query); + + // okay it doesn't exist - create user entry + if (db_num_rows($result) == 0) { + + if ($debug_enabled) { + _debug("update_rss_feed: user record not found, creating..."); + } + + if ($score >= -500 && !find_article_filter($article_filters, 'catchup')) { + $unread = 'true'; + $last_read_qpart = 'NULL'; + } else { + $unread = 'false'; + $last_read_qpart = 'NOW()'; + } + + if (find_article_filter($article_filters, 'mark') || $score > 1000) { + $marked = 'true'; + } else { + $marked = 'false'; + } + + if (find_article_filter($article_filters, 'publish')) { + $published = 'true'; + } else { + $published = 'false'; + } + + $result = db_query($link, + "INSERT INTO ttrss_user_entries + (ref_id, owner_uid, feed_id, unread, last_read, marked, + published, score, tag_cache, label_cache, uuid) + VALUES ('$ref_id', '$owner_uid', '$feed', $unread, + $last_read_qpart, $marked, $published, '$score', '', '', '')"); + + if (PUBSUBHUBBUB_HUB && $published == 'true') { + $rss_link = get_self_url_prefix() . + "/public.php?op=rss&id=-2&key=" . + get_feed_access_key($link, -2, false, $owner_uid); + + $p = new Publisher(PUBSUBHUBBUB_HUB); + + $pubsub_result = $p->publish_update($rss_link); + } + + $result = db_query($link, + "SELECT int_id FROM ttrss_user_entries WHERE + ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND + feed_id = '$feed' LIMIT 1"); + + if (db_num_rows($result) == 1) { + $entry_int_id = db_fetch_result($result, 0, "int_id"); + } + } else { + if ($debug_enabled) { + _debug("update_rss_feed: user record FOUND"); + } + + $entry_ref_id = db_fetch_result($result, 0, "ref_id"); + $entry_int_id = db_fetch_result($result, 0, "int_id"); + } + + if ($debug_enabled) { + _debug("update_rss_feed: RID: $entry_ref_id, IID: $entry_int_id"); + } + + $post_needs_update = false; + $update_insignificant = false; + + if ($orig_num_comments != $num_comments) { + $post_needs_update = true; + $update_insignificant = true; + } + + if ($content_hash != $orig_content_hash) { + $post_needs_update = true; + $update_insignificant = false; + } + + if (db_escape_string($orig_title) != $entry_title) { + $post_needs_update = true; + $update_insignificant = false; + } + + // if post needs update, update it and mark all user entries + // linking to this post as updated + if ($post_needs_update) { + + if (defined('DAEMON_EXTENDED_DEBUG')) { + _debug("update_rss_feed: post $entry_guid needs update..."); + } + +// print ""; + + db_query($link, "UPDATE ttrss_entries + SET title = '$entry_title', content = '$entry_content', + content_hash = '$content_hash', + updated = '$entry_timestamp_fmt', + num_comments = '$num_comments' + WHERE id = '$ref_id'"); + + if (!$update_insignificant) { + if ($mark_unread_on_update) { + db_query($link, "UPDATE ttrss_user_entries + SET last_read = null, unread = true WHERE ref_id = '$ref_id'"); + } else if ($update_on_checksum_change) { + db_query($link, "UPDATE ttrss_user_entries + SET last_read = null WHERE ref_id = '$ref_id' + AND unread = false"); + } + } + } + } + + db_query($link, "COMMIT"); + + if ($debug_enabled) { + _debug("update_rss_feed: assigning labels..."); + } + + assign_article_to_labels($link, $entry_ref_id, $article_filters, + $owner_uid); + + if ($debug_enabled) { + _debug("update_rss_feed: looking for enclosures..."); + } + + // enclosures + + $enclosures = array(); + + if ($use_simplepie) { + $encs = $item->get_enclosures(); + + if (is_array($encs)) { + foreach ($encs as $e) { + $e_item = array( + $e->link, $e->type, $e->length); + + array_push($enclosures, $e_item); + } + } + + } else { + // + + $e_ctr = $item['enclosure#']; + + if ($e_ctr > 0) { + $e_item = array($item['enclosure@url'], + $item['enclosure@type'], + $item['enclosure@length']); + + array_push($enclosures, $e_item); + + for ($i = 0; $i <= $e_ctr; $i++ ) { + + if ($item["enclosure#$i@url"]) { + $e_item = array($item["enclosure#$i@url"], + $item["enclosure#$i@type"], + $item["enclosure#$i@length"]); + array_push($enclosures, $e_item); + } + } + } + + // + // can there be many of those? yes -fox + + $m_ctr = $item['media']['content#']; + + if ($m_ctr > 0) { + $e_item = array($item['media']['content@url'], + $item['media']['content@medium'], + $item['media']['content@length']); + + array_push($enclosures, $e_item); + + for ($i = 0; $i <= $m_ctr; $i++ ) { + + if ($item["media"]["content#$i@url"]) { + $e_item = array($item["media"]["content#$i@url"], + $item["media"]["content#$i@medium"], + $item["media"]["content#$i@length"]); + array_push($enclosures, $e_item); + } + } + + } + } + + + if ($debug_enabled) { + _debug("update_rss_feed: article enclosures:"); + print_r($enclosures); + } + + db_query($link, "BEGIN"); + + foreach ($enclosures as $enc) { + $enc_url = db_escape_string($enc[0]); + $enc_type = db_escape_string($enc[1]); + $enc_dur = db_escape_string($enc[2]); + + $result = db_query($link, "SELECT id FROM ttrss_enclosures + WHERE content_url = '$enc_url' AND post_id = '$entry_ref_id'"); + + if (db_num_rows($result) == 0) { + db_query($link, "INSERT INTO ttrss_enclosures + (content_url, content_type, title, duration, post_id) VALUES + ('$enc_url', '$enc_type', '', '$enc_dur', '$entry_ref_id')"); + } + } + + db_query($link, "COMMIT"); + + // check for manual tags (we have to do it here since they're loaded from filters) + + foreach ($article_filters as $f) { + if ($f[0] == "tag") { + + $manual_tags = trim_array(explode(",", $f[1])); + + foreach ($manual_tags as $tag) { + if (tag_is_valid($tag)) { + array_push($entry_tags, $tag); + } + } + } + } + + // Skip boring tags + + $boring_tags = trim_array(explode(",", mb_strtolower(get_pref($link, + 'BLACKLISTED_TAGS', $owner_uid, ''), 'utf-8'))); + + $filtered_tags = array(); + $tags_to_cache = array(); + + if ($entry_tags && is_array($entry_tags)) { + foreach ($entry_tags as $tag) { + if (array_search($tag, $boring_tags) === false) { + array_push($filtered_tags, $tag); + } + } + } + + $filtered_tags = array_unique($filtered_tags); + + if ($debug_enabled) { + _debug("update_rss_feed: filtered article tags:"); + print_r($filtered_tags); + } + + // Save article tags in the database + + if (count($filtered_tags) > 0) { + + db_query($link, "BEGIN"); + + foreach ($filtered_tags as $tag) { + + $tag = sanitize_tag($tag); + $tag = db_escape_string($tag); + + if (!tag_is_valid($tag)) continue; + + $result = db_query($link, "SELECT id FROM ttrss_tags + WHERE tag_name = '$tag' AND post_int_id = '$entry_int_id' AND + owner_uid = '$owner_uid' LIMIT 1"); + + if ($result && db_num_rows($result) == 0) { + + db_query($link, "INSERT INTO ttrss_tags + (owner_uid,tag_name,post_int_id) + VALUES ('$owner_uid','$tag', '$entry_int_id')"); + } + + array_push($tags_to_cache, $tag); + } + + /* update the cache */ + + $tags_to_cache = array_unique($tags_to_cache); + + $tags_str = db_escape_string(join(",", $tags_to_cache)); + + db_query($link, "UPDATE ttrss_user_entries + SET tag_cache = '$tags_str' WHERE ref_id = '$entry_ref_id' + AND owner_uid = $owner_uid"); + + db_query($link, "COMMIT"); + } + + if ($debug_enabled) { + _debug("update_rss_feed: article processed"); + } + } + + if (!$last_updated) { + if ($debug_enabled) { + _debug("update_rss_feed: new feed, catching it up..."); + } + catchup_feed($link, $feed, false, $owner_uid); + } + + if ($debug_enabled) { + _debug("purging feed..."); + } + + purge_feed($link, $feed, 0, $debug_enabled); + + db_query($link, "UPDATE ttrss_feeds + SET last_updated = NOW(), last_error = '' WHERE id = '$feed'"); + +// db_query($link, "COMMIT"); + + } else { + + if ($use_simplepie) { + $error_msg = mb_substr($rss->error(), 0, 250); + } else { + $error_msg = mb_substr(magpie_error(), 0, 250); + } + + if ($debug_enabled) { + _debug("update_rss_feed: error fetching feed: $error_msg"); + } + + $error_msg = db_escape_string($error_msg); + + db_query($link, + "UPDATE ttrss_feeds SET last_error = '$error_msg', + last_updated = NOW() WHERE id = '$feed'"); + } + + if ($use_simplepie) { + unset($rss); + } + + if ($debug_enabled) { + _debug("update_rss_feed: done"); + } + + } + + function print_select($id, $default, $values, $attributes = "") { + print ""; + } + + function print_select_hash($id, $default, $values, $attributes = "") { + print ""; + } + + function get_article_filters($filters, $title, $content, $link, $timestamp, $author, $tags) { + $matches = array(); + + if ($filters["title"]) { + foreach ($filters["title"] as $filter) { + $reg_exp = $filter["reg_exp"]; + $inverse = $filter["inverse"]; + if ((!$inverse && @preg_match("/$reg_exp/i", $title)) || + ($inverse && !@preg_match("/$reg_exp/i", $title))) { + + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + + if ($filters["content"]) { + foreach ($filters["content"] as $filter) { + $reg_exp = $filter["reg_exp"]; + $inverse = $filter["inverse"]; + + if ((!$inverse && @preg_match("/$reg_exp/i", $content)) || + ($inverse && !@preg_match("/$reg_exp/i", $content))) { + + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + + if ($filters["both"]) { + foreach ($filters["both"] as $filter) { + $reg_exp = $filter["reg_exp"]; + $inverse = $filter["inverse"]; + + if ($inverse) { + if (!@preg_match("/$reg_exp/i", $title) && !preg_match("/$reg_exp/i", $content)) { + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } else { + if (@preg_match("/$reg_exp/i", $title) || preg_match("/$reg_exp/i", $content)) { + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + } + + if ($filters["link"]) { + $reg_exp = $filter["reg_exp"]; + foreach ($filters["link"] as $filter) { + $reg_exp = $filter["reg_exp"]; + $inverse = $filter["inverse"]; + + if ((!$inverse && @preg_match("/$reg_exp/i", $link)) || + ($inverse && !@preg_match("/$reg_exp/i", $link))) { + + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + + if ($filters["date"]) { + $reg_exp = $filter["reg_exp"]; + foreach ($filters["date"] as $filter) { + $date_modifier = $filter["filter_param"]; + $inverse = $filter["inverse"]; + $check_timestamp = strtotime($filter["reg_exp"]); + + # no-op when timestamp doesn't parse to prevent misfires + + if ($check_timestamp) { + $match_ok = false; + + if ($date_modifier == "before" && $timestamp < $check_timestamp || + $date_modifier == "after" && $timestamp > $check_timestamp) { + $match_ok = true; + } + + if ($inverse) $match_ok = !$match_ok; + + if ($match_ok) { + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + } + + if ($filters["author"]) { + foreach ($filters["author"] as $filter) { + $reg_exp = $filter["reg_exp"]; + $inverse = $filter["inverse"]; + if ((!$inverse && @preg_match("/$reg_exp/i", $author)) || + ($inverse && !@preg_match("/$reg_exp/i", $author))) { + + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + + if ($filters["tag"]) { + + $tag_string = join(",", $tags); + + foreach ($filters["tag"] as $filter) { + $reg_exp = $filter["reg_exp"]; + $inverse = $filter["inverse"]; + + if ((!$inverse && @preg_match("/$reg_exp/i", $tag_string)) || + ($inverse && !@preg_match("/$reg_exp/i", $tag_string))) { + + array_push($matches, array($filter["action"], $filter["action_param"])); + } + } + } + + + return $matches; + } + + function find_article_filter($filters, $filter_name) { + foreach ($filters as $f) { + if ($f[0] == $filter_name) { + return $f; + }; + } + return false; + } + + function calculate_article_score($filters) { + $score = 0; + + foreach ($filters as $f) { + if ($f[0] == "score") { + $score += $f[1]; + }; + } + return $score; + } + + function assign_article_to_labels($link, $id, $filters, $owner_uid) { + foreach ($filters as $f) { + if ($f[0] == "label") { + label_add_article($link, $id, $f[1], $owner_uid); + }; + } + } + + function getmicrotime() { + list($usec, $sec) = explode(" ",microtime()); + return ((float)$usec + (float)$sec); + } + + function print_radio($id, $default, $true_is, $values, $attributes = "") { + foreach ($values as $v) { + + if ($v == $default) + $sel = "checked"; + else + $sel = ""; + + if ($v == $true_is) { + $sel .= " value=\"1\""; + } else { + $sel .= " value=\"0\""; + } + + print " $v "; + + } + } + + function initialize_user_prefs($link, $uid, $profile = false) { + + $uid = db_escape_string($uid); + + if (!$profile) { + $profile = "NULL"; + $profile_qpart = "AND profile IS NULL"; + } else { + $profile_qpart = "AND profile = '$profile'"; + } + + if (get_schema_version($link) < 63) $profile_qpart = ""; + + db_query($link, "BEGIN"); + + $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs"); + + $u_result = db_query($link, "SELECT pref_name + FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart"); + + $active_prefs = array(); + + while ($line = db_fetch_assoc($u_result)) { + array_push($active_prefs, $line["pref_name"]); + } + + while ($line = db_fetch_assoc($result)) { + if (array_search($line["pref_name"], $active_prefs) === FALSE) { +// print "adding " . $line["pref_name"] . "
    "; + + if (get_schema_version($link) < 63) { + db_query($link, "INSERT INTO ttrss_user_prefs + (owner_uid,pref_name,value) VALUES + ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')"); + + } else { + db_query($link, "INSERT INTO ttrss_user_prefs + (owner_uid,pref_name,value, profile) VALUES + ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)"); + } + + } + } + + db_query($link, "COMMIT"); + + } + + function get_ssl_certificate_id() { + if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) { + return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] . + $_SERVER["REDIRECT_SSL_CLIENT_V_START"] . + $_SERVER["REDIRECT_SSL_CLIENT_V_END"] . + $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]); + } + return ""; + } + + function get_login_by_ssl_certificate($link) { + + $cert_serial = db_escape_string(get_ssl_certificate_id()); + + if ($cert_serial) { + $result = db_query($link, "SELECT login FROM ttrss_user_prefs, ttrss_users + WHERE pref_name = 'SSL_CERT_SERIAL' AND value = '$cert_serial' AND + owner_uid = ttrss_users.id"); + + if (db_num_rows($result) != 0) { + return db_escape_string(db_fetch_result($result, 0, "login")); + } + } + + return ""; + } + + function get_remote_user($link) { + + if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH) { + return db_escape_string($_SERVER["REMOTE_USER"]); + } + + return db_escape_string(get_login_by_ssl_certificate($link)); + } + + function get_remote_fakepass($link) { + if (get_remote_user($link)) + return "******"; + else + return ""; + } + + function authenticate_user($link, $login, $password, $force_auth = false) { + + if (!SINGLE_USER_MODE) { + + $pwd_hash1 = encrypt_password($password); + $pwd_hash2 = encrypt_password($password, $login); + $login = db_escape_string($login); + + $remote_user = get_remote_user($link); + + if ($remote_user && $remote_user == $login && $login != "admin") { + + $login = $remote_user; + + $query = "SELECT id,login,access_level,pwd_hash + FROM ttrss_users WHERE + login = '$login'"; + + if (defined('AUTO_CREATE_USER') && AUTO_CREATE_USER + && $_SERVER["REMOTE_USER"]) { + $result = db_query($link, $query); + + // First login ? + if (db_num_rows($result) == 0) { + $query2 = "INSERT INTO ttrss_users + (login,access_level,last_login,created) + VALUES ('$login', 0, null, NOW())"; + db_query($link, $query2); + } + } + + } else { + $query = "SELECT id,login,access_level,pwd_hash + FROM ttrss_users WHERE + login = '$login' AND (pwd_hash = '$pwd_hash1' OR + pwd_hash = '$pwd_hash2')"; + } + + $result = db_query($link, $query); + + if (db_num_rows($result) == 1) { + $_SESSION["uid"] = db_fetch_result($result, 0, "id"); + $_SESSION["name"] = db_fetch_result($result, 0, "login"); + $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level"); + + db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " . + $_SESSION["uid"]); + + + // LemonLDAP can send user informations via HTTP HEADER + if (defined('AUTO_CREATE_USER') && AUTO_CREATE_USER){ + // update user name + $fullname = $_SERVER['HTTP_USER_NAME'] ? $_SERVER['HTTP_USER_NAME'] : $_SERVER['AUTHENTICATE_CN']; + if ($fullname){ + $fullname = db_escape_string($fullname); + db_query($link, "UPDATE ttrss_users SET full_name = '$fullname' WHERE id = " . + $_SESSION["uid"]); + } + // update user mail + $email = $_SERVER['HTTP_USER_MAIL'] ? $_SERVER['HTTP_USER_MAIL'] : $_SERVER['AUTHENTICATE_MAIL']; + if ($email){ + $email = db_escape_string($email); + db_query($link, "UPDATE ttrss_users SET email = '$email' WHERE id = " . + $_SESSION["uid"]); + } + } + + $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"]; + $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash"); + + $_SESSION["last_version_check"] = time(); + + initialize_user_prefs($link, $_SESSION["uid"]); + + return true; + } + + return false; + + } else { + + $_SESSION["uid"] = 1; + $_SESSION["name"] = "admin"; + + $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"]; + + initialize_user_prefs($link, $_SESSION["uid"]); + + return true; + } + } + + function make_password($length = 8) { + + $password = ""; + $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ"; + + $i = 0; + + while ($i < $length) { + $char = substr($possible, mt_rand(0, strlen($possible)-1), 1); + + if (!strstr($password, $char)) { + $password .= $char; + $i++; + } + } + return $password; + } + + // this is called after user is created to initialize default feeds, labels + // or whatever else + + // user preferences are checked on every login, not here + + function initialize_user($link, $uid) { + + db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url) + values ('$uid', 'Tiny Tiny RSS: New Releases', + 'http://tt-rss.org/releases.rss')"); + + db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url) + values ('$uid', 'Tiny Tiny RSS: Forum', + 'http://tt-rss.org/forum/rss.php')"); + } + + function logout_user() { + session_destroy(); + if (isset($_COOKIE[session_name()])) { + setcookie(session_name(), '', time()-42000, '/'); + } + } + + 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 login_sequence($link, $mobile = false) { + $_SESSION["prefs_cache"] = array(); + + if (!SINGLE_USER_MODE) { + + $login_action = $_POST["login_action"]; + + # try to authenticate user if called from login form + if ($login_action == "do_login") { + $login = db_escape_string($_POST["login"]); + $password = $_POST["password"]; + $remember_me = $_POST["remember_me"]; + + if (authenticate_user($link, $login, $password)) { + $_POST["password"] = ""; + + $_SESSION["language"] = $_POST["language"]; + $_SESSION["ref_schema_version"] = get_schema_version($link, true); + $_SESSION["bw_limit"] = !!$_POST["bw_limit"]; + + if ($_POST["profile"]) { + + $profile = db_escape_string($_POST["profile"]); + + $result = db_query($link, "SELECT id FROM ttrss_settings_profiles + WHERE id = '$profile' AND owner_uid = " . $_SESSION["uid"]); + + if (db_num_rows($result) != 0) { + $_SESSION["profile"] = $profile; + $_SESSION["prefs_cache"] = array(); + } + } + + if ($_REQUEST['return']) { + header("Location: " . $_REQUEST['return']); + } else { + header("Location: " . $_SERVER["REQUEST_URI"]); + } + + exit; + + return; + } else { + $_SESSION["login_error_msg"] = __("Incorrect username or password"); + } + } + + if (!$_SESSION["uid"] || !validate_session($link)) { + + if (get_remote_user($link) && AUTO_LOGIN) { + authenticate_user($link, get_remote_user($link), null); + $_SESSION["ref_schema_version"] = get_schema_version($link, true); + } else { + render_login_form($link, $mobile); + //header("Location: login.php"); + exit; + } + } else { + /* bump login timestamp */ + db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " . + $_SESSION["uid"]); + + if ($_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) { + setcookie("ttrss_lang", $_SESSION["language"], + time() + SESSION_COOKIE_LIFETIME); + } + + // try to remove possible duplicates from feed counter cache +// ccache_cleanup($link, $_SESSION["uid"]); + } + + } else { + return authenticate_user($link, "admin", null); + } + } + + function truncate_string($str, $max_len, $suffix = '…') { + if (mb_strlen($str, "utf-8") > $max_len - 3) { + return mb_substr($str, 0, $max_len, "utf-8") . $suffix; + } else { + return $str; + } + } + + function theme_image($link, $filename) { + if ($link) { + $theme_path = get_user_theme_path($link); + + if ($theme_path && is_file($theme_path.$filename)) { + return $theme_path.$filename; + } else { + return $filename; + } + } else { + return $filename; + } + } + + function get_user_theme($link) { + + if (get_schema_version($link) >= 63 && $_SESSION["uid"]) { + $theme_name = get_pref($link, "_THEME_ID"); + if (is_dir("themes/$theme_name")) { + return $theme_name; + } else { + return ''; + } + } else { + return ''; + } + + } + + function get_user_theme_path($link) { + $theme_path = ''; + + if (get_schema_version($link) >= 63 && $_SESSION["uid"]) { + $theme_name = get_pref($link, "_THEME_ID"); + + if ($theme_name && is_dir("themes/$theme_name")) { + $theme_path = "themes/$theme_name/"; + } else { + $theme_name = ''; + } + } else { + $theme_path = ''; + } + + if ($theme_path) { + if (is_file("$theme_path/theme.ini")) { + $ini = parse_ini_file("$theme_path/theme.ini", true); + if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED) { + return $theme_path; + } + } + } + return ''; + } + + function get_user_theme_options($link) { + $t = get_user_theme_path($link); + + if ($t) { + if (is_file("$t/theme.ini")) { + $ini = parse_ini_file("$t/theme.ini", true); + if ($ini['theme']['version']) { + return $ini['theme']['options']; + } + } + } + return ''; + } + + function print_theme_includes($link) { + + $t = get_user_theme_path($link); + $time = time(); + + if ($t) { + print ""; + if (file_exists("$t/theme.js")) { + print ""; + } + } + } + + function get_all_themes() { + $themes = glob("themes/*"); + + asort($themes); + + $rv = array(); + + foreach ($themes as $t) { + if (is_file("$t/theme.ini")) { + $ini = parse_ini_file("$t/theme.ini", true); + if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED && + !$ini['theme']['disabled']) { + $entry = array(); + $entry["path"] = $t; + $entry["base"] = basename($t); + $entry["name"] = $ini['theme']['name']; + $entry["version"] = $ini['theme']['version']; + $entry["author"] = $ini['theme']['author']; + $entry["options"] = $ini['theme']['options']; + array_push($rv, $entry); + } + } + } + + return $rv; + } + + function convert_timestamp($timestamp, $source_tz, $dest_tz) { + + try { + $source_tz = new DateTimeZone($source_tz); + } catch (Exception $e) { + $source_tz = new DateTimeZone('UTC'); + } + + try { + $dest_tz = new DateTimeZone($dest_tz); + } catch (Exception $e) { + $dest_tz = new DateTimeZone('UTC'); + } + + $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz); + return $dt->format('U') + $dest_tz->getOffset($dt); + } + + function make_local_datetime($link, $timestamp, $long, $owner_uid = false, + $no_smart_dt = false) { + + if (!$owner_uid) $owner_uid = $_SESSION['uid']; + if (!$timestamp) $timestamp = '1970-01-01 0:00'; + + global $utc_tz; + global $tz_offset; + + # We store date in UTC internally + $dt = new DateTime($timestamp, $utc_tz); + + if ($tz_offset == -1) { + + $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid); + + try { + $user_tz = new DateTimeZone($user_tz_string); + } catch (Exception $e) { + $user_tz = $utc_tz; + } + + $tz_offset = $user_tz->getOffset($dt); + } + + $user_timestamp = $dt->format('U') + $tz_offset; + + if (!$no_smart_dt) { + return smart_date_time($link, $user_timestamp, + $tz_offset, $owner_uid); + } else { + if ($long) + $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid); + else + $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid); + + return date($format, $user_timestamp); + } + } + + function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) { + if (!$owner_uid) $owner_uid = $_SESSION['uid']; + + if (date("Y.m.d", $timestamp) == date("Y.m.d", time() + $tz_offset)) { + return date("G:i", $timestamp); + } else if (date("Y", $timestamp) == date("Y", time() + $tz_offset)) { + $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid); + return date($format, $timestamp); + } else { + $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid); + return date($format, $timestamp); + } + } + + function sql_bool_to_bool($s) { + if ($s == "t" || $s == "1" || $s == "true") { + return true; + } else { + return false; + } + } + + function bool_to_sql_bool($s) { + if ($s) { + return "true"; + } else { + return "false"; + } + } + + // Session caching removed due to causing wrong redirects to upgrade + // script when get_schema_version() is called on an obsolete session + // created on a previous schema version. + function get_schema_version($link, $nocache = false) { + global $schema_version; + + if (!$schema_version) { + $result = db_query($link, "SELECT schema_version FROM ttrss_version"); + $version = db_fetch_result($result, 0, "schema_version"); + $schema_version = $version; + return $version; + } else { + return $schema_version; + } + } + + function sanity_check($link) { + require_once 'errors.php'; + + $error_code = 0; + $schema_version = get_schema_version($link, true); + + if ($schema_version != SCHEMA_VERSION) { + $error_code = 5; + } + + if (DB_TYPE == "mysql") { + $result = db_query($link, "SELECT true", false); + if (db_num_rows($result) != 1) { + $error_code = 10; + } + } + + if (db_escape_string("testTEST") != "testTEST") { + $error_code = 12; + } + + return array("code" => $error_code, "message" => $ERRORS[$error_code]); + } + + function file_is_locked($filename) { + if (function_exists('flock')) { + $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r"); + if ($fp) { + if (flock($fp, LOCK_EX | LOCK_NB)) { + flock($fp, LOCK_UN); + fclose($fp); + return false; + } + fclose($fp); + return true; + } else { + return false; + } + } + return true; // consider the file always locked and skip the test + } + + function make_lockfile($filename) { + $fp = fopen(LOCK_DIRECTORY . "/$filename", "w"); + + if (flock($fp, LOCK_EX | LOCK_NB)) { + if (function_exists('posix_getpid')) { + fwrite($fp, posix_getpid() . "\n"); + } + return $fp; + } else { + return false; + } + } + + function make_stampfile($filename) { + $fp = fopen(LOCK_DIRECTORY . "/$filename", "w"); + + if (flock($fp, LOCK_EX | LOCK_NB)) { + fwrite($fp, time() . "\n"); + flock($fp, LOCK_UN); + fclose($fp); + return true; + } else { + return false; + } + } + + function sql_random_function() { + if (DB_TYPE == "mysql") { + return "RAND()"; + } else { + return "RANDOM()"; + } + } + + function catchup_feed($link, $feed, $cat_view, $owner_uid = false) { + + if (!$owner_uid) $owner_uid = $_SESSION['uid']; + + //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { + + if (is_numeric($feed)) { + if ($cat_view) { + + if ($feed >= 0) { + + if ($feed > 0) { + $cat_qpart = "cat_id = '$feed'"; + } else { + $cat_qpart = "cat_id IS NULL"; + } + + $tmp_result = db_query($link, "SELECT id + FROM ttrss_feeds WHERE $cat_qpart AND owner_uid = $owner_uid"); + + while ($tmp_line = db_fetch_assoc($tmp_result)) { + + $tmp_feed = $tmp_line["id"]; + + db_query($link, "UPDATE ttrss_user_entries + SET unread = false,last_read = NOW() + WHERE feed_id = '$tmp_feed' AND owner_uid = $owner_uid"); + } + } 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 unread = true 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 owner_uid = $owner_uid"); + + } else if ($feed < 0 && $feed > -10) { // special, like starred + + if ($feed == -1) { + db_query($link, "UPDATE ttrss_user_entries + SET unread = false,last_read = NOW() + WHERE marked = true AND owner_uid = $owner_uid"); + } + + if ($feed == -2) { + db_query($link, "UPDATE ttrss_user_entries + SET unread = false,last_read = NOW() + WHERE published = true AND owner_uid = $owner_uid"); + } + + if ($feed == -3) { + + $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE"); + + if (DB_TYPE == "pgsql") { + $match_part = "updated > NOW() - INTERVAL '$intl hour' "; + } else { + $match_part = "updated > 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); + } + + if ($feed == -4) { + db_query($link, "UPDATE ttrss_user_entries + SET unread = false,last_read = NOW() + WHERE owner_uid = $owner_uid"); + } + + } else if ($feed < -10) { // label + + $label_id = -$feed - 11; + + 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 owner_uid = '$owner_uid' AND ref_id = article_id"); + + } + + ccache_update($link, $feed, $owner_uid, $cat_view); + + } else { // tag + db_query($link, "BEGIN"); + + $tag_name = db_escape_string($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 int_id = " . $line["post_int_id"]); + } + db_query($link, "COMMIT"); + } + } + + function getAllCounters($link, $omode = "flc", $active_feed = false) { + + if (!$omode) $omode = "flc"; + + $data = getGlobalCounters($link); + + $data = array_merge($data, getVirtCounters($link)); + + if (strchr($omode, "l")) $data = array_merge($data, getLabelCounters($link)); + if (strchr($omode, "f")) $data = array_merge($data, getFeedCounters($link, $active_feed)); + if (strchr($omode, "t")) $data = array_merge($data, getTagCounters($link)); + if (strchr($omode, "c")) $data = array_merge($data, getCategoryCounters($link)); + + return $data; + } + + function getCategoryCounters($link) { + $ret_arr = array(); + + /* Labels category */ + + $cv = array("id" => -2, "kind" => "cat", + "counter" => getCategoryUnread($link, -2)); + + array_push($ret_arr, $cv); + + $age_qpart = getMaxAgeSubquery(); + + $result = db_query($link, "SELECT id AS cat_id, value AS unread + FROM ttrss_feed_categories, ttrss_cat_counters_cache + WHERE ttrss_cat_counters_cache.feed_id = id AND + ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]); + + while ($line = db_fetch_assoc($result)) { + $line["cat_id"] = (int) $line["cat_id"]; + + $cv = array("id" => $line["cat_id"], "kind" => "cat", + "counter" => $line["unread"]); + + array_push($ret_arr, $cv); + } + + /* Special case: NULL category doesn't actually exist in the DB */ + + $cv = array("id" => 0, "kind" => "cat", + "counter" => ccache_find($link, 0, $_SESSION["uid"], true)); + + array_push($ret_arr, $cv); + + return $ret_arr; + } + + function getCategoryUnread($link, $cat, $owner_uid = false) { + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + if ($cat >= 0) { + + if ($cat != 0) { + $cat_query = "cat_id = '$cat'"; + } else { + $cat_query = "cat_id IS NULL"; + } + + $age_qpart = getMaxAgeSubquery(); + + $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query + AND owner_uid = " . $owner_uid); + + $cat_feeds = array(); + while ($line = db_fetch_assoc($result)) { + array_push($cat_feeds, "feed_id = " . $line["id"]); + } + + if (count($cat_feeds) == 0) return 0; + + $match_part = implode(" OR ", $cat_feeds); + + $result = db_query($link, "SELECT COUNT(int_id) AS unread + FROM ttrss_user_entries,ttrss_entries + WHERE unread = true AND ($match_part) AND id = ref_id + AND $age_qpart AND owner_uid = " . $owner_uid); + + $unread = 0; + + # this needs to be rewritten + while ($line = db_fetch_assoc($result)) { + $unread += $line["unread"]; + } + + return $unread; + } else if ($cat == -1) { + return getFeedUnread($link, -1) + getFeedUnread($link, -2) + getFeedUnread($link, -3) + getFeedUnread($link, 0); + } else if ($cat == -2) { + + $result = db_query($link, " + SELECT COUNT(unread) AS unread FROM + ttrss_user_entries, ttrss_labels2, ttrss_user_labels2, ttrss_feeds + WHERE label_id = ttrss_labels2.id AND article_id = ref_id AND + ttrss_labels2.owner_uid = '$owner_uid' + AND unread = true AND feed_id = ttrss_feeds.id + AND ttrss_user_entries.owner_uid = '$owner_uid'"); + + $unread = db_fetch_result($result, 0, "unread"); + + return $unread; + + } + } + + function getMaxAgeSubquery($days = COUNTERS_MAX_AGE) { + if (DB_TYPE == "pgsql") { + return "ttrss_entries.date_updated > + NOW() - INTERVAL '$days days'"; + } else { + return "ttrss_entries.date_updated > + DATE_SUB(NOW(), INTERVAL $days DAY)"; + } + } + + function getFeedUnread($link, $feed, $is_cat = false) { + return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]); + } + + function getLabelUnread($link, $label_id, $owner_uid = false) { + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + $result = db_query($link, " + SELECT COUNT(unread) AS unread FROM + ttrss_user_entries, ttrss_labels2, ttrss_user_labels2, ttrss_feeds + WHERE label_id = ttrss_labels2.id AND article_id = ref_id AND + ttrss_labels2.owner_uid = '$owner_uid' AND ttrss_labels2.id = '$label_id' + AND unread = true AND feed_id = ttrss_feeds.id + AND ttrss_user_entries.owner_uid = '$owner_uid'"); + + if (db_num_rows($result) != 0) { + return db_fetch_result($result, 0, "unread"); + } else { + return 0; + } + } + + function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false, + $owner_uid = false) { + + $n_feed = (int) $feed; + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + if ($unread_only) { + $unread_qpart = "unread = true"; + } else { + $unread_qpart = "true"; + } + + $age_qpart = getMaxAgeSubquery(); + + if ($is_cat) { + return getCategoryUnread($link, $n_feed, $owner_uid); + } if ($feed != "0" && $n_feed == 0) { + + $feed = db_escape_string($feed); + + $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id) + FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id + AND ref_id = id AND $age_qpart + AND $unread_qpart)) AS count FROM ttrss_tags + WHERE owner_uid = $owner_uid AND tag_name = '$feed'"); + return db_fetch_result($result, 0, "count"); + + } else if ($n_feed == -1) { + $match_part = "marked = true"; + } else if ($n_feed == -2) { + $match_part = "published = true"; + } else if ($n_feed == -3) { + $match_part = "unread = true AND score >= 0"; + + $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid); + + if (DB_TYPE == "pgsql") { + $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' "; + } else { + $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) "; + } + } else if ($n_feed == -4) { + $match_part = "true"; + } else if ($n_feed >= 0) { + + if ($n_feed != 0) { + $match_part = "feed_id = '$n_feed'"; + } else { + $match_part = "feed_id IS NULL"; + } + + } else if ($feed < -10) { + + $label_id = -$feed - 11; + + return getLabelUnread($link, $label_id, $owner_uid); + + } + + if ($match_part) { + + if ($n_feed != 0) { + $from_qpart = "ttrss_user_entries,ttrss_feeds,ttrss_entries"; + $feeds_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND"; + } else { + $from_qpart = "ttrss_user_entries,ttrss_entries"; + $feeds_qpart = ''; + } + + $query = "SELECT count(int_id) AS unread + FROM $from_qpart WHERE + ttrss_user_entries.ref_id = ttrss_entries.id AND + $age_qpart AND + $feeds_qpart + $unread_qpart AND ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid"; + + $result = db_query($link, $query); + + } else { + + $result = db_query($link, "SELECT COUNT(post_int_id) AS unread + FROM ttrss_tags,ttrss_user_entries,ttrss_entries + WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id + AND $unread_qpart AND $age_qpart AND + ttrss_tags.owner_uid = " . $owner_uid); + } + + $unread = db_fetch_result($result, 0, "unread"); + + return $unread; + } + + function getGlobalUnread($link, $user_id = false) { + + if (!$user_id) { + $user_id = $_SESSION["uid"]; + } + + $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache + WHERE owner_uid = '$user_id' AND feed_id > 0"); + + $c_id = db_fetch_result($result, 0, "c_id"); + + return $c_id; + } + + function getGlobalCounters($link, $global_unread = -1) { + $ret_arr = array(); + + if ($global_unread == -1) { + $global_unread = getGlobalUnread($link); + } + + $cv = array("id" => "global-unread", + "counter" => $global_unread); + + array_push($ret_arr, $cv); + + $result = db_query($link, "SELECT COUNT(id) AS fn FROM + ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); + + $subscribed_feeds = db_fetch_result($result, 0, "fn"); + + $cv = array("id" => "subscribed-feeds", + "counter" => $subscribed_feeds); + + array_push($ret_arr, $cv); + + return $ret_arr; + } + + function getTagCounters($link) { + + $ret_arr = array(); + + $age_qpart = getMaxAgeSubquery(); + + $result = db_query($link, "SELECT tag_name,SUM((SELECT COUNT(int_id) + FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id + AND ref_id = id AND $age_qpart + AND unread = true)) AS count FROM ttrss_tags + WHERE owner_uid = ".$_SESSION['uid']." GROUP BY tag_name + ORDER BY count DESC LIMIT 55"); + + $tags = array(); + + while ($line = db_fetch_assoc($result)) { + $tags[$line["tag_name"]] += $line["count"]; + } + + foreach (array_keys($tags) as $tag) { + $unread = $tags[$tag]; + $tag = htmlspecialchars($tag); + + $cv = array("id" => $tag, + "kind" => "tag", + "counter" => $unread); + + array_push($ret_arr, $cv); + } + + return $ret_arr; + } + + function getVirtCounters($link) { + + $ret_arr = array(); + + for ($i = 0; $i >= -4; $i--) { + + $count = getFeedUnread($link, $i); + + $cv = array("id" => $i, + "counter" => $count); + +// if (get_pref($link, 'EXTENDED_FEEDLIST')) +// $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total"); + + array_push($ret_arr, $cv); + } + + return $ret_arr; + } + + function getLabelCounters($link, $descriptions = false) { + + $ret_arr = array(); + + $age_qpart = getMaxAgeSubquery(); + + $owner_uid = $_SESSION["uid"]; + + $result = db_query($link, "SELECT id, caption FROM ttrss_labels2 + WHERE owner_uid = '$owner_uid'"); + + while ($line = db_fetch_assoc($result)) { + + $id = -$line["id"] - 11; + + $label_name = $line["caption"]; + $count = getFeedUnread($link, $id); + + $cv = array("id" => $id, + "counter" => $count); + + if ($descriptions) + $cv["description"] = $label_name; + +// if (get_pref($link, 'EXTENDED_FEEDLIST')) +// $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total"); + + array_push($ret_arr, $cv); + } + + return $ret_arr; + } + + function getFeedCounters($link, $active_feed = false) { + + $ret_arr = array(); + + $age_qpart = getMaxAgeSubquery(); + + $query = "SELECT ttrss_feeds.id, + ttrss_feeds.title, + ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated, + last_error, value AS count + FROM ttrss_feeds, ttrss_counters_cache + WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]." + AND ttrss_counters_cache.feed_id = id"; + + $result = db_query($link, $query); + $fctrs_modified = false; + + while ($line = db_fetch_assoc($result)) { + + $id = $line["id"]; + $count = $line["count"]; + $last_error = htmlspecialchars($line["last_error"]); + + $last_updated = make_local_datetime($link, $line['last_updated'], false); + + $has_img = feed_has_icon($id); + + if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2) + $last_updated = ''; + + $cv = array("id" => $id, + "updated" => $last_updated, + "counter" => $count, + "has_img" => (int) $has_img); + + if ($last_error) + $cv["error"] = $last_error; + +// if (get_pref($link, 'EXTENDED_FEEDLIST')) +// $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total"); + + if ($active_feed && $id == $active_feed) + $cv["title"] = truncate_string($line["title"], 30); + + array_push($ret_arr, $cv); + + } + + return $ret_arr; + } + + function get_pgsql_version($link) { + $result = db_query($link, "SELECT version() AS version"); + $version = explode(" ", db_fetch_result($result, 0, "version")); + return $version[1]; + } + + /** + * Subscribes the user to the given feed + * + * @param resource $link Database connection + * @param string $url Feed URL to subscribe to + * @param integer $cat_id Category ID the feed shall be added to + * @param string $auth_login (optional) Feed username + * @param string $auth_pass (optional) Feed password + * + * @return integer Status code: + * 0 - OK, Feed already exists + * 1 - OK, Feed added + * 2 - Invalid URL + * 3 - URL content is HTML, no feeds available + * 4 - URL content is HTML which contains multiple feeds. + * Here you should call extractfeedurls in rpc-backend + * to get all possible feeds. + * 5 - Couldn't download the URL content. + */ + function subscribe_to_feed($link, $url, $cat_id = 0, + $auth_login = '', $auth_pass = '') { + + $url = fix_url($url); + + if (!$url || !validate_feed_url($url)) return 2; + + $update_method = 0; + + $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users + WHERE id = ".$_SESSION['uid']); + + $has_oauth = db_fetch_result($result, 0, 'twitter_oauth'); + + if (!$has_oauth || strpos($url, '://api.twitter.com') === false) { + if (!fetch_file_contents($url, false, $auth_login, $auth_pass)) return 5; + + if (url_is_html($url, $auth_login, $auth_pass)) { + $feedUrls = get_feeds_from_html($url, $auth_login, $auth_pass); + if (count($feedUrls) == 0) { + return 3; + } else if (count($feedUrls) > 1) { + return 4; + } + //use feed url as new URL + $url = key($feedUrls); + } + + } else { + if (!fetch_twitter_rss($link, $url, $_SESSION['uid'])) + return 5; + + $update_method = 3; + } + if ($cat_id == "0" || !$cat_id) { + $cat_qpart = "NULL"; + } else { + $cat_qpart = "'$cat_id'"; + } + + $result = db_query($link, + "SELECT id FROM ttrss_feeds + WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]); + + if (db_num_rows($result) == 0) { + $result = db_query($link, + "INSERT INTO ttrss_feeds + (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method) + VALUES ('".$_SESSION["uid"]."', '$url', + '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', '$update_method')"); + + $result = db_query($link, + "SELECT id FROM ttrss_feeds WHERE feed_url = '$url' + AND owner_uid = " . $_SESSION["uid"]); + + $feed_id = db_fetch_result($result, 0, "id"); + + if ($feed_id) { + update_rss_feed($link, $feed_id, true); + } + + return 1; + } else { + return 0; + } + } + + function print_feed_select($link, $id, $default_id = "", + $attributes = "", $include_all_feeds = true) { + + print ""; + } + + function print_feed_cat_select($link, $id, $default_id = "", + $attributes = "", $include_all_cats = true) { + + print ""; + } + + function checkbox_to_sql_bool($val) { + return ($val == "on") ? "true" : "false"; + } + + function getFeedCatTitle($link, $id) { + if ($id == -1) { + return __("Special"); + } else if ($id < -10) { + return __("Labels"); + } else if ($id > 0) { + $result = db_query($link, "SELECT ttrss_feed_categories.title + FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND + cat_id = ttrss_feed_categories.id"); + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "title"); + } else { + return __("Uncategorized"); + } + } else { + return "getFeedCatTitle($id) failed"; + } + + } + + function getFeedIcon($id) { + switch ($id) { + case 0: + return "images/archive.png"; + break; + case -1: + return "images/mark_set.png"; + break; + case -2: + return "images/pub_set.png"; + break; + case -3: + return "images/fresh.png"; + break; + case -4: + return "images/tag.png"; + break; + default: + if ($id < -10) { + return "images/label.png"; + } else { + if (file_exists(ICONS_DIR . "/$id.ico")) + return ICONS_URL . "/$id.ico"; + } + break; + } + } + + function getFeedTitle($link, $id) { + if ($id == -1) { + return __("Starred articles"); + } else if ($id == -2) { + return __("Published articles"); + } else if ($id == -3) { + return __("Fresh articles"); + } else if ($id == -4) { + return __("All articles"); + } else if ($id === 0 || $id === "0") { + return __("Archived articles"); + } else if ($id < -10) { + $label_id = -$id - 11; + $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'"); + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "caption"); + } else { + return "Unknown label ($label_id)"; + } + + } else if (is_numeric($id) && $id > 0) { + $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'"); + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "title"); + } else { + return "Unknown feed ($id)"; + } + } else { + return $id; + } + } + + function make_init_params($link) { + $params = array(); + + $params["theme"] = get_user_theme($link); + $params["theme_options"] = get_user_theme_options($link); + + $params["sign_progress"] = theme_image($link, "images/indicator_white.gif"); + $params["sign_progress_tiny"] = theme_image($link, "images/indicator_tiny.gif"); + $params["sign_excl"] = theme_image($link, "images/sign_excl.png"); + $params["sign_info"] = theme_image($link, "images/sign_info.png"); + + 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", + "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) { + + $params[strtolower($param)] = (int) get_pref($link, $param); + } + + $params["icons_url"] = ICONS_URL; + $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME; + $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE"); + $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT"); + $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY"); + $params["bw_limit"] = (int) $_SESSION["bw_limit"]; + + $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM + ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); + + $max_feed_id = db_fetch_result($result, 0, "mid"); + $num_feeds = db_fetch_result($result, 0, "nf"); + + $params["max_feed_id"] = (int) $max_feed_id; + $params["num_feeds"] = (int) $num_feeds; + + $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST"); + + return $params; + } + + function make_runtime_info($link) { + $data = array(); + + $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM + ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]); + + $max_feed_id = db_fetch_result($result, 0, "mid"); + $num_feeds = db_fetch_result($result, 0, "nf"); + + $data["max_feed_id"] = (int) $max_feed_id; + $data["num_feeds"] = (int) $num_feeds; + + $data['last_article_id'] = getLastArticleId($link); + $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED'); + + if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) { + + $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock"); + + if (time() - $_SESSION["daemon_stamp_check"] > 30) { + + $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp"); + + if ($stamp) { + $stamp_delta = time() - $stamp; + + if ($stamp_delta > 1800) { + $stamp_check = 0; + } else { + $stamp_check = 1; + $_SESSION["daemon_stamp_check"] = time(); + } + + $data['daemon_stamp_ok'] = $stamp_check; + + $stamp_fmt = date("Y.m.d, G:i", $stamp); + + $data['daemon_stamp'] = $stamp_fmt; + } + } + } + + if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) { + $new_version_details = @check_for_update($link); + + $data['new_version_available'] = (int) ($new_version_details != false); + + $_SESSION["last_version_check"] = time(); + } + + return $data; + } + + function search_to_sql($link, $search, $match_on) { + + $search_query_part = ""; + + $keywords = explode(" ", $search); + $query_keywords = array(); + + foreach ($keywords as $k) { + if (strpos($k, "-") === 0) { + $k = substr($k, 1); + $not = "NOT"; + } else { + $not = ""; + } + + $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]) { + + if ($commandpair[1] == "true") + array_push($query_keywords, "($not (published = true))"); + else + array_push($query_keywords, "($not (published = false))"); + + } else 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')); + + //$k = date("Y-m-d", strtotime(substr($k, 1))); + + array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')"); + } else if ($match_on == "both") { + array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%') + OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))"); + } else if ($match_on == "title") { + array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%'))"); + } else if ($match_on == "content") { + array_push($query_keywords, "(UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))"); + } + } + + $search_query_part = implode("AND", $query_keywords); + + return $search_query_part; + } + + + function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $match_on, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0) { + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + $ext_tables_part = ""; + + if ($search) { + + if (SPHINX_ENABLED) { + $ids = join(",", @sphinx_search($search, 0, 500)); + + if ($ids) + $search_query_part = "ref_id IN ($ids) AND "; + else + $search_query_part = "ref_id = -1 AND "; + + } else { + $search_query_part = search_to_sql($link, $search, $match_on); + $search_query_part .= " AND "; + } + + } else { + $search_query_part = ""; + } + + if ($filter) { + $filter_query_part = filter_to_sql($filter); + } else { + $filter_query_part = ""; + } + + if ($since_id) { + $since_id_part = "ttrss_entries.id > $since_id AND "; + } else { + $since_id_part = ""; + } + + $view_query_part = ""; + + if ($view_mode == "adaptive" || $view_query_part == "noscores") { + if ($search) { + $view_query_part = " "; + } else if ($feed != -1) { + $unread = getFeedUnread($link, $feed, $cat_view); + if ($unread > 0) { + $view_query_part = " unread = true AND "; + } + } + } + + if ($view_mode == "marked") { + $view_query_part = " marked = true AND "; + } + + if ($view_mode == "published") { + $view_query_part = " published = true AND "; + } + + if ($view_mode == "unread") { + $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; + } + + $vfeed_query_part = ""; + + // override query strategy and enable feed display when searching globally + if ($search && $search_mode == "all_feeds") { + $query_strategy_part = "ttrss_entries.id > 0"; + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + /* tags */ + } else if (preg_match("/^-?[0-9][0-9]*$/", $feed) == false) { + $query_strategy_part = "ttrss_entries.id > 0"; + $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE + id = feed_id) as feed_title,"; + } else if ($feed > 0 && $search && $search_mode == "this_cat") { + + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + + $tmp_result = false; + + if ($cat_view) { + $tmp_result = db_query($link, "SELECT id + FROM ttrss_feeds WHERE cat_id = '$feed'"); + } else { + $tmp_result = db_query($link, "SELECT id + FROM ttrss_feeds WHERE cat_id = (SELECT cat_id FROM ttrss_feeds + WHERE id = '$feed') AND id != '$feed'"); + } + + $cat_siblings = array(); + + if (db_num_rows($tmp_result) > 0) { + while ($p = db_fetch_assoc($tmp_result)) { + array_push($cat_siblings, "feed_id = " . $p["id"]); + } + + $query_strategy_part = sprintf("(feed_id = %d OR %s)", + $feed, implode(" OR ", $cat_siblings)); + + } else { + $query_strategy_part = "ttrss_entries.id > 0"; + } + + } else if ($feed > 0) { + + if ($cat_view) { + + if ($feed > 0) { + $query_strategy_part = "cat_id = '$feed'"; + } else { + $query_strategy_part = "cat_id IS NULL"; + } + + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + + } else { + $query_strategy_part = "feed_id = '$feed'"; + } + } else if ($feed == 0 && !$cat_view) { // archive virtual feed + $query_strategy_part = "feed_id IS NULL"; + } else if ($feed == 0 && $cat_view) { // uncategorized + $query_strategy_part = "cat_id IS NULL"; + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + } else if ($feed == -1) { // starred virtual feed + $query_strategy_part = "marked = true"; + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + } else if ($feed == -2) { // published virtual feed OR labels category + + if (!$cat_view) { + $query_strategy_part = "published = true"; + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + } else { + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + + $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2"; + + $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND + ttrss_user_labels2.article_id = ref_id"; + + } + + } else if ($feed == -3) { // fresh virtual feed + $query_strategy_part = "unread = true AND score >= 0"; + + $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid); + + if (DB_TYPE == "pgsql") { + $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' "; + } else { + $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) "; + } + + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + } else if ($feed == -4) { // all articles virtual feed + $query_strategy_part = "true"; + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + } else if ($feed <= -10) { // labels + $label_id = -$feed - 11; + + $query_strategy_part = "label_id = '$label_id' AND + ttrss_labels2.id = ttrss_user_labels2.label_id AND + ttrss_user_labels2.article_id = ref_id"; + + $vfeed_query_part = "ttrss_feeds.title AS feed_title,"; + $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2"; + + } else { + $query_strategy_part = "id > 0"; // dumb + } + + 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"; + } + + if ($override_order) { + $order_by = $override_order; + } + + $feed_title = ""; + + if ($search) { + $feed_title = "Search results"; + } else { + if ($cat_view) { + $feed_title = getCategoryTitle($link, $feed); + } else { + if (is_numeric($feed) && $feed > 0) { + $result = db_query($link, "SELECT title,site_url,last_error + FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid"); + + $feed_title = db_fetch_result($result, 0, "title"); + $feed_site_url = db_fetch_result($result, 0, "site_url"); + $last_error = db_fetch_result($result, 0, "last_error"); + } else { + $feed_title = getFeedTitle($link, $feed); + } + } + } + + $content_query_part = "content as content_preview,"; + + if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { + + if ($feed >= 0) { + $feed_kind = "Feeds"; + } else { + $feed_kind = "Labels"; + } + + if ($limit_query_part) { + $offset_query_part = "OFFSET $offset"; + } + + if ($vfeed_query_part && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) { + if (!$override_order) { + $order_by = "ttrss_feeds.title, $order_by"; + } + } + + if ($feed != "0") { + $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part"; + $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND"; + + } else { + $from_qpart = "ttrss_entries,ttrss_user_entries$ext_tables_part + LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)"; + } + + $query = "SELECT DISTINCT + date_entered, + guid, + ttrss_entries.id,ttrss_entries.title, + updated, + label_cache, + tag_cache, + always_display_enclosures, + site_url, + note, + num_comments, + comments, + int_id, + unread,feed_id,marked,published,link,last_read,orig_feed_id, + ".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 + WHERE + $feed_check_qpart + ttrss_user_entries.ref_id = ttrss_entries.id AND + ttrss_user_entries.owner_uid = '$owner_uid' AND + $search_query_part + $filter_query_part + $view_query_part + $since_id_part + $query_strategy_part ORDER BY $order_by + $limit_query_part $offset_query_part"; + + if ($_REQUEST["debug"]) print $query; + + $result = db_query($link, $query); + + } else { + // browsing by tag + + $select_qpart = "SELECT DISTINCT " . + "date_entered," . + "guid," . + "note," . + "ttrss_entries.id as id," . + "title," . + "updated," . + "unread," . + "feed_id," . + "orig_feed_id," . + "site_url," . + "always_display_enclosures, ". + "marked," . + "num_comments, " . + "comments, " . + "tag_cache," . + "label_cache," . + "link," . + "last_read," . + 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"; + $all_tags = explode(",", $feed); + if ($search_mode == 'any') { + $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")"; + $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags "; + $where_qpart = " WHERE " . + "ref_id = ttrss_entries.id AND " . + "ttrss_user_entries.owner_uid = $owner_uid AND " . + "post_int_id = int_id AND $tag_sql AND " . + $view_query_part . + $search_query_part . + $query_strategy_part . " ORDER BY $order_by " . + $limit_query_part; + + } else { + $i = 1; + $sub_selects = array(); + $sub_ands = array(); + foreach ($all_tags as $term) { + array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i"); + $i++; + } + if ($i > 2) { + $x = 1; + $y = 2; + do { + array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id"); + $x++; + $y++; + } while ($y < $i); + } + array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid"); + array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id"); + $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries"; + $where_qpart = " WHERE " . implode(" AND ", $sub_ands); + } + // error_log("TAG SQL: " . $tag_sql); + // $tag_sql = "tag_name = '$feed'"; DEFAULT way + + // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]"); + $result = db_query($link, $select_qpart . $from_qpart . $where_qpart); + } + + return array($result, $feed_title, $feed_site_url, $last_error); + + } + + function generate_syndicated_feed($link, $owner_uid, $feed, $is_cat, + $limit, $search, $search_mode, $match_on, $view_mode = false) { + + require_once "lib/MiniTemplator.class.php"; + + $note_style = "background-color : #fff7d5; + border-width : 1px; ". + "padding : 5px; border-style : dashed; border-color : #e7d796;". + "margin-bottom : 1em; color : #9a8c59;"; + + if (!$limit) $limit = 30; + + if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) { + $date_sort_field = "updated"; + } else { + $date_sort_field = "date_entered"; + } + + $qfh_ret = queryFeedHeadlines($link, $feed, + $limit, $view_mode, $is_cat, $search, $search_mode, + $match_on, "$date_sort_field DESC", 0, $owner_uid); + + $result = $qfh_ret[0]; + $feed_title = htmlspecialchars($qfh_ret[1]); + $feed_site_url = $qfh_ret[2]; + $last_error = $qfh_ret[3]; + + $feed_self_url = get_self_url_prefix() . + "/public.php?op=rss&id=-2&key=" . + get_feed_access_key($link, -2, false); + + if (!$feed_site_url) $feed_site_url = get_self_url_prefix(); + + $tpl = new MiniTemplator; + + $tpl->readTemplateFromFile("templates/generated_feed.txt"); + + $tpl->setVariable('FEED_TITLE', $feed_title); + $tpl->setVariable('VERSION', VERSION); + $tpl->setVariable('FEED_URL', htmlspecialchars($feed_self_url)); + + if (PUBSUBHUBBUB_HUB && $feed == -2) { + $tpl->setVariable('HUB_URL', htmlspecialchars(PUBSUBHUBBUB_HUB)); + $tpl->addBlock('feed_hub'); + } + + $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix())); + + while ($line = db_fetch_assoc($result)) { + $tpl->setVariable('ARTICLE_ID', htmlspecialchars($line['link'])); + $tpl->setVariable('ARTICLE_LINK', htmlspecialchars($line['link'])); + $tpl->setVariable('ARTICLE_TITLE', htmlspecialchars($line['title'])); + $tpl->setVariable('ARTICLE_EXCERPT', + truncate_string(strip_tags($line["content_preview"]), 100, '...')); + + $content = sanitize_rss($link, $line["content_preview"], false, $owner_uid); + + if ($line['note']) { + $content = "
    Article note: " . $line['note'] . "
    " . + $content; + } + + $tpl->setVariable('ARTICLE_CONTENT', $content); + + $tpl->setVariable('ARTICLE_UPDATED', date('c', strtotime($line["updated"]))); + $tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author'])); + + $tags = get_article_tags($link, $line["id"], $owner_uid); + + foreach ($tags as $tag) { + $tpl->setVariable('ARTICLE_CATEGORY', htmlspecialchars($tag)); + $tpl->addBlock('category'); + } + + $enclosures = get_article_enclosures($link, $line["id"]); + + foreach ($enclosures as $e) { + $type = htmlspecialchars($e['content_type']); + $url = htmlspecialchars($e['content_url']); + $length = $e['duration']; + + $tpl->setVariable('ARTICLE_ENCLOSURE_URL', $url); + $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', $type); + $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', $length); + + $tpl->addBlock('enclosure'); + } + + $tpl->addBlock('entry'); + } + + $tmp = ""; + + $tpl->addBlock('feed'); + $tpl->generateOutputToString($tmp); + + print $tmp; + } + + function getCategoryTitle($link, $cat_id) { + + if ($cat_id == -1) { + return __("Special"); + } else if ($cat_id == -2) { + return __("Labels"); + } else { + + $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE + id = '$cat_id'"); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "title"); + } else { + return "Uncategorized"; + } + } + } + + function sanitize_rss($link, $str, $force_strip_tags = false, $owner = false, $site_url = false) { + global $purifier; + + if (!$owner) $owner = $_SESSION["uid"]; + + $res = trim($str); if (!$res) return ''; + + // create global Purifier object if needed + if (!$purifier) { + require_once 'lib/htmlpurifier/library/HTMLPurifier.auto.php'; + + $config = HTMLPurifier_Config::createDefault(); + + $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title],ul,ol,li,h1,h2,h3,h4,s,object[classid|type|id|name|width|height|codebase],param[name|value],table,tr,td"; + + $config->set('HTML.SafeObject', true); + @$config->set('HTML', 'Allowed', $allowed); + $config->set('Output.FlashCompat', true); + $config->set('Attr.EnableID', true); + if (!defined('MOBILE_VERSION')) { + @$config->set('Cache', 'SerializerPath', CACHE_DIR . "/htmlpurifier"); + } else { + @$config->set('Cache', 'SerializerPath', "../" . CACHE_DIR . "/htmlpurifier"); + } + + $purifier = new HTMLPurifier($config); + } + + $res = $purifier->purify($res); + + if (get_pref($link, "STRIP_IMAGES", $owner)) { + $res = preg_replace('/]+>/is', '', $res); + } + + if (strpos($res, "href=") === false) + $res = rewrite_urls($res); + + $charset_hack = ' + + '; + + $res = trim($res); if (!$res) return ''; + + libxml_use_internal_errors(true); + + $doc = new DOMDocument(); + $doc->loadHTML($charset_hack . $res); + $xpath = new DOMXPath($doc); + + $entries = $xpath->query('(//a[@href]|//img[@src])'); + $br_inserted = 0; + + foreach ($entries as $entry) { + + if ($site_url) { + + if ($entry->hasAttribute('href')) + $entry->setAttribute('href', + rewrite_relative_url($site_url, $entry->getAttribute('href'))); + + if ($entry->hasAttribute('src')) + if (preg_match('/^image.php\?i=[a-z0-9]+$/', $entry->getAttribute('src')) == 0) + $entry->setAttribute('src', + rewrite_relative_url($site_url, $entry->getAttribute('src'))); + } + + if (strtolower($entry->nodeName) == "a") { + $entry->setAttribute("target", "_blank"); + } + + if (strtolower($entry->nodeName) == "img" && !$br_inserted) { + $br = $doc->createElement("br"); + + if ($entry->parentNode->nextSibling) { + $entry->parentNode->insertBefore($br, $entry->nextSibling); + $br_inserted = 1; + } + + } + } + + $node = $doc->getElementsByTagName('body')->item(0); + + return $doc->saveXML($node); + } + + /** + * Send by mail a digest of last articles. + * + * @param mixed $link The database connection. + * @param integer $limit The maximum number of articles by digest. + * @return boolean Return false if digests are not enabled. + */ + function send_headlines_digests($link, $limit = 100) { + + require_once 'lib/phpmailer/class.phpmailer.php'; + + if (!DIGEST_ENABLE) return false; + + $user_limit = DIGEST_EMAIL_LIMIT; + $days = 1; + + print "Sending digests, batch of max $user_limit users, days = $days, headline limit = $limit\n\n"; + + if (DB_TYPE == "pgsql") { + $interval_query = "last_digest_sent < NOW() - INTERVAL '$days days'"; + } else if (DB_TYPE == "mysql") { + $interval_query = "last_digest_sent < DATE_SUB(NOW(), INTERVAL $days DAY)"; + } + + $result = db_query($link, "SELECT id,email FROM ttrss_users + WHERE email != '' AND (last_digest_sent IS NULL OR $interval_query)"); + + while ($line = db_fetch_assoc($result)) { + + if (get_pref($link, 'DIGEST_ENABLE', $line['id'], false)) { + print "Sending digest for UID:" . $line['id'] . " - " . $line["email"] . " ... "; + + $do_catchup = get_pref($link, 'DIGEST_CATCHUP', $line['id'], false); + + $tuple = prepare_headlines_digest($link, $line["id"], $days, $limit); + $digest = $tuple[0]; + $headlines_count = $tuple[1]; + $affected_ids = $tuple[2]; + $digest_text = $tuple[3]; + + if ($headlines_count > 0) { + + $mail = new PHPMailer(); + + $mail->PluginDir = "lib/phpmailer/"; + $mail->SetLanguage("en", "lib/phpmailer/language/"); + + $mail->CharSet = "UTF-8"; + + $mail->From = DIGEST_FROM_ADDRESS; + $mail->FromName = DIGEST_FROM_NAME; + $mail->AddAddress($line["email"], $line["login"]); + + if (DIGEST_SMTP_HOST) { + $mail->Host = DIGEST_SMTP_HOST; + $mail->Mailer = "smtp"; + $mail->SMTPAuth = DIGEST_SMTP_LOGIN != ''; + $mail->Username = DIGEST_SMTP_LOGIN; + $mail->Password = DIGEST_SMTP_PASSWORD; + } + + $mail->IsHTML(true); + $mail->Subject = DIGEST_SUBJECT; + $mail->Body = $digest; + $mail->AltBody = $digest_text; + + $rc = $mail->Send(); + + if (!$rc) print "ERROR: " . $mail->ErrorInfo; + + print "RC=$rc\n"; + + if ($rc && $do_catchup) { + print "Marking affected articles as read...\n"; + catchupArticlesById($link, $affected_ids, 0, $line["id"]); + } + } else { + print "No headlines\n"; + } + + db_query($link, "UPDATE ttrss_users SET last_digest_sent = NOW() + WHERE id = " . $line["id"]); + } + } + + print "All done.\n"; + + } + + function prepare_headlines_digest($link, $user_id, $days = 1, $limit = 100) { + + require_once "lib/MiniTemplator.class.php"; + + $tpl = new MiniTemplator; + $tpl_t = new MiniTemplator; + + $tpl->readTemplateFromFile("templates/digest_template_html.txt"); + $tpl_t->readTemplateFromFile("templates/digest_template.txt"); + + $tpl->setVariable('CUR_DATE', date('Y/m/d')); + $tpl->setVariable('CUR_TIME', date('G:i')); + + $tpl_t->setVariable('CUR_DATE', date('Y/m/d')); + $tpl_t->setVariable('CUR_TIME', date('G:i')); + + $affected_ids = array(); + + if (DB_TYPE == "pgsql") { + $interval_query = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'"; + } else if (DB_TYPE == "mysql") { + $interval_query = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)"; + } + + $result = db_query($link, "SELECT ttrss_entries.title, + ttrss_feeds.title AS feed_title, + date_updated, + ttrss_user_entries.ref_id, + link, + SUBSTRING(content, 1, 120) AS excerpt, + ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated + FROM + ttrss_user_entries,ttrss_entries,ttrss_feeds + WHERE + ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id + AND include_in_digest = true + AND $interval_query + AND ttrss_user_entries.owner_uid = $user_id + AND unread = true + ORDER BY ttrss_feeds.title, date_updated DESC + LIMIT $limit"); + + $cur_feed_title = ""; + + $headlines_count = db_num_rows($result); + + $headlines = array(); + + while ($line = db_fetch_assoc($result)) { + array_push($headlines, $line); + } + + for ($i = 0; $i < sizeof($headlines); $i++) { + + $line = $headlines[$i]; + + array_push($affected_ids, $line["ref_id"]); + + $updated = make_local_datetime($link, $line['last_updated'], false, + $user_id); + + $tpl->setVariable('FEED_TITLE', $line["feed_title"]); + $tpl->setVariable('ARTICLE_TITLE', $line["title"]); + $tpl->setVariable('ARTICLE_LINK', $line["link"]); + $tpl->setVariable('ARTICLE_UPDATED', $updated); + $tpl->setVariable('ARTICLE_EXCERPT', + truncate_string(strip_tags($line["excerpt"]), 100)); + + $tpl->addBlock('article'); + + $tpl_t->setVariable('FEED_TITLE', $line["feed_title"]); + $tpl_t->setVariable('ARTICLE_TITLE', $line["title"]); + $tpl_t->setVariable('ARTICLE_LINK', $line["link"]); + $tpl_t->setVariable('ARTICLE_UPDATED', $updated); +// $tpl_t->setVariable('ARTICLE_EXCERPT', +// truncate_string(strip_tags($line["excerpt"]), 100)); + + $tpl_t->addBlock('article'); + + if ($headlines[$i]['feed_title'] != $headlines[$i+1]['feed_title']) { + $tpl->addBlock('feed'); + $tpl_t->addBlock('feed'); + } + + } + + $tpl->addBlock('digest'); + $tpl->generateOutputToString($tmp); + + $tpl_t->addBlock('digest'); + $tpl_t->generateOutputToString($tmp_t); + + return array($tmp, $headlines_count, $affected_ids, $tmp_t); + } + + function check_for_update($link) { + if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) { + $version_url = "http://tt-rss.org/version.php?ver=" . VERSION; + + $version_data = @fetch_file_contents($version_url); + + if ($version_data) { + $version_data = json_decode($version_data, true); + if ($version_data && $version_data['version']) { + + if (version_compare(VERSION, $version_data['version']) == -1) { + return $version_data; + } + } + } + } + return false; + } + + function markArticlesById($link, $ids, $cmode) { + + $tmp_ids = array(); + + foreach ($ids as $id) { + array_push($tmp_ids, "ref_id = '$id'"); + } + + $ids_qpart = join(" OR ", $tmp_ids); + + if ($cmode == 0) { + db_query($link, "UPDATE ttrss_user_entries SET + marked = false,last_read = NOW() + WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + } else if ($cmode == 1) { + db_query($link, "UPDATE ttrss_user_entries SET + marked = true + WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + } else { + db_query($link, "UPDATE ttrss_user_entries SET + marked = NOT marked,last_read = NOW() + WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + } + } + + function publishArticlesById($link, $ids, $cmode) { + + $tmp_ids = array(); + + foreach ($ids as $id) { + array_push($tmp_ids, "ref_id = '$id'"); + } + + $ids_qpart = join(" OR ", $tmp_ids); + + if ($cmode == 0) { + db_query($link, "UPDATE ttrss_user_entries SET + published = false,last_read = NOW() + WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + } else if ($cmode == 1) { + db_query($link, "UPDATE ttrss_user_entries SET + published = true + WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + } else { + db_query($link, "UPDATE ttrss_user_entries SET + published = NOT published,last_read = NOW() + WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]); + } + + if (PUBSUBHUBBUB_HUB) { + $rss_link = get_self_url_prefix() . + "/public.php?op=rss&id=-2&key=" . + get_feed_access_key($link, -2, false); + + $p = new Publisher(PUBSUBHUBBUB_HUB); + + $pubsub_result = $p->publish_update($rss_link); + } + } + + function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) { + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + if (count($ids) == 0) return; + + $tmp_ids = array(); + + foreach ($ids as $id) { + array_push($tmp_ids, "ref_id = '$id'"); + } + + $ids_qpart = join(" OR ", $tmp_ids); + + if ($cmode == 0) { + db_query($link, "UPDATE ttrss_user_entries SET + unread = false,last_read = NOW() + WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + } else if ($cmode == 1) { + db_query($link, "UPDATE ttrss_user_entries SET + unread = true + WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + } else { + db_query($link, "UPDATE ttrss_user_entries SET + unread = NOT unread,last_read = NOW() + WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + } + + /* update ccache */ + + $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries + WHERE ($ids_qpart) AND owner_uid = $owner_uid"); + + while ($line = db_fetch_assoc($result)) { + ccache_update($link, $line["feed_id"], $owner_uid); + } + } + + function catchupArticleById($link, $id, $cmode) { + + if ($cmode == 0) { + db_query($link, "UPDATE ttrss_user_entries SET + unread = false,last_read = NOW() + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } else if ($cmode == 1) { + db_query($link, "UPDATE ttrss_user_entries SET + unread = true + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } else { + db_query($link, "UPDATE ttrss_user_entries SET + unread = NOT unread,last_read = NOW() + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } + + $feed_id = getArticleFeed($link, $id); + ccache_update($link, $feed_id, $_SESSION["uid"]); + } + + function make_guid_from_title($title) { + return preg_replace("/[ \"\',.:;]/", "-", + mb_strtolower(strip_tags($title), 'utf-8')); + } + + function format_headline_subtoolbar($link, $feed_site_url, $feed_title, + $feed_id, $is_cat, $search, $match_on, + $search_mode, $view_mode, $error) { + + $page_prev_link = "viewFeedGoPage(-1)"; + $page_next_link = "viewFeedGoPage(1)"; + $page_first_link = "viewFeedGoPage(0)"; + + $catchup_page_link = "catchupPage()"; + $catchup_feed_link = "catchupCurrentFeed()"; + $catchup_sel_link = "catchupSelection()"; + + $archive_sel_link = "archiveSelection()"; + $delete_sel_link = "deleteSelection()"; + + $sel_all_link = "selectArticles('all')"; + $sel_unread_link = "selectArticles('unread')"; + $sel_none_link = "selectArticles('none')"; + $sel_inv_link = "selectArticles('invert')"; + + $tog_unread_link = "selectionToggleUnread()"; + $tog_marked_link = "selectionToggleMarked()"; + $tog_published_link = "selectionTogglePublished()"; + + $reply = "
    "; + + $reply .= __('Select:')." + ".__('All').", + ".__('Unread').", + ".__('Invert').", + ".__('None').""; + + $reply .= " "; + + $reply .= ""; + + $reply .= "
    "; + + $reply .= "
    "; + + if ($feed_site_url) { + $target = "target=\"_blank\""; + $reply .= "". + truncate_string($feed_title,30).""; + + if ($error) { + $reply .= " (Error)"; + } + + } else { + if ($feed_id < -10) { + $label_id = -11-$feed_id; + + $result = db_query($link, "SELECT fg_color, bg_color + FROM ttrss_labels2 WHERE id = '$label_id' AND owner_uid = " . + $_SESSION["uid"]); + + if (db_num_rows($result) != 0) { + $fg_color = db_fetch_result($result, 0, "fg_color"); + $bg_color = db_fetch_result($result, 0, "bg_color"); + + $reply .= ""; + $reply .= $feed_title; + $reply .= ""; + } else { + $reply .= $feed_title; + } + + } else { + $reply .= $feed_title; + } + } + + $reply .= " + + "; + + $reply .= "
    "; + + return $reply; + } + + function outputFeedList($link, $special = true) { + + $feedlist = array(); + + $enable_cats = get_pref($link, 'ENABLE_FEED_CATS'); + + $feedlist['identifier'] = 'id'; + $feedlist['label'] = 'name'; + $feedlist['items'] = array(); + + $owner_uid = $_SESSION["uid"]; + + /* virtual feeds */ + + if ($special) { + + if ($enable_cats) { + $cat_hidden = get_pref($link, "_COLLAPSED_SPECIAL"); + $cat = feedlist_init_cat($link, -1, $cat_hidden); + } else { + $cat['items'] = array(); + } + + foreach (array(-4, -3, -1, -2, 0) as $i) { + array_push($cat['items'], feedlist_init_feed($link, $i)); + } + + if ($enable_cats) { + array_push($feedlist['items'], $cat); + } else { + $feedlist['items'] = array_merge($feedlist['items'], $cat['items']); + } + + $result = db_query($link, "SELECT * FROM + ttrss_labels2 WHERE owner_uid = '$owner_uid' ORDER by caption"); + + if (db_num_rows($result) > 0) { + + if (get_pref($link, 'ENABLE_FEED_CATS')) { + $cat_hidden = get_pref($link, "_COLLAPSED_LABELS"); + $cat = feedlist_init_cat($link, -2, $cat_hidden); + } else { + $cat['items'] = array(); + } + + while ($line = db_fetch_assoc($result)) { + + $label_id = -$line['id'] - 11; + $count = getFeedUnread($link, $label_id); + + $feed = feedlist_init_feed($link, $label_id, false, $count); + + $feed['fg_color'] = $line['fg_color']; + $feed['bg_color'] = $line['bg_color']; + + array_push($cat['items'], $feed); + } + + if ($enable_cats) { + array_push($feedlist['items'], $cat); + } else { + $feedlist['items'] = array_merge($feedlist['items'], $cat['items']); + } + } + } + +/* if (get_pref($link, 'ENABLE_FEED_CATS')) { + if (get_pref($link, "FEEDS_SORT_BY_UNREAD")) { + $order_by_qpart = "order_id,category,unread DESC,title"; + } else { + $order_by_qpart = "order_id,category,title"; + } + } else { + if (get_pref($link, "FEEDS_SORT_BY_UNREAD")) { + $order_by_qpart = "unread DESC,title"; + } else { + $order_by_qpart = "title"; + } + } */ + + /* real feeds */ + + if ($enable_cats) + $order_by_qpart = "ttrss_feed_categories.order_id,category, + ttrss_feeds.order_id,title"; + else + $order_by_qpart = "title"; + + $age_qpart = getMaxAgeSubquery(); + + $query = "SELECT ttrss_feeds.id, ttrss_feeds.title, + ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated_noms, + cat_id,last_error, + ttrss_feed_categories.title AS category, + ttrss_feed_categories.collapsed, + value AS unread + FROM ttrss_feeds LEFT JOIN ttrss_feed_categories + ON (ttrss_feed_categories.id = cat_id) + LEFT JOIN ttrss_counters_cache + ON + (ttrss_feeds.id = feed_id) + WHERE + ttrss_feeds.owner_uid = '$owner_uid' + ORDER BY $order_by_qpart"; + + $result = db_query($link, $query); + + $actid = $_REQUEST["actid"]; + + if (db_num_rows($result) > 0) { + + $category = ""; + + if (!$enable_cats) + $cat['items'] = array(); + else + $cat = false; + + while ($line = db_fetch_assoc($result)) { + + $feed = htmlspecialchars(trim($line["title"])); + + if (!$feed) $feed = "[Untitled]"; + + $feed_id = $line["id"]; + $unread = $line["unread"]; + + $cat_id = $line["cat_id"]; + $tmp_category = $line["category"]; + if (!$tmp_category) $tmp_category = __("Uncategorized"); + + if ($category != $tmp_category && $enable_cats) { + + $category = $tmp_category; + + $collapsed = sql_bool_to_bool($line["collapsed"]); + + // workaround for NULL category + if ($category == __("Uncategorized")) { + $collapsed = get_pref($link, "_COLLAPSED_UNCAT"); + } + + if ($cat) array_push($feedlist['items'], $cat); + + $cat = feedlist_init_cat($link, $cat_id, $collapsed); + } + + $updated = make_local_datetime($link, $line["updated_noms"], false); + + array_push($cat['items'], feedlist_init_feed($link, $feed_id, + $feed, $unread, $line['last_error'], $updated)); + } + + if ($enable_cats) { + array_push($feedlist['items'], $cat); + } else { + $feedlist['items'] = array_merge($feedlist['items'], $cat['items']); + } + + } + + return $feedlist; + } + + function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) { + + global $memcache; + + $a_id = db_escape_string($id); + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + $query = "SELECT DISTINCT tag_name, + owner_uid as owner FROM + ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE + ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name"; + + $obj_id = md5("TAGS:$owner_uid:$id"); + $tags = array(); + + if ($memcache && $obj = $memcache->get($obj_id)) { + $tags = $obj; + } else { + /* check cache first */ + + if ($tag_cache === false) { + $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + + $tag_cache = db_fetch_result($result, 0, "tag_cache"); + } + + if ($tag_cache) { + $tags = explode(",", $tag_cache); + } else { + + /* do it the hard way */ + + $tmp_result = db_query($link, $query); + + while ($tmp_line = db_fetch_assoc($tmp_result)) { + array_push($tags, $tmp_line["tag_name"]); + } + + /* update the cache */ + + $tags_str = db_escape_string(join(",", $tags)); + + db_query($link, "UPDATE ttrss_user_entries + SET tag_cache = '$tags_str' WHERE ref_id = '$id' + AND owner_uid = " . $_SESSION["uid"]); + } + + if ($memcache) $memcache->add($obj_id, $tags, 0, 3600); + } + + return $tags; + } + + function trim_array($array) { + $tmp = $array; + array_walk($tmp, 'trim'); + return $tmp; + } + + function tag_is_valid($tag) { + if ($tag == '') return false; + if (preg_match("/^[0-9]*$/", $tag)) return false; + if (mb_strlen($tag) > 250) return false; + + if (function_exists('iconv')) { + $tag = iconv("utf-8", "utf-8", $tag); + } + + if (!$tag) return false; + + return true; + } + + function render_login_form($link, $mobile = 0) { + switch ($mobile) { + case 0: + require_once "login_form.php"; + break; + case 1: + require_once "mobile/login_form.php"; + break; + case 2: + require_once "mobile/classic/login_form.php"; + } + } + + // from http://developer.apple.com/internet/safari/faq.html + function no_cache_incantation() { + header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :) + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified + header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1 + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); // HTTP/1.0 + } + + function format_warning($msg, $id = "") { + global $link; + return "
    + $msg
    "; + } + + function format_notice($msg, $id = "") { + global $link; + return "
    + $msg
    "; + } + + function format_error($msg, $id = "") { + global $link; + return "
    + $msg
    "; + } + + function print_notice($msg) { + return print format_notice($msg); + } + + function print_warning($msg) { + return print format_warning($msg); + } + + function print_error($msg) { + return print format_error($msg); + } + + + function T_sprintf() { + $args = func_get_args(); + return vsprintf(__(array_shift($args)), $args); + } + + function format_inline_player($link, $url, $ctype) { + + $entry = ""; + + if (strpos($ctype, "audio/") === 0) { + + if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false || + strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false || + strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) { + + $id = 'AUDIO-' . uniqid(); + + $entry .= ""; + + $entry .= "".__("Play").""; + + } else { + + $entry .= " + + "; + } + } + + $filename = substr($url, strrpos($url, "/")+1); + + $entry .= " " . + $filename . " (" . $ctype . ")" . ""; + + return $entry; + } + + function format_article($link, $id, $mark_as_read = true, $zoom_mode = false) { + + $rv = array(); + + $rv['id'] = $id; + + /* we can figure out feed_id from article id anyway, why do we + * pass feed_id here? let's ignore the argument :( */ + + $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries + WHERE ref_id = '$id'"); + + $feed_id = (int) db_fetch_result($result, 0, "feed_id"); + + $rv['feed_id'] = $feed_id; + + //if (!$zoom_mode) { print "
    "; + } else { + $feed_icon = " "; + } + + $feed_site_url = $line['site_url']; + + $num_comments = $line["num_comments"]; + $entry_comments = ""; + + if ($num_comments > 0) { + if ($line["comments"]) { + $comments_url = $line["comments"]; + } else { + $comments_url = $line["link"]; + } + $entry_comments = "$num_comments comments"; + } else { + if ($line["comments"] && $line["link"] != $line["comments"]) { + $entry_comments = "comments"; + } + } + + if ($zoom_mode) { + header("Content-Type: text/html"); + $rv['content'] .= " + + Tiny Tiny RSS - ".$line["title"]." + + "; + } + + $rv['content'] .= "
    " . + truncate_string(strip_tags($line['title']), 15) . "
    "; + + $rv['content'] .= "
    "; + + $rv['content'] .= "
    "; + + $entry_author = $line["author"]; + + if ($entry_author) { + $entry_author = __(" - ") . $entry_author; + } + + $parsed_updated = make_local_datetime($link, $line["updated"], true, + false, true); + + $rv['content'] .= "
    $parsed_updated
    "; + + if ($line["link"]) { + $rv['content'] .= ""; + } else { + $rv['content'] .= "
    " . $line["title"] . "$entry_author
    "; + } + + $tag_cache = $line["tag_cache"]; + + if (!$tag_cache) + $tags = get_article_tags($link, $id); + else + $tags = explode(",", $tag_cache); + + $tags_str = format_tags_string($tags, $id); + $tags_str_full = join(", ", $tags); + + if (!$tags_str_full) $tags_str_full = __("no tags"); + + if (!$entry_comments) $entry_comments = " "; # placeholder + + $rv['content'] .= "
    + Tags "; + + if (!$zoom_mode) { + $rv['content'] .= "$tags_str + (+)"; + + $rv['content'] .= "
    $tags_str_full
    "; + + $rv['content'] .= "Zoom"; + + //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES); + + $rv['content'] .= "PubNote"; + + if (DIGEST_ENABLE) { + $rv['content'] .= "Zoom"; + } + + if (ENABLE_TWEET_BUTTON) { + $rv['content'] .= "Zoom"; + } + + $rv['content'] .= "Zoom"; + + $rv['content'] .= "Zoom"; + + } else { + $tags_str = strip_tags($tags_str); + $rv['content'] .= "$tags_str"; + } + $rv['content'] .= "
    "; + $rv['content'] .= "
    $entry_comments
    "; + + if ($line["orig_feed_id"]) { + + $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds + WHERE id = ".$line["orig_feed_id"]); + + if (db_num_rows($tmp_result) != 0) { + + $rv['content'] .= "
    "; + $rv['content'] .= __("Originally from:"); + + $rv['content'] .= " "; + + $tmp_line = db_fetch_assoc($tmp_result); + + $rv['content'] .= "" . + $tmp_line['title'] . ""; + + $rv['content'] .= " "; + + $rv['content'] .= ""; + $rv['content'] .= ""; + + $rv['content'] .= "
    "; + } + } + + $rv['content'] .= "
    "; + + $rv['content'] .= "
    "; + if ($line['note']) { + $rv['content'] .= format_article_note($id, $line['note']); + } + $rv['content'] .= "
    "; + + $rv['content'] .= ""; + + $rv['content'] .= "
    "; + + $article_content = sanitize_rss($link, $line["content"], false, false, + $feed_site_url); + + $rv['content'] .= $article_content; + + $rv['content'] .= format_article_enclosures($link, $id, + $always_display_enclosures, $article_content); + + $rv['content'] .= "
    "; + + $rv['content'] .= "
    "; + + } + + if ($zoom_mode) { + $rv['content'] .= " +
    +
    "; + $rv['content'] .= ""; + } + + return $rv; + + } + + function format_headlines_list($link, $feed, $subop, $view_mode, $limit, $cat_view, + $next_unread_feed, $offset, $vgr_last_feed = false, + $override_order = false) { + + $disable_cache = false; + + $reply = array(); + + $timing_info = getmicrotime(); + + $topmost_article_ids = array(); + + if (!$offset) $offset = 0; + if ($subop == "undefined") $subop = ""; + + $subop_split = explode(":", $subop); + +/* if ($subop == "CatchupSelected") { + $ids = explode(",", db_escape_string($_REQUEST["ids"])); + $cmode = sprintf("%d", $_REQUEST["cmode"]); + + catchupArticlesById($link, $ids, $cmode); + } */ + + if ($subop == "ForceUpdate" && $feed && is_numeric($feed) > 0) { + update_rss_feed($link, $feed, true); + } + + if ($subop == "MarkAllRead") { + catchup_feed($link, $feed, $cat_view); + + if (get_pref($link, 'ON_CATCHUP_SHOW_NEXT_FEED')) { + if ($next_unread_feed) { + $feed = $next_unread_feed; + } + } + } + + if ($subop_split[0] == "MarkAllReadGR") { + catchup_feed($link, $subop_split[1], false); + } + + // FIXME: might break tag display? + + if (is_numeric($feed) && $feed > 0 && !$cat_view) { + $result = db_query($link, + "SELECT id FROM ttrss_feeds WHERE id = '$feed' LIMIT 1"); + + if (db_num_rows($result) == 0) { + $reply['content'] = "
    ".__('Feed not found.')."
    "; + } + } + + if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { + + $result = db_query($link, "SELECT rtl_content FROM ttrss_feeds + WHERE id = '$feed' AND owner_uid = " . $_SESSION["uid"]); + + if (db_num_rows($result) == 1) { + $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content")); + } else { + $rtl_content = false; + } + + if ($rtl_content) { + $rtl_tag = "dir=\"RTL\""; + } else { + $rtl_tag = ""; + } + } else { + $rtl_tag = ""; + $rtl_content = false; + } + + @$search = db_escape_string($_REQUEST["query"]); + + if ($search) { + $disable_cache = true; + } + + @$search_mode = db_escape_string($_REQUEST["search_mode"]); + @$match_on = db_escape_string($_REQUEST["match_on"]); + + if (!$match_on) { + $match_on = "both"; + } + + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H0", $timing_info); + +// error_log("format_headlines_list: [" . $feed . "] subop [" . $subop . "]"); + if( $search_mode == '' && $subop != '' ){ + $search_mode = $subop; + } +// error_log("search_mode: " . $search_mode); + $qfh_ret = queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, + $search, $search_mode, $match_on, $override_order, $offset); + + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H1", $timing_info); + + $result = $qfh_ret[0]; + $feed_title = $qfh_ret[1]; + $feed_site_url = $qfh_ret[2]; + $last_error = $qfh_ret[3]; + + $vgroup_last_feed = $vgr_last_feed; + +// if (!$offset) { + + if (db_num_rows($result) > 0) { + $reply['toolbar'] = format_headline_subtoolbar($link, $feed_site_url, + $feed_title, + $feed, $cat_view, $search, $match_on, $search_mode, $view_mode, + $last_error); + } +// } + + $headlines_count = db_num_rows($result); + + if (db_num_rows($result) > 0) { + + $lnum = $offset; + + $num_unread = 0; + $cur_feed_title = ''; + + $fresh_intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE") * 60 * 60; + + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PS", $timing_info); + + while ($line = db_fetch_assoc($result)) { + + $class = ($lnum % 2) ? "even" : "odd"; + + $id = $line["id"]; + $feed_id = $line["feed_id"]; + $label_cache = $line["label_cache"]; + $labels = false; + + if ($label_cache) { + $label_cache = json_decode($label_cache, true); + + if ($label_cache) { + if ($label_cache["no-labels"] == 1) + $labels = array(); + else + $labels = $label_cache; + } + } + + if (!is_array($labels)) $labels = get_article_labels($link, $id); + + $labels_str = ""; + $labels_str .= format_article_labels($labels, $id); + $labels_str .= ""; + + if (count($topmost_article_ids) < 3) { + array_push($topmost_article_ids, $id); + } + + if ($line["last_read"] == "" && !sql_bool_to_bool($line["unread"])) { + + $update_pic = "\"Updated\""; + } else { + $update_pic = "\"Updated\""; + } + + if (sql_bool_to_bool($line["unread"]) && + time() - strtotime($line["updated_noms"]) < $fresh_intl) { + + $update_pic = "\"Fresh\""; + } + + if ($line["unread"] == "t" || $line["unread"] == "1") { + $class .= " Unread"; + ++$num_unread; + $is_unread = true; + } else { + $is_unread = false; + } + + if ($line["marked"] == "t" || $line["marked"] == "1") { + $marked_pic = "\"Unstar"; + } else { + $marked_pic = "\"Star"; + } + + if ($line["published"] == "t" || $line["published"] == "1") { + $published_pic = "\"Unpublish"; + } else { + $published_pic = "\"Publish"; + } + +# $content_link = "" . +# $line["title"] . ""; + +# $content_link = "" . +# $line["title"] . ""; + +# $content_link = "" . +# $line["title"] . ""; + + $updated_fmt = make_local_datetime($link, $line["updated_noms"], false); + + if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) { + $content_preview = truncate_string(strip_tags($line["content_preview"]), + 100); + } + + $score = $line["score"]; + + $score_pic = theme_image($link, + "images/" . get_score_pic($score)); + +/* $score_title = __("(Click to change)"); + $score_pic = ""; */ + + $score_pic = ""; + + if ($score > 500) { + $hlc_suffix = "H"; + } else if ($score < -100) { + $hlc_suffix = "L"; + } else { + $hlc_suffix = ""; + } + + $entry_author = $line["author"]; + + if ($entry_author) { + $entry_author = " - $entry_author"; + } + + $has_feed_icon = feed_has_icon($feed_id); + + if ($has_feed_icon) { + $feed_icon_img = "\"\""; + } else { + $feed_icon_img = "\"\""; + } + + if (!get_pref($link, 'COMBINED_DISPLAY_MODE')) { + + if (get_pref($link, 'VFEED_GROUP_BY_FEED')) { + if ($feed_id != $vgroup_last_feed && $line["feed_title"]) { + + $cur_feed_title = $line["feed_title"]; + $vgroup_last_feed = $feed_id; + + $cur_feed_title = htmlspecialchars($cur_feed_title); + + $vf_catchup_link = "(".__('mark as read').")"; + + $reply['content'] .= "
    ". + "
    $feed_icon_img
    ". + "". + $line["feed_title"]." $vf_catchup_link
    "; + + } + } + + $mouseover_attrs = "onmouseover='postMouseIn($id)' + onmouseout='postMouseOut($id)'"; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    $update_pic
    "; + + $reply['content'] .= "
    "; + + $reply['content'] .= ""; + + $reply['content'] .= "$marked_pic"; + $reply['content'] .= "$published_pic"; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + $reply['content'] .= "" . + truncate_string($line["title"], 200); + + if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) { + if ($content_preview) { + $reply['content'] .= " - $content_preview"; + } + } + + $reply['content'] .= ""; + + $reply['content'] .= $labels_str; + + if (!get_pref($link, 'VFEED_GROUP_BY_FEED') && + defined('_SHOW_FEED_TITLE_IN_VFEEDS')) { + if (@$line["feed_title"]) { + $reply['content'] .= " + (". + $line["feed_title"].") + "; + } + } + + $reply['content'] .= "
    "; + + $reply['content'] .= "$updated_fmt"; + $reply['content'] .= "
    "; + + $reply['content'] .= $score_pic; + + if ($line["feed_title"] && !get_pref($link, 'VFEED_GROUP_BY_FEED')) { + + $reply['content'] .= " + $feed_icon_img"; + } + + $reply['content'] .= "
    "; + $reply['content'] .= "
    "; + + } else { + + if (get_pref($link, 'VFEED_GROUP_BY_FEED') && $line["feed_title"]) { + if ($feed_id != $vgroup_last_feed) { + + $cur_feed_title = $line["feed_title"]; + $vgroup_last_feed = $feed_id; + + $cur_feed_title = htmlspecialchars($cur_feed_title); + + $vf_catchup_link = "(".__('mark as read').")"; + + $has_feed_icon = feed_has_icon($feed_id); + + if ($has_feed_icon) { + $feed_icon_img = "\"\""; + } else { + //$feed_icon_img = "\"\""; + } + + $reply['content'] .= "
    ". + "
    $feed_icon_img
    ". + "". + $line["feed_title"]." $vf_catchup_link
    "; + } + } + + $expand_cdm = get_pref($link, 'CDM_EXPANDED'); + + $mouseover_attrs = "onmouseover='postMouseIn($id)' + onmouseout='postMouseOut($id)'"; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + $reply['content'] .= ""; + + $reply['content'] .= "$marked_pic"; + $reply['content'] .= "$published_pic"; + + $reply['content'] .= "
    "; + + $reply['content'] .= " + ". + truncate_string($line["title"], 100) . + " $entry_author"; + + $reply['content'] .= $labels_str; + + if (!get_pref($link, 'VFEED_GROUP_BY_FEED') && + defined('_SHOW_FEED_TITLE_IN_VFEEDS')) { + if (@$line["feed_title"]) { + $reply['content'] .= " + (". + $line["feed_title"].") + "; + } + } + + if (!$expand_cdm) + $content_hidden = "style=\"display : none\""; + else + $excerpt_hidden = "style=\"display : none\""; + + $reply['content'] .= " - $content_preview"; + + $reply['content'] .= ""; + + $reply['content'] .= "
    "; + $reply['content'] .= "$updated_fmt"; + $reply['content'] .= "$score_pic"; + + if (!get_pref($link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) { + $reply['content'] .= "$feed_icon_img"; + } + $reply['content'] .= "
    $update_pic
    "; + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + if ($line["orig_feed_id"]) { + + $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds + WHERE id = ".$line["orig_feed_id"]); + + if (db_num_rows($tmp_result) != 0) { + + $reply['content'] .= "
    "; + $reply['content'] .= __("Originally from:"); + + $reply['content'] .= " "; + + $tmp_line = db_fetch_assoc($tmp_result); + + $reply['content'] .= "" . + $tmp_line['title'] . ""; + + $reply['content'] .= " "; + + $reply['content'] .= ""; + $reply['content'] .= ""; + + $reply['content'] .= "
    "; + } + } + + $feed_site_url = $line["site_url"]; + + $article_content = sanitize_rss($link, $line["content_preview"], + false, false, $feed_site_url); + + $reply['content'] .= "
    "; + if ($line['note']) { + $reply['content'] .= format_article_note($id, $line['note']); + } + $reply['content'] .= "
    "; + + $reply['content'] .= ""; + $reply['content'] .= $expand_cdm ? $article_content : ''; + $reply['content'] .= ""; + +/* $tmp_result = db_query($link, "SELECT always_display_enclosures FROM + ttrss_feeds WHERE id = ". + (($line['feed_id'] == null) ? $line['orig_feed_id'] : + $line['feed_id'])." AND owner_uid = ".$_SESSION["uid"]); + + $always_display_enclosures = sql_bool_to_bool(db_fetch_result($tmp_result, + 0, "always_display_enclosures")); */ + + $always_display_enclosures = sql_bool_to_bool($line["always_display_enclosures"]); + + $reply['content'] .= format_article_enclosures($link, $id, $always_display_enclosures, + $article_content); + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + $tag_cache = $line["tag_cache"]; + + $tags_str = format_tags_string( + get_article_tags($link, $id, $_SESSION["uid"], $tag_cache), + $id); + + $reply['content'] .= "Tags + $tags_str + (+)"; + + $num_comments = $line["num_comments"]; + $entry_comments = ""; + + if ($num_comments > 0) { + if ($line["comments"]) { + $comments_url = $line["comments"]; + } else { + $comments_url = $line["link"]; + } + $entry_comments = "$num_comments comments"; + } else { + if ($line["comments"] && $line["link"] != $line["comments"]) { + $entry_comments = "comments"; + } + } + + if ($entry_comments) $reply['content'] .= " ($entry_comments)"; + + $reply['content'] .= "
    "; + + $reply['content'] .= "Zoom"; + + //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES); + + $reply['content'] .= "PubNote"; + + if (DIGEST_ENABLE) { + $reply['content'] .= "Zoom"; + } + + if (ENABLE_TWEET_BUTTON) { + $reply['content'] .= "Zoom"; + } + + $reply['content'] .= "Zoom"; + + $reply['content'] .= "Dismiss"; + + $reply['content'] .= "
    "; + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + $reply['content'] .= "
    "; + + } + + ++$lnum; + } + + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("PE", $timing_info); + + } else { + $message = ""; + + switch ($view_mode) { + case "unread": + $message = __("No unread articles found to display."); + break; + case "updated": + $message = __("No updated articles found to display."); + break; + case "marked": + $message = __("No starred articles found to display."); + break; + default: + if ($feed < -10) { + $message = __("No articles found to display. You can assign articles to labels manually (see the Actions menu above) or use a filter."); + } else { + $message = __("No articles found to display."); + } + } + + if (!$offset && $message) { + $reply['content'] .= "
    $message"; + + $reply['content'] .= "

    "; + + $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds + WHERE owner_uid = " . $_SESSION['uid']); + + $last_updated = db_fetch_result($result, 0, "last_updated"); + $last_updated = make_local_datetime($link, $last_updated, false); + + $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); + + $result = db_query($link, "SELECT COUNT(id) AS num_errors + FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); + + $num_errors = db_fetch_result($result, 0, "num_errors"); + + if ($num_errors > 0) { + $reply['content'] .= "
    "; + $reply['content'] .= "". + __('Some feeds have update errors (click for details)').""; + } + $reply['content'] .= "

    "; + } + } + + if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H2", $timing_info); + + return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, + $vgroup_last_feed, $reply); + } + +// from here: http://www.roscripts.com/Create_tag_cloud-71.html + + function printTagCloud($link) { + + $query = "SELECT tag_name, COUNT(post_int_id) AS count + FROM ttrss_tags WHERE owner_uid = ".$_SESSION["uid"]." + GROUP BY tag_name ORDER BY count DESC LIMIT 50"; + + $result = db_query($link, $query); + + $tags = array(); + + while ($line = db_fetch_assoc($result)) { + $tags[$line["tag_name"]] = $line["count"]; + } + + if( count($tags) == 0 ){ return; } + + ksort($tags); + + $max_size = 32; // max font size in pixels + $min_size = 11; // min font size in pixels + + // largest and smallest array values + $max_qty = max(array_values($tags)); + $min_qty = min(array_values($tags)); + + // find the range of values + $spread = $max_qty - $min_qty; + if ($spread == 0) { // we don't want to divide by zero + $spread = 1; + } + + // set the font-size increment + $step = ($max_size - $min_size) / ($spread); + + // loop through the tag array + foreach ($tags as $key => $value) { + // calculate font-size + // find the $value in excess of $min_qty + // multiply by the font-size increment ($size) + // and add the $min_size set above + $size = round($min_size + (($value - $min_qty) * $step)); + + $key_escaped = str_replace("'", "\\'", $key); + + echo "' . $key . ' '; + } + } + + function print_checkpoint($n, $s) { + $ts = getmicrotime(); + echo sprintf("", $ts - $s); + return $ts; + } + + function sanitize_tag($tag) { + $tag = trim($tag); + + $tag = mb_strtolower($tag, 'utf-8'); + + $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag); + +// $tag = str_replace('"', "", $tag); +// $tag = str_replace("+", " ", $tag); + $tag = str_replace("technorati tag: ", "", $tag); + + return $tag; + } + + function get_self_url_prefix() { + return SELF_URL_PATH; + } + + function opml_publish_url($link){ + + $url_path = get_self_url_prefix(); + $url_path .= "/opml.php?op=publish&key=" . + get_feed_access_key($link, 'OPML:Publish', false, $_SESSION["uid"]); + + return $url_path; + } + + /** + * Purge a feed contents, marked articles excepted. + * + * @param mixed $link The database connection. + * @param integer $id The id of the feed to purge. + * @return void + */ + function clear_feed_articles($link, $id) { + + if ($id != 0) { + $result = db_query($link, "DELETE FROM ttrss_user_entries + WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]); + } else { + $result = db_query($link, "DELETE FROM ttrss_user_entries + WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]); + } + + $result = db_query($link, "DELETE FROM ttrss_entries WHERE + (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0"); + + ccache_update($link, $id, $_SESSION['uid']); + } // function clear_feed_articles + + /** + * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI. + * + * @return string The Mozilla Firefox feed adding URL. + */ + function add_feed_url() { + //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); + + $url_path = get_self_url_prefix() . + "/backend.php?op=pref-feeds&quiet=1&subop=add&feed_url=%s"; + return $url_path; + } // function add_feed_url + + /** + * Encrypt a password in SHA1. + * + * @param string $pass The password to encrypt. + * @param string $login A optionnal login. + * @return string The encrypted password. + */ + function encrypt_password($pass, $login = '') { + if ($login) { + return "SHA1X:" . sha1("$login:$pass"); + } else { + return "SHA1:" . sha1($pass); + } + } // function encrypt_password + + /** + * Update a feed batch. + * Used by daemons to update n feeds by run. + * Only update feed needing a update, and not being processed + * by another process. + * + * @param mixed $link Database link + * @param integer $limit Maximum number of feeds in update batch. Default to DAEMON_FEED_LIMIT. + * @param boolean $from_http Set to true if you call this function from http to disable cli specific code. + * @param boolean $debug Set to false to disable debug output. Default to true. + * @return void + */ + function update_daemon_common($link, $limit = DAEMON_FEED_LIMIT, $from_http = false, $debug = true) { + // Process all other feeds using last_updated and interval parameters + + // Test if the user has loggued in recently. If not, it does not update its feeds. + if (DAEMON_UPDATE_LOGIN_LIMIT > 0) { + if (DB_TYPE == "pgsql") { + $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".DAEMON_UPDATE_LOGIN_LIMIT." days'"; + } else { + $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL ".DAEMON_UPDATE_LOGIN_LIMIT." DAY)"; + } + } else { + $login_thresh_qpart = ""; + } + + // Test if the feed need a update (update interval exceded). + if (DB_TYPE == "pgsql") { + $update_limit_qpart = "AND (( + ttrss_feeds.update_interval = 0 + AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL) + ) OR ( + ttrss_feeds.update_interval > 0 + AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL) + ) OR ttrss_feeds.last_updated IS NULL)"; + } else { + $update_limit_qpart = "AND (( + ttrss_feeds.update_interval = 0 + AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE) + ) OR ( + ttrss_feeds.update_interval > 0 + AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE) + ) OR ttrss_feeds.last_updated IS NULL)"; + } + + // Test if feed is currently being updated by another process. + if (DB_TYPE == "pgsql") { + $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '5 minutes')"; + } else { + $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))"; + } + + // Test if there is a limit to number of updated feeds + $query_limit = ""; + if($limit) $query_limit = sprintf("LIMIT %d", $limit); + + $random_qpart = sql_random_function(); + + // We search for feed needing update. + $result = db_query($link, "SELECT ttrss_feeds.feed_url,ttrss_feeds.id, ttrss_feeds.owner_uid, + ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated, + ttrss_feeds.update_interval + FROM + ttrss_feeds, ttrss_users, ttrss_user_prefs + WHERE + ttrss_feeds.owner_uid = ttrss_users.id + AND ttrss_users.id = ttrss_user_prefs.owner_uid + AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL' + $login_thresh_qpart $update_limit_qpart + $updstart_thresh_qpart + ORDER BY $random_qpart $query_limit"); + + $user_prefs_cache = array(); + + if($debug) _debug(sprintf("Scheduled %d feeds to update...\n", db_num_rows($result))); + + // Here is a little cache magic in order to minimize risk of double feed updates. + $feeds_to_update = array(); + while ($line = db_fetch_assoc($result)) { + $feeds_to_update[$line['id']] = $line; + } + + // We update the feed last update started date before anything else. + // There is no lag due to feed contents downloads + // It prevent an other process to update the same feed. + $feed_ids = array_keys($feeds_to_update); + if($feed_ids) { + db_query($link, sprintf("UPDATE ttrss_feeds SET last_update_started = NOW() + WHERE id IN (%s)", implode(',', $feed_ids))); + } + + // For each feed, we call the feed update function. + while ($line = array_pop($feeds_to_update)) { + + if($debug) _debug("Feed: " . $line["feed_url"] . ", " . $line["last_updated"]); + + update_rss_feed($link, $line["id"], true); + + sleep(1); // prevent flood (FIXME make this an option?) + } + + // Send feed digests by email if needed. + if (DAEMON_SENDS_DIGESTS) send_headlines_digests($link); + + } // function update_daemon_common + + function sanitize_article_content($text) { + # we don't support CDATA sections in articles, they break our own escaping + $text = preg_replace("/\[\[CDATA/", "", $text); + $text = preg_replace("/\]\]\>/", "", $text); + return $text; + } + + function load_filters($link, $feed, $owner_uid, $action_id = false) { + $filters = array(); + + global $memcache; + + $obj_id = md5("FILTER:$feed:$owner_uid:$action_id"); + + if ($memcache && $obj = $memcache->get($obj_id)) { + + return $obj; + + } else { + + if ($action_id) $ftype_query_part = "action_id = '$action_id' AND"; + + $result = db_query($link, "SELECT reg_exp, + ttrss_filter_types.name AS name, + ttrss_filter_actions.name AS action, + inverse, + action_param, + filter_param + FROM ttrss_filters,ttrss_filter_types,ttrss_filter_actions WHERE + enabled = true AND + $ftype_query_part + owner_uid = $owner_uid AND + ttrss_filter_types.id = filter_type AND + ttrss_filter_actions.id = action_id AND + (feed_id IS NULL OR feed_id = '$feed') ORDER BY reg_exp"); + + while ($line = db_fetch_assoc($result)) { + if (!$filters[$line["name"]]) $filters[$line["name"]] = array(); + $filter["reg_exp"] = $line["reg_exp"]; + $filter["action"] = $line["action"]; + $filter["action_param"] = $line["action_param"]; + $filter["filter_param"] = $line["filter_param"]; + $filter["inverse"] = sql_bool_to_bool($line["inverse"]); + + array_push($filters[$line["name"]], $filter); + } + + if ($memcache) $memcache->add($obj_id, $filters, 0, 3600*8); + + return $filters; + } + } + + function get_score_pic($score) { + if ($score > 100) { + return "score_high.png"; + } else if ($score > 0) { + return "score_half_high.png"; + } else if ($score < -100) { + return "score_low.png"; + } else if ($score < 0) { + return "score_half_low.png"; + } else { + return "score_neutral.png"; + } + } + + function feed_has_icon($id) { + return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0; + } + + function init_connection($link) { + if (DB_TYPE == "pgsql") { + pg_query($link, "set client_encoding = 'UTF-8'"); + pg_set_client_encoding("UNICODE"); + pg_query($link, "set datestyle = 'ISO, european'"); + pg_query($link, "set TIME ZONE 0"); + } else { + db_query($link, "SET time_zone = '+0:0'"); + + if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) { + db_query($link, "SET NAMES " . MYSQL_CHARSET); + // db_query($link, "SET CHARACTER SET " . MYSQL_CHARSET); + } + } + } + + function update_feedbrowser_cache($link) { + + $result = db_query($link, "SELECT feed_url, site_url, title, COUNT(id) AS subscribers + FROM ttrss_feeds WHERE (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf + WHERE tf.feed_url = ttrss_feeds.feed_url + AND (private IS true OR auth_login != '' OR auth_pass != '' OR feed_url LIKE '%:%@%/%')) + GROUP BY feed_url, site_url, title ORDER BY subscribers DESC LIMIT 1000"); + + db_query($link, "BEGIN"); + + db_query($link, "DELETE FROM ttrss_feedbrowser_cache"); + + $count = 0; + + while ($line = db_fetch_assoc($result)) { + $subscribers = db_escape_string($line["subscribers"]); + $feed_url = db_escape_string($line["feed_url"]); + $title = db_escape_string($line["title"]); + $site_url = db_escape_string($line["site_url"]); + + $tmp_result = db_query($link, "SELECT subscribers FROM + ttrss_feedbrowser_cache WHERE feed_url = '$feed_url'"); + + if (db_num_rows($tmp_result) == 0) { + + db_query($link, "INSERT INTO ttrss_feedbrowser_cache + (feed_url, site_url, title, subscribers) VALUES ('$feed_url', + '$site_url', '$title', '$subscribers')"); + + ++$count; + + } + + } + + db_query($link, "COMMIT"); + + return $count; + + } + + /* function ccache_zero($link, $feed_id, $owner_uid) { + db_query($link, "UPDATE ttrss_counters_cache SET + value = 0, updated = NOW() WHERE + feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); + } */ + + function ccache_zero_all($link, $owner_uid) { + db_query($link, "UPDATE ttrss_counters_cache SET + value = 0 WHERE owner_uid = '$owner_uid'"); + + db_query($link, "UPDATE ttrss_cat_counters_cache SET + value = 0 WHERE owner_uid = '$owner_uid'"); + } + + function ccache_remove($link, $feed_id, $owner_uid, $is_cat = false) { + + if (!$is_cat) { + $table = "ttrss_counters_cache"; + } else { + $table = "ttrss_cat_counters_cache"; + } + + db_query($link, "DELETE FROM $table WHERE + feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); + + } + + function ccache_update_all($link, $owner_uid) { + + if (get_pref($link, 'ENABLE_FEED_CATS', $owner_uid)) { + + $result = db_query($link, "SELECT feed_id FROM ttrss_cat_counters_cache + WHERE feed_id > 0 AND owner_uid = '$owner_uid'"); + + while ($line = db_fetch_assoc($result)) { + ccache_update($link, $line["feed_id"], $owner_uid, true); + } + + /* We have to manually include category 0 */ + + ccache_update($link, 0, $owner_uid, true); + + } else { + $result = db_query($link, "SELECT feed_id FROM ttrss_counters_cache + WHERE feed_id > 0 AND owner_uid = '$owner_uid'"); + + while ($line = db_fetch_assoc($result)) { + print ccache_update($link, $line["feed_id"], $owner_uid); + + } + + } + } + + function ccache_find($link, $feed_id, $owner_uid, $is_cat = false, + $no_update = false) { + + if (!is_numeric($feed_id)) return; + + if (!$is_cat) { + $table = "ttrss_counters_cache"; + if ($feed_id > 0) { + $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds + WHERE id = '$feed_id'"); + $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid"); + } + } else { + $table = "ttrss_cat_counters_cache"; + } + + if (DB_TYPE == "pgsql") { + $date_qpart = "updated > NOW() - INTERVAL '15 minutes'"; + } else if (DB_TYPE == "mysql") { + $date_qpart = "updated > DATE_SUB(NOW(), INTERVAL 15 MINUTE)"; + } + + $result = db_query($link, "SELECT value FROM $table + WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' + LIMIT 1"); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "value"); + } else { + if ($no_update) { + return -1; + } else { + return ccache_update($link, $feed_id, $owner_uid, $is_cat); + } + } + + } + + function ccache_update($link, $feed_id, $owner_uid, $is_cat = false, + $update_pcat = true) { + + if (!is_numeric($feed_id)) return; + + if (!$is_cat && $feed_id > 0) { + $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds + WHERE id = '$feed_id'"); + $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid"); + } + + $prev_unread = ccache_find($link, $feed_id, $owner_uid, $is_cat, true); + + /* When updating a label, all we need to do is recalculate feed counters + * because labels are not cached */ + + if ($feed_id < 0) { + ccache_update_all($link, $owner_uid); + return; + } + + if (!$is_cat) { + $table = "ttrss_counters_cache"; + } else { + $table = "ttrss_cat_counters_cache"; + } + + if ($is_cat && $feed_id >= 0) { + if ($feed_id != 0) { + $cat_qpart = "cat_id = '$feed_id'"; + } else { + $cat_qpart = "cat_id IS NULL"; + } + + /* Recalculate counters for child feeds */ + + $result = db_query($link, "SELECT id FROM ttrss_feeds + WHERE owner_uid = '$owner_uid' AND $cat_qpart"); + + while ($line = db_fetch_assoc($result)) { + ccache_update($link, $line["id"], $owner_uid, false, false); + } + + $result = db_query($link, "SELECT SUM(value) AS sv + FROM ttrss_counters_cache, ttrss_feeds + WHERE id = feed_id AND $cat_qpart AND + ttrss_feeds.owner_uid = '$owner_uid'"); + + $unread = (int) db_fetch_result($result, 0, "sv"); + + } else { + $unread = (int) getFeedArticles($link, $feed_id, $is_cat, true, $owner_uid); + } + + db_query($link, "BEGIN"); + + $result = db_query($link, "SELECT feed_id FROM $table + WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' LIMIT 1"); + + if (db_num_rows($result) == 1) { + db_query($link, "UPDATE $table SET + value = '$unread', updated = NOW() WHERE + feed_id = '$feed_id' AND owner_uid = '$owner_uid'"); + + } else { + db_query($link, "INSERT INTO $table + (feed_id, value, owner_uid, updated) + VALUES + ($feed_id, $unread, $owner_uid, NOW())"); + } + + db_query($link, "COMMIT"); + + if ($feed_id > 0 && $prev_unread != $unread) { + + if (!$is_cat) { + + /* Update parent category */ + + if ($update_pcat) { + + $result = db_query($link, "SELECT cat_id FROM ttrss_feeds + WHERE owner_uid = '$owner_uid' AND id = '$feed_id'"); + + $cat_id = (int) db_fetch_result($result, 0, "cat_id"); + + ccache_update($link, $cat_id, $owner_uid, true); + + } + } + } else if ($feed_id < 0) { + ccache_update_all($link, $owner_uid); + } + + return $unread; + } + + /* function ccache_cleanup($link, $owner_uid) { + + if (DB_TYPE == "pgsql") { + db_query($link, "DELETE FROM ttrss_counters_cache AS c1 WHERE + (SELECT count(*) FROM ttrss_counters_cache AS c2 + WHERE c1.feed_id = c2.feed_id AND c2.owner_uid = c1.owner_uid) > 1 + AND owner_uid = '$owner_uid'"); + + db_query($link, "DELETE FROM ttrss_cat_counters_cache AS c1 WHERE + (SELECT count(*) FROM ttrss_cat_counters_cache AS c2 + WHERE c1.feed_id = c2.feed_id AND c2.owner_uid = c1.owner_uid) > 1 + AND owner_uid = '$owner_uid'"); + } else { + db_query($link, "DELETE c1 FROM + ttrss_counters_cache AS c1, + ttrss_counters_cache AS c2 + WHERE + c1.owner_uid = '$owner_uid' AND + c1.owner_uid = c2.owner_uid AND + c1.feed_id = c2.feed_id"); + + db_query($link, "DELETE c1 FROM + ttrss_cat_counters_cache AS c1, + ttrss_cat_counters_cache AS c2 + WHERE + c1.owner_uid = '$owner_uid' AND + c1.owner_uid = c2.owner_uid AND + c1.feed_id = c2.feed_id"); + + } + } */ + + function label_find_id($link, $label, $owner_uid) { + $result = db_query($link, + "SELECT id FROM ttrss_labels2 WHERE caption = '$label' + AND owner_uid = '$owner_uid' LIMIT 1"); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "id"); + } else { + return 0; + } + } + + function get_article_labels($link, $id) { + global $memcache; + + $obj_id = md5("LABELS:$id:" . $_SESSION["uid"]); + + $rv = array(); + + if ($memcache && $obj = $memcache->get($obj_id)) { + return $obj; + } else { + + $result = db_query($link, "SELECT label_cache FROM + ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " . + $_SESSION["uid"]); + + $label_cache = db_fetch_result($result, 0, "label_cache"); + + if ($label_cache) { + + $label_cache = json_decode($label_cache, true); + + if ($label_cache["no-labels"] == 1) + return $rv; + else + return $label_cache; + } + + $result = db_query($link, + "SELECT DISTINCT label_id,caption,fg_color,bg_color + FROM ttrss_labels2, ttrss_user_labels2 + WHERE id = label_id + AND article_id = '$id' + AND owner_uid = ".$_SESSION["uid"] . " + ORDER BY caption"); + + while ($line = db_fetch_assoc($result)) { + $rk = array($line["label_id"], $line["caption"], $line["fg_color"], + $line["bg_color"]); + array_push($rv, $rk); + } + if ($memcache) $memcache->add($obj_id, $rv, 0, 3600); + + if (count($rv) > 0) + label_update_cache($link, $id, $rv); + else + label_update_cache($link, $id, array("no-labels" => 1)); + } + + return $rv; + } + + + function label_find_caption($link, $label, $owner_uid) { + $result = db_query($link, + "SELECT caption FROM ttrss_labels2 WHERE id = '$label' + AND owner_uid = '$owner_uid' LIMIT 1"); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "caption"); + } else { + return ""; + } + } + + function label_update_cache($link, $id, $labels = false, $force = false) { + + if ($force) + label_clear_cache($link, $id); + + if (!$labels) + $labels = get_article_labels($link, $id); + + $labels = db_escape_string(json_encode($labels)); + + db_query($link, "UPDATE ttrss_user_entries SET + label_cache = '$labels' WHERE ref_id = '$id'"); + + } + + function label_clear_cache($link, $id) { + + db_query($link, "UPDATE ttrss_user_entries SET + label_cache = '' WHERE ref_id = '$id'"); + + } + + function label_remove_article($link, $id, $label, $owner_uid) { + + $label_id = label_find_id($link, $label, $owner_uid); + + if (!$label_id) return; + + $result = db_query($link, + "DELETE FROM ttrss_user_labels2 + WHERE + label_id = '$label_id' AND + article_id = '$id'"); + + label_clear_cache($link, $id); + } + + function label_add_article($link, $id, $label, $owner_uid) { + + global $memcache; + + if ($memcache) { + $obj_id = md5("LABELS:$id:$owner_uid"); + $memcache->delete($obj_id); + } + + $label_id = label_find_id($link, $label, $owner_uid); + + if (!$label_id) return; + + $result = db_query($link, + "SELECT + article_id FROM ttrss_labels2, ttrss_user_labels2 + WHERE + label_id = id AND + label_id = '$label_id' AND + article_id = '$id' AND owner_uid = '$owner_uid' + LIMIT 1"); + + if (db_num_rows($result) == 0) { + db_query($link, "INSERT INTO ttrss_user_labels2 + (label_id, article_id) VALUES ('$label_id', '$id')"); + } + + label_clear_cache($link, $id); + + } + + function label_remove($link, $id, $owner_uid) { + global $memcache; + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + if ($memcache) { + $obj_id = md5("LABELS:$id:$owner_uid"); + $memcache->delete($obj_id); + } + + db_query($link, "BEGIN"); + + $result = db_query($link, "SELECT caption FROM ttrss_labels2 + WHERE id = '$id'"); + + $caption = db_fetch_result($result, 0, "caption"); + + $result = db_query($link, "DELETE FROM ttrss_labels2 WHERE id = '$id' + AND owner_uid = " . $owner_uid); + + if (db_affected_rows($link, $result) != 0 && $caption) { + + /* Remove access key for the label */ + + $ext_id = -11 - $id; + + db_query($link, "DELETE FROM ttrss_access_keys WHERE + feed_id = '$ext_id' AND owner_uid = $owner_uid"); + + /* Disable filters that reference label being removed */ + + db_query($link, "UPDATE ttrss_filters SET + enabled = false WHERE action_param = '$caption' + AND action_id = 7 + AND owner_uid = " . $owner_uid); + + /* Remove cached data */ + + db_query($link, "UPDATE ttrss_user_entries SET label_cache = '' + WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $owner_uid); + + } + + db_query($link, "COMMIT"); + } + + function label_create($link, $caption) { + + db_query($link, "BEGIN"); + + $result = false; + + $result = db_query($link, "SELECT id FROM ttrss_labels2 + WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]); + + if (db_num_rows($result) == 0) { + $result = db_query($link, + "INSERT INTO ttrss_labels2 (caption,owner_uid) + VALUES ('$caption', '".$_SESSION["uid"]."')"); + + $result = db_affected_rows($link, $result) != 0; + } + + db_query($link, "COMMIT"); + + return $result; + } + + function format_tags_string($tags, $id) { + + $tags_str = ""; + $tags_nolinks_str = ""; + + $num_tags = 0; + + $tag_limit = 6; + + $formatted_tags = array(); + + foreach ($tags as $tag) { + $num_tags++; + $tag_escaped = str_replace("'", "\\'", $tag); + + if (mb_strlen($tag) > 30) { + $tag = truncate_string($tag, 30); + } + + $tag_str = "$tag"; + + array_push($formatted_tags, $tag_str); + + $tmp_tags_str = implode(", ", $formatted_tags); + + if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) { + break; + } + } + + $tags_str = implode(", ", $formatted_tags); + + if ($num_tags < count($tags)) { + $tags_str .= ", …"; + } + + if ($num_tags == 0) { + $tags_str = __("no tags"); + } + + return $tags_str; + + } + + function format_article_labels($labels, $id) { + + $labels_str = ""; + + foreach ($labels as $l) { + $labels_str .= sprintf("%s", + $l[2], $l[3], $l[1]); + } + + return $labels_str; + + } + + function format_article_note($id, $note) { + + $str = "
    +
    ". + __('(edit note)')."
    $note
    "; + + return $str; + } + + function toggle_collapse_cat($link, $cat_id, $mode) { + if ($cat_id > 0) { + $mode = bool_to_sql_bool($mode); + + db_query($link, "UPDATE ttrss_feed_categories SET + collapsed = $mode WHERE id = '$cat_id' AND owner_uid = " . + $_SESSION["uid"]); + } else { + $pref_name = ''; + + switch ($cat_id) { + case -1: + $pref_name = '_COLLAPSED_SPECIAL'; + break; + case -2: + $pref_name = '_COLLAPSED_LABELS'; + break; + case 0: + $pref_name = '_COLLAPSED_UNCAT'; + break; + } + + if ($pref_name) { + if ($mode) { + set_pref($link, $pref_name, 'true'); + } else { + set_pref($link, $pref_name, 'false'); + } + } + } + } + + function remove_feed($link, $id, $owner_uid) { + + if ($id > 0) { + + /* save starred articles in Archived feed */ + + db_query($link, "BEGIN"); + + /* prepare feed if necessary */ + + $result = db_query($link, "SELECT id FROM ttrss_archived_feeds + WHERE id = '$id'"); + + if (db_num_rows($result) == 0) { + db_query($link, "INSERT INTO ttrss_archived_feeds + (id, owner_uid, title, feed_url, site_url) + SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds + WHERE id = '$id'"); + } + + db_query($link, "UPDATE ttrss_user_entries SET feed_id = NULL, + orig_feed_id = '$id' WHERE feed_id = '$id' AND + marked = true AND owner_uid = $owner_uid"); + + /* Remove access key for the feed */ + + db_query($link, "DELETE FROM ttrss_access_keys WHERE + feed_id = '$id' AND owner_uid = $owner_uid"); + + /* remove the feed */ + + db_query($link, "DELETE FROM ttrss_feeds + WHERE id = '$id' AND owner_uid = $owner_uid"); + + db_query($link, "COMMIT"); + + if (file_exists(ICONS_DIR . "/$id.ico")) { + unlink(ICONS_DIR . "/$id.ico"); + } + + ccache_remove($link, $id, $owner_uid); + + } else { + label_remove($link, -11-$id, $owner_uid); + ccache_remove($link, -11-$id, $owner_uid); + } + } + + function add_feed_category($link, $feed_cat) { + + if (!$feed_cat) return false; + + db_query($link, "BEGIN"); + + $result = db_query($link, + "SELECT id FROM ttrss_feed_categories + WHERE title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]); + + if (db_num_rows($result) == 0) { + + $result = db_query($link, + "INSERT INTO ttrss_feed_categories (owner_uid,title) + VALUES ('".$_SESSION["uid"]."', '$feed_cat')"); + + db_query($link, "COMMIT"); + + return true; + } + + return false; + } + + function remove_feed_category($link, $id, $owner_uid) { + + db_query($link, "DELETE FROM ttrss_feed_categories + WHERE id = '$id' AND owner_uid = $owner_uid"); + + ccache_remove($link, $id, $owner_uid, true); + } + + function archive_article($link, $id, $owner_uid) { + db_query($link, "BEGIN"); + + $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries + WHERE ref_id = '$id' AND owner_uid = $owner_uid"); + + if (db_num_rows($result) != 0) { + + /* prepare the archived table */ + + $feed_id = (int) db_fetch_result($result, 0, "feed_id"); + + if ($feed_id) { + $result = db_query($link, "SELECT id FROM ttrss_archived_feeds + WHERE id = '$feed_id'"); + + if (db_num_rows($result) == 0) { + db_query($link, "INSERT INTO ttrss_archived_feeds + (id, owner_uid, title, feed_url, site_url) + SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds + WHERE id = '$feed_id'"); + } + + db_query($link, "UPDATE ttrss_user_entries + SET orig_feed_id = feed_id, feed_id = NULL + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + } + } + + db_query($link, "COMMIT"); + } + + function getArticleFeed($link, $id) { + $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries + WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); + + if (db_num_rows($result) != 0) { + return db_fetch_result($result, 0, "feed_id"); + } else { + return 0; + } + } + + /** + * Fixes incomplete URLs by prepending "http://". + * Also replaces feed:// with http://, and + * prepends a trailing slash if the url is a domain name only. + * + * @param string $url Possibly incomplete URL + * + * @return string Fixed URL. + */ + function fix_url($url) { + if (strpos($url, '://') === false) { + $url = 'http://' . $url; + } else if (substr($url, 0, 5) == 'feed:') { + $url = 'http:' . substr($url, 5); + } + + //prepend slash if the URL has no slash in it + // "http://www.example" -> "http://www.example/" + if (strpos($url, '/', strpos($url, ':') + 3) === false) { + $url .= '/'; + } + + if ($url != "http:///") + return $url; + else + return ''; + } + + function validate_feed_url($url) { + $parts = parse_url($url); + + return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https'); + + } + + function get_article_enclosures($link, $id) { + + global $memcache; + + $query = "SELECT * FROM ttrss_enclosures + WHERE post_id = '$id' AND content_url != ''"; + + $obj_id = md5("ENCLOSURES:$id"); + + $rv = array(); + + if ($memcache && $obj = $memcache->get($obj_id)) { + $rv = $obj; + } else { + $result = db_query($link, $query); + + if (db_num_rows($result) > 0) { + while ($line = db_fetch_assoc($result)) { + array_push($rv, $line); + } + if ($memcache) $memcache->add($obj_id, $rv, 0, 3600); + } + } + + return $rv; + } + + function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset) { + + $feeds = array(); + + /* Labels */ + + if ($cat_id == -4 || $cat_id == -2) { + $counters = getLabelCounters($link, true); + + foreach (array_values($counters) as $cv) { + + $unread = $cv["counter"]; + + if ($unread || !$unread_only) { + + $row = array( + "id" => $cv["id"], + "title" => $cv["description"], + "unread" => $cv["counter"], + "cat_id" => -2, + ); + + array_push($feeds, $row); + } + } + } + + /* Virtual feeds */ + + if ($cat_id == -4 || $cat_id == -1) { + foreach (array(-1, -2, -3, -4, 0) as $i) { + $unread = getFeedUnread($link, $i); + + if ($unread || !$unread_only) { + $title = getFeedTitle($link, $i); + + $row = array( + "id" => $i, + "title" => $title, + "unread" => $unread, + "cat_id" => -1, + ); + array_push($feeds, $row); + } + + } + } + + /* Real feeds */ + + if ($limit) { + $limit_qpart = "LIMIT $limit OFFSET $offset"; + } else { + $limit_qpart = ""; + } + + if ($cat_id == -4 || $cat_id == -3) { + $result = db_query($link, "SELECT + id, feed_url, cat_id, title, ". + SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated + FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"] . + " ORDER BY cat_id, title " . $limit_qpart); + } else { + + if ($cat_id) + $cat_qpart = "cat_id = '$cat_id'"; + else + $cat_qpart = "cat_id IS NULL"; + + $result = db_query($link, "SELECT + id, feed_url, cat_id, title, ". + SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated + FROM ttrss_feeds WHERE + $cat_qpart AND owner_uid = " . $_SESSION["uid"] . + " ORDER BY cat_id, title " . $limit_qpart); + } + + while ($line = db_fetch_assoc($result)) { + + $unread = getFeedUnread($link, $line["id"]); + + $has_icon = feed_has_icon($line['id']); + + if ($unread || !$unread_only) { + + $row = array( + "feed_url" => $line["feed_url"], + "title" => $line["title"], + "id" => (int)$line["id"], + "unread" => (int)$unread, + "has_icon" => $has_icon, + "cat_id" => (int)$line["cat_id"], + "last_updated" => strtotime($line["last_updated"]) + ); + + array_push($feeds, $row); + } + } + + return $feeds; + } + + function api_get_headlines($link, $feed_id, $limit, $offset, + $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order, + $include_attachments, $since_id) { + + /* do not rely on params below */ + + $search = db_escape_string($_REQUEST["search"]); + $search_mode = db_escape_string($_REQUEST["search_mode"]); + $match_on = db_escape_string($_REQUEST["match_on"]); + + $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit, + $view_mode, $is_cat, $search, $search_mode, $match_on, + $order, $offset, 0, false, $since_id); + + $result = $qfh_ret[0]; + $feed_title = $qfh_ret[1]; + + $headlines = array(); + + while ($line = db_fetch_assoc($result)) { + $is_updated = ($line["last_read"] == "" && + ($line["unread"] != "t" && $line["unread"] != "1")); + + $tags = explode(",", $line["tag_cache"]); + $labels = json_decode($line["label_cache"], true); + + //if (!$tags) $tags = get_article_tags($link, $line["id"]); + //if (!$labels) $labels = get_article_labels($link, $line["id"]); + + $headline_row = array( + "id" => (int)$line["id"], + "unread" => sql_bool_to_bool($line["unread"]), + "marked" => sql_bool_to_bool($line["marked"]), + "published" => sql_bool_to_bool($line["published"]), + "updated" => strtotime($line["updated"]), + "is_updated" => $is_updated, + "title" => $line["title"], + "link" => $line["link"], + "feed_id" => $line["feed_id"], + "tags" => $tags, + ); + + if ($include_attachments) + $headline_row['attachments'] = get_article_enclosures($link, + $line['id']); + + if ($show_excerpt) { + $excerpt = truncate_string(strip_tags($line["content_preview"]), 100); + $headline_row["excerpt"] = $excerpt; + } + + if ($show_content) { + $headline_row["content"] = $line["content_preview"]; + } + + // unify label output to ease parsing + if ($labels["no-labels"] == 1) $labels = array(); + + $headline_row["labels"] = $labels; + + array_push($headlines, $headline_row); + } + + return $headlines; + } + + function generate_error_feed($link, $error) { + $reply = array(); + + $reply['headlines']['id'] = -6; + $reply['headlines']['is_cat'] = false; + + $reply['headlines']['toolbar'] = ''; + $reply['headlines']['content'] = "
    ". $error . "
    "; + + $reply['headlines-info'] = array("count" => 0, + "vgroup_last_feed" => '', + "unread" => 0, + "disable_cache" => true); + + return $reply; + } + + + function generate_dashboard_feed($link) { + $reply = array(); + + $reply['headlines']['id'] = -5; + $reply['headlines']['is_cat'] = false; + + $reply['headlines']['toolbar'] = ''; + $reply['headlines']['content'] = "
    ".__('No feed selected.'); + + $reply['headlines']['content'] .= "

    "; + + $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds + WHERE owner_uid = " . $_SESSION['uid']); + + $last_updated = db_fetch_result($result, 0, "last_updated"); + $last_updated = make_local_datetime($link, $last_updated, false); + + $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); + + $result = db_query($link, "SELECT COUNT(id) AS num_errors + FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]); + + $num_errors = db_fetch_result($result, 0, "num_errors"); + + if ($num_errors > 0) { + $reply['headlines']['content'] .= "
    "; + $reply['headlines']['content'] .= "". + __('Some feeds have update errors (click for details)').""; + } + $reply['headlines']['content'] .= "

    "; + + $reply['headlines-info'] = array("count" => 0, + "vgroup_last_feed" => '', + "unread" => 0, + "disable_cache" => true); + + return $reply; + } + + function save_email_address($link, $email) { + // FIXME: implement persistent storage of emails + + if (!$_SESSION['stored_emails']) + $_SESSION['stored_emails'] = array(); + + if (!in_array($email, $_SESSION['stored_emails'])) + array_push($_SESSION['stored_emails'], $email); + } + + function update_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) { + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + $sql_is_cat = bool_to_sql_bool($is_cat); + + $result = db_query($link, "SELECT access_key FROM ttrss_access_keys + WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat + AND owner_uid = " . $owner_uid); + + if (db_num_rows($result) == 1) { + $key = db_escape_string(sha1(uniqid(rand(), true))); + + db_query($link, "UPDATE ttrss_access_keys SET access_key = '$key' + WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat + AND owner_uid = " . $owner_uid); + + return $key; + + } else { + return get_feed_access_key($link, $feed_id, $is_cat, $owner_uid); + } + } + + function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) { + + if (!$owner_uid) $owner_uid = $_SESSION["uid"]; + + $sql_is_cat = bool_to_sql_bool($is_cat); + + $result = db_query($link, "SELECT access_key FROM ttrss_access_keys + WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat + AND owner_uid = " . $owner_uid); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "access_key"); + } else { + $key = db_escape_string(sha1(uniqid(rand(), true))); + + $result = db_query($link, "INSERT INTO ttrss_access_keys + (access_key, feed_id, is_cat, owner_uid) + VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')"); + + return $key; + } + return false; + } + + /** + * Extracts RSS/Atom feed URLs from the given HTML URL. + * + * @param string $url HTML page URL + * + * @return array Array of feeds. Key is the full URL, value the title + */ + function get_feeds_from_html($url, $login = false, $pass = false) + { + $url = fix_url($url); + $baseUrl = substr($url, 0, strrpos($url, '/') + 1); + + libxml_use_internal_errors(true); + + $content = @fetch_file_contents($url, false, $login, $pass); + + $doc = new DOMDocument(); + $doc->loadHTML($content); + $xpath = new DOMXPath($doc); + $entries = $xpath->query('/html/head/link[@rel="alternate"]'); + $feedUrls = array(); + foreach ($entries as $entry) { + if ($entry->hasAttribute('href')) { + $title = $entry->getAttribute('title'); + if ($title == '') { + $title = $entry->getAttribute('type'); + } + $feedUrl = rewrite_relative_url( + $baseUrl, $entry->getAttribute('href') + ); + $feedUrls[$feedUrl] = $title; + } + } + return $feedUrls; + } + + /** + * Checks if the content behind the given URL is a HTML file + * + * @param string $url URL to check + * + * @return boolean True if the URL contains HTML content + */ + function url_is_html($url, $login = false, $pass = false) { + $content = substr(fetch_file_contents($url, false, $login, $pass), 0, 1000); + + if (stripos($content, '') === false + && stripos($content, '"; + + while ($line = db_fetch_assoc($result)) { + + $issel = ($line["caption"] == $value) ? "selected=\"1\"" : ""; + + print ""; + + } + +# print ""; + + print ""; + + + } + + function format_article_enclosures($link, $id, $always_display_enclosures, + $article_content) { + + $result = get_article_enclosures($link, $id); + $rv = ''; + + if (count($result) > 0) { + + $entries_html = array(); + $entries = array(); + + foreach ($result as $line) { + + $url = $line["content_url"]; + $ctype = $line["content_type"]; + + if (!$ctype) $ctype = __("unknown type"); + +# $filename = substr($url, strrpos($url, "/")+1); + + $entry = format_inline_player($link, $url, $ctype); + +# $entry .= " " . +# $filename . " (" . $ctype . ")" . ""; + + array_push($entries_html, $entry); + + $entry = array(); + + $entry["type"] = $ctype; + $entry["filename"] = $filename; + $entry["url"] = $url; + + array_push($entries, $entry); + } + + $rv .= "
    "; + + if (!get_pref($link, "STRIP_IMAGES")) { + if ($always_display_enclosures || + !preg_match("/

    "; + } + } + } + } + + if (count($entries) == 1) { + $rv .= __("Attachment:") . " "; + } else { + $rv .= __("Attachments:") . " "; + } + + $rv .= join(", ", $entries_html); + + $rv .= "
    "; + } + + return $rv; + } + + function getLastArticleId($link) { + $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries + WHERE owner_uid = " . $_SESSION["uid"]); + + if (db_num_rows($result) == 1) { + return db_fetch_result($result, 0, "id"); + } else { + return -1; + } + } + + function build_url($parts) { + return $parts['scheme'] . "://" . $parts['host'] . $parts['path']; + } + + /** + * Converts a (possibly) relative URL to a absolute one. + * + * @param string $url Base URL (i.e. from where the document is) + * @param string $rel_url Possibly relative URL in the document + * + * @return string Absolute URL + */ + function rewrite_relative_url($url, $rel_url) { + if (strpos($rel_url, "://") !== false) { + return $rel_url; + } else if (strpos($rel_url, "/") === 0) + { + $parts = parse_url($url); + $parts['path'] = $rel_url; + + return build_url($parts); + + } else { + $parts = parse_url($url); + if (!isset($parts['path'])) { + $parts['path'] = '/'; + } + $dir = $parts['path']; + if (substr($dir, -1) !== '/') { + $dir = dirname($parts['path']); + $dir !== '/' && $dir .= '/'; + } + $parts['path'] = $dir . $rel_url; + + return build_url($parts); + } + } + + function sphinx_search($query, $offset = 0, $limit = 30) { + require_once 'lib/sphinxapi.php'; + + $sphinxClient = new SphinxClient(); + + $sphinxClient->SetServer('localhost', 9312); + $sphinxClient->SetConnectTimeout(1); + + $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30, + 'feed_title' => 20)); + + $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2); + $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25); + $sphinxClient->SetLimits($offset, $limit, 1000); + $sphinxClient->SetArrayResult(false); + $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid'])); + + $result = $sphinxClient->Query($query, SPHINX_INDEX); + + $ids = array(); + + if (is_array($result['matches'])) { + foreach (array_keys($result['matches']) as $int_id) { + $ref_id = $result['matches'][$int_id]['attrs']['ref_id']; + array_push($ids, $ref_id); + } + } + + return $ids; + } + + function cleanup_tags($link, $days = 14, $limit = 1000) { + + if (DB_TYPE == "pgsql") { + $interval_query = "date_updated < NOW() - INTERVAL '$days days'"; + } else if (DB_TYPE == "mysql") { + $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)"; + } + + $tags_deleted = 0; + + while ($limit > 0) { + $limit_part = 500; + + $query = "SELECT ttrss_tags.id AS id + FROM ttrss_tags, ttrss_user_entries, ttrss_entries + WHERE post_int_id = int_id AND $interval_query AND + ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part"; + + $result = db_query($link, $query); + + $ids = array(); + + while ($line = db_fetch_assoc($result)) { + array_push($ids, $line['id']); + } + + 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); + } else { + break; + } + + $limit -= $limit_part; + } + + print "\n"; + + return $tags_deleted; + } + + function feedlist_init_cat($link, $cat_id, $hidden = false) { + $obj = array(); + $cat_id = (int) $cat_id; + + if ($cat_id > 0) { + $cat_unread = ccache_find($link, $cat_id, $_SESSION["uid"], true); + } else if ($cat_id == 0 || $cat_id == -2) { + $cat_unread = getCategoryUnread($link, $cat_id); + } + + $obj['id'] = 'CAT:' . $cat_id; + $obj['items'] = array(); + $obj['name'] = getCategoryTitle($link, $cat_id); + $obj['type'] = 'feed'; + $obj['unread'] = (int) $cat_unread; + $obj['hidden'] = $hidden; + $obj['bare_id'] = $cat_id; + + return $obj; + } + + function feedlist_init_feed($link, $feed_id, $title = false, $unread = false, $error = '', $updated = '') { + $obj = array(); + $feed_id = (int) $feed_id; + + if (!$title) + $title = getFeedTitle($link, $feed_id, false); + + if ($unread === false) + $unread = getFeedUnread($link, $feed_id, false); + + $obj['id'] = 'FEED:' . $feed_id; + $obj['name'] = $title; + $obj['unread'] = (int) $unread; + $obj['type'] = 'feed'; + $obj['error'] = $error; + $obj['updated'] = $updated; + $obj['icon'] = getFeedIcon($feed_id); + $obj['bare_id'] = $feed_id; + + return $obj; + } + + + function fetch_twitter_rss($link, $url, $owner_uid) { + + require_once 'lib/tmhoauth/tmhOAuth.php'; + + $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users + WHERE id = $owner_uid"); + + $access_token = json_decode(db_fetch_result($result, 0, 'twitter_oauth'), true); + $url_escaped = db_escape_string($url); + + if ($access_token) { + + $tmhOAuth = new tmhOAuth(array( + 'consumer_key' => CONSUMER_KEY, + 'consumer_secret' => CONSUMER_SECRET, + 'user_token' => $access_token['oauth_token'], + 'user_secret' => $access_token['oauth_token_secret'], + )); + + $code = $tmhOAuth->request('GET', $url); + + if ($code == 200) { + + $content = $tmhOAuth->response['response']; + + define('MAGPIE_CACHE_ON', false); + + $rss = new MagpieRSS($content, MAGPIE_OUTPUT_ENCODING, + MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING ); + + return $rss; + + } else { + + db_query($link, "UPDATE ttrss_feeds + SET last_error = 'OAuth authorization failed ($code).' + WHERE feed_url = '$url_escaped' AND owner_uid = $owner_uid"); + } + + } else { + + db_query($link, "UPDATE ttrss_feeds + SET last_error = 'OAuth information not found.' + WHERE feed_url = '$url_escaped' AND owner_uid = $owner_uid"); + + return false; + } + } + + function print_user_stylesheet($link) { + $value = get_pref($link, 'USER_STYLESHEET'); + + if ($value) { + print ""; + } + + } + + function rewrite_urls($line) { + global $url_regex; + + $urls = null; + + $result = preg_replace("/((?\\1", $line); + + return $result; + } + + function filter_to_sql($filter) { + $query = ""; + + if (DB_TYPE == "pgsql") + $reg_qpart = "~"; + else + $reg_qpart = "REGEXP"; + + switch ($filter["type"]) { + case "title": + $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". + $filter['reg_exp'] . "')"; + break; + case "content": + $query = "LOWER(ttrss_entries.content) $reg_qpart LOWER('". + $filter['reg_exp'] . "')"; + break; + case "both": + $query = "LOWER(ttrss_entries.title) $reg_qpart LOWER('". + $filter['reg_exp'] . "') OR LOWER(" . + "ttrss_entries.content) $reg_qpart LOWER('" . $filter['reg_exp'] . "')"; + break; + case "tag": + $query = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('". + $filter['reg_exp'] . "')"; + break; + case "link": + $query = "LOWER(ttrss_entries.link) $reg_qpart LOWER('". + $filter['reg_exp'] . "')"; + break; + case "date": + + if ($filter["filter_param"] == "before") + $cmp_qpart = "<"; + else + $cmp_qpart = ">="; + + $timestamp = date("Y-m-d H:N:s", strtotime($filter["reg_exp"])); + $query = "ttrss_entries.date_entered $cmp_qpart '$timestamp'"; + break; + case "author": + $query = "LOWER(ttrss_entries.author) $reg_qpart LOWER('". + $filter['reg_exp'] . "')"; + break; + } + + if ($filter["inverse"]) + $query = "NOT ($query)"; + + if ($query) { + if (DB_TYPE == "pgsql") { + $query = " ($query) AND ttrss_entries.date_entered > NOW() - INTERVAL '14 days'"; + } else { + $query = " ($query) AND ttrss_entries.date_entered > DATE_SUB(NOW(), INTERVAL 14 DAY)"; + } + $query .= " AND "; + } + + + return $query; + } + + // Status codes: + // -1 - never connected + // 0 - no data received + // 1 - data received successfully + // 2 - did not receive valid data + // >10 - server error, code + 10 (e.g. 16 means server error 6) + + function get_linked_feeds($link, $instance_id = false) { + if ($instance_id) + $instance_qpart = "id = '$instance_id' AND "; + else + $instance_qpart = ""; + + if (DB_TYPE == "pgsql") { + $date_qpart = "last_connected < NOW() - INTERVAL '6 hours'"; + } else { + $date_qpart = "last_connected < DATE_SUB(NOW(), INTERVAL 6 HOUR)"; + } + + $result = db_query($link, "SELECT id, access_key, access_url FROM ttrss_linked_instances + WHERE $instance_qpart $date_qpart ORDER BY last_connected"); + + while ($line = db_fetch_assoc($result)) { + $id = $line['id']; + + _debug("Updating: " . $line['access_url'] . " ($id)"); + + $fetch_url = $line['access_url'] . '/public.php?op=fbexport'; + $post_query = 'key=' . $line['access_key']; + + $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query); + + // try doing it the old way + if (!$feeds) { + $fetch_url = $line['access_url'] . '/backend.php?op=fbexport'; + $feeds = fetch_file_contents($fetch_url, false, false, false, $post_query); + } + + if ($feeds) { + $feeds = json_decode($feeds, true); + + if ($feeds) { + if ($feeds['error']) { + $status = $feeds['error']['code'] + 10; + } else { + $status = 1; + + if (count($feeds['feeds']) > 0) { + + db_query($link, "DELETE FROM ttrss_linked_feeds + WHERE instance_id = '$id'"); + + foreach ($feeds['feeds'] as $feed) { + $feed_url = db_escape_string($feed['feed_url']); + $title = db_escape_string($feed['title']); + $subscribers = db_escape_string($feed['subscribers']); + $site_url = db_escape_string($feed['site_url']); + + db_query($link, "INSERT INTO ttrss_linked_feeds + (feed_url, site_url, title, subscribers, instance_id, created, updated) + VALUES + ('$feed_url', '$site_url', '$title', '$subscribers', '$id', NOW(), NOW())"); + } + } else { + // received 0 feeds, this might indicate that + // the instance on the other hand is rebuilding feedbrowser cache + // we will try again later + + // TODO: maybe perform expiration based on updated here? + } + + _debug("Processed " . count($feeds['feeds']) . " feeds."); + } + } else { + $status = 2; + } + + } else { + $status = 0; + } + + _debug("Status: $status"); + + db_query($link, "UPDATE ttrss_linked_instances SET + last_status_out = '$status', last_connected = NOW() WHERE id = '$id'"); + + } + } + + function handle_public_request($link, $op) { + switch ($op) { + + case "getUnread": + $login = db_escape_string($_REQUEST["login"]); + $fresh = $_REQUEST["fresh"] == "1"; + + $result = db_query($link, "SELECT id FROM ttrss_users WHERE login = '$login'"); + + if (db_num_rows($result) == 1) { + $uid = db_fetch_result($result, 0, "id"); + + print getGlobalUnread($link, $uid); + + if ($fresh) { + print ";"; + print getFeedArticles($link, -3, false, true, $uid); + } + + } else { + print "-1;User not found"; + } + + break; // getUnread + + case "getProfiles": + $login = db_escape_string($_REQUEST["login"]); + $password = db_escape_string($_REQUEST["password"]); + + if (authenticate_user($link, $login, $password)) { + $result = db_query($link, "SELECT * FROM ttrss_settings_profiles + WHERE owner_uid = " . $_SESSION["uid"] . " ORDER BY title"); + + print ""; + + $_SESSION = array(); + } + break; // getprofiles + + case "pubsub": + $mode = db_escape_string($_REQUEST['hub_mode']); + $feed_id = (int) db_escape_string($_REQUEST['id']); + $feed_url = db_escape_string($_REQUEST['hub_topic']); + + if (!PUBSUBHUBBUB_ENABLED) { + header('HTTP/1.0 404 Not Found'); + echo "404 Not found"; + return; + } + + // TODO: implement hub_verifytoken checking + + $result = db_query($link, "SELECT feed_url FROM ttrss_feeds + WHERE id = '$feed_id'"); + + if (db_num_rows($result) != 0) { + + $check_feed_url = db_fetch_result($result, 0, "feed_url"); + + if ($check_feed_url && ($check_feed_url == $feed_url || !$feed_url)) { + if ($mode == "subscribe") { + + db_query($link, "UPDATE ttrss_feeds SET pubsub_state = 2 + WHERE id = '$feed_id'"); + + print $_REQUEST['hub_challenge']; + return; + + } else if ($mode == "unsubscribe") { + + db_query($link, "UPDATE ttrss_feeds SET pubsub_state = 0 + WHERE id = '$feed_id'"); + + print $_REQUEST['hub_challenge']; + return; + + } else if (!$mode) { + + // Received update ping, schedule feed update. + //update_rss_feed($link, $feed_id, true, true); + + db_query($link, "UPDATE ttrss_feeds SET + last_update_started = '1970-01-01', + last_updated = '1970-01-01' WHERE id = '$feed_id'"); + + } + } else { + header('HTTP/1.0 404 Not Found'); + echo "404 Not found"; + } + } else { + header('HTTP/1.0 404 Not Found'); + echo "404 Not found"; + } + + break; // pubsub + + case "logout": + logout_user(); + header("Location: index.php"); + break; // logout + + case "fbexport": + + $access_key = db_escape_string($_POST["key"]); + + // TODO: rate limit checking using last_connected + $result = db_query($link, "SELECT id FROM ttrss_linked_instances + WHERE access_key = '$access_key'"); + + if (db_num_rows($result) == 1) { + + $instance_id = db_fetch_result($result, 0, "id"); + + $result = db_query($link, "SELECT feed_url, site_url, title, subscribers + FROM ttrss_feedbrowser_cache ORDER BY subscribers DESC LIMIT 100"); + + $feeds = array(); + + while ($line = db_fetch_assoc($result)) { + array_push($feeds, $line); + } + + db_query($link, "UPDATE ttrss_linked_instances SET + last_status_in = 1 WHERE id = '$instance_id'"); + + print json_encode(array("feeds" => $feeds)); + } else { + print json_encode(array("error" => array("code" => 6))); + } + break; // fbexport + + case "share": + $uuid = db_escape_string($_REQUEST["key"]); + + $result = db_query($link, "SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE + uuid = '$uuid'"); + + if (db_num_rows($result) != 0) { + header("Content-Type: text/html"); + + $id = db_fetch_result($result, 0, "ref_id"); + $owner_uid = db_fetch_result($result, 0, "owner_uid"); + + $_SESSION["uid"] = $owner_uid; + $article = format_article($link, $id, false, true); + $_SESSION["uid"] = ""; + + print_r($article['content']); + + } else { + print "Article not found."; + } + + break; + + case "rss": + $feed = db_escape_string($_REQUEST["id"]); + $key = db_escape_string($_REQUEST["key"]); + $is_cat = $_REQUEST["is_cat"] != false; + $limit = (int)db_escape_string($_REQUEST["limit"]); + + $search = db_escape_string($_REQUEST["q"]); + $match_on = db_escape_string($_REQUEST["m"]); + $search_mode = db_escape_string($_REQUEST["smode"]); + $view_mode = db_escape_string($_REQUEST["view-mode"]); + + if (SINGLE_USER_MODE) { + authenticate_user($link, "admin", null); + } + + $owner_id = false; + + if ($key) { + $result = db_query($link, "SELECT owner_uid FROM + ttrss_access_keys WHERE access_key = '$key' AND feed_id = '$feed'"); + + if (db_num_rows($result) == 1) + $owner_id = db_fetch_result($result, 0, "owner_uid"); + } + + if ($owner_id) { + $_SESSION['uid'] = $owner_id; + + generate_syndicated_feed($link, 0, $feed, $is_cat, $limit, + $search, $search_mode, $match_on, $view_mode); + } else { + header('HTTP/1.1 403 Forbidden'); + } + break; // rss + + + case "globalUpdateFeeds": + // Update all feeds needing a update. + update_daemon_common($link, 0, true, true); + break; // globalUpdateFeeds + + + default: + header("Content-Type: text/plain"); + print json_encode(array("error" => array("code" => 7))); + break; // fallback + + } + } +?> diff --git a/include/localized_schema.php b/include/localized_schema.php new file mode 100644 index 000000000..29663f351 --- /dev/null +++ b/include/localized_schema.php @@ -0,0 +1,57 @@ + diff --git a/include/login_form.php b/include/login_form.php new file mode 100644 index 000000000..04ca3e6d0 --- /dev/null +++ b/include/login_form.php @@ -0,0 +1,199 @@ + + + Tiny Tiny RSS : Login + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + +
    + +
    + +
    + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + + + + + + + + +
    + +
    + +
    + +
    +
    + Tiny Tiny RSS + + v + + © 2005– Andrew Dolgov +
    + +
    + + diff --git a/include/sanity_check.php b/include/sanity_check.php new file mode 100644 index 000000000..3e5c09803 --- /dev/null +++ b/include/sanity_check.php @@ -0,0 +1,175 @@ +Fatal Error: You forgot to copy + config.php-dist to config.php and edit it.\n"; + exit; + } + + require_once "config.php"; + require_once "sanity_config.php"; + + if (CONFIG_VERSION != EXPECTED_CONFIG_VERSION) { + $err_msg = "config: your config file version is incorrect. See config.php-dist.\n"; + } + + $purifier_cache_dir = CACHE_DIR . "/htmlpurifier"; + + if (!is_writable($purifier_cache_dir)) { + $err_msg = "config: HTMLPurifier cache directory should be writable by anyone (chmod -R 777 $purifier_cache_dir)"; + } + + if (GENERATED_CONFIG_CHECK != EXPECTED_CONFIG_VERSION) { + $err_msg = "config: your sanity_config.php is outdated, please recreate it using ./utils/regen_config_checks.sh"; + } + + foreach ($requred_defines as $d) { + if (!defined($d)) { + $err_msg = "config: required constant $d is not defined. Please check config.php"; + } + } + + if (defined('RSS_BACKEND_TYPE')) { + print "Fatal error: RSS_BACKEND_TYPE is deprecated. Please remove this + option from config.php\n"; + exit; + } + + if (file_exists("xml-export.php") || file_exists("xml-import.php")) { + print "Fatal Error: XML Import/Export tools (xml-export.php + and xml-import.php) could be used maliciously. Please remove them + from your TT-RSS instance.\n"; + exit; + } + + if (SINGLE_USER_MODE && DAEMON_UPDATE_LOGIN_LIMIT > 0) { + print "Fatal Error: Please set DAEMON_UPDATE_LOGIN_LIMIT + to 0 in single user mode.\n"; + exit; + } + + if (!defined('SESSION_EXPIRE_TIME')) { + $err_msg = "config: SESSION_EXPIRE_TIME is undefined"; + } + + if (SESSION_EXPIRE_TIME < 60) { + $err_msg = "config: SESSION_EXPIRE_TIME is too low (less than 60)"; + } + + if (SESSION_EXPIRE_TIME < SESSION_COOKIE_LIFETIME) { + $err_msg = "config: SESSION_EXPIRE_TIME should be greater or equal to" . + "SESSION_COOKIE_LIFETIME"; + } + +/* if (defined('DISABLE_SESSIONS')) { + $err_msg = "config: you have enabled DISABLE_SESSIONS. Please disable this option."; +} */ + + if (DATABASE_BACKED_SESSIONS && SINGLE_USER_MODE) { + $err_msg = "config: DATABASE_BACKED_SESSIONS is incompatible with SINGLE_USER_MODE"; + } + + if (DATABASE_BACKED_SESSIONS && DB_TYPE == "mysql") { + $err_msg = "config: DATABASE_BACKED_SESSIONS are currently broken with MySQL"; + } + + if (SINGLE_USER_MODE) { + $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); + + if ($link) { + $result = db_query($link, "SELECT id FROM ttrss_users WHERE id = 1"); + + if (db_num_rows($result) != 1) { + $err_msg = "config: SINGLE_USER_MODE is enabled but default admin account (UID=1) is not found."; + } + } + } + + if (defined('MAIL_FROM')) { + $err_msg = "config: MAIL_FROM has been split into DIGEST_FROM_NAME and DIGEST_FROM_ADDRESS"; + } + + if (!defined('COUNTERS_MAX_AGE')) { + $err_msg = "config: option COUNTERS_MAX_AGE expected, but not defined"; + } + + if (defined('DAEMON_REFRESH_ONLY')) { + $err_msg = "config: option DAEMON_REFRESH_ONLY is obsolete. Please remove this option and read about other ways to update feeds on the wiki."; + + } + + if (defined('ENABLE_SIMPLEPIE')) { + $err_msg = "config: ENABLE_SIMPLEPIE is obsolete and replaced with DEFAULT_UPDATE_METHOD. Please adjust your config.php."; + } + + if (!defined('DEFAULT_UPDATE_METHOD') || (DEFAULT_UPDATE_METHOD != 0 && + DEFAULT_UPDATE_METHOD != 1)) { + $err_msg = "config: DEFAULT_UPDATE_METHOD should be either 0 or 1."; + } + + if (SELF_URL_PATH == "http://yourserver/tt-rss/") { + $err_msg = "config: please set SELF_URL_PATH to the correct value."; + } + + if (!is_writable(ICONS_DIR)) { + $err_msg = "config: your ICONS_DIR (" . ICONS_DIR . ") is not writable.\n"; + } + + if (ini_get("open_basedir")) { + $err_msg = "php.ini: open_basedir is not supported."; + } + + if (!function_exists("curl_init") && !ini_get("allow_url_fopen")) { + $err_msg = "php.ini: either allow_url_fopen or CURL needs to be enabled."; + } + + if (!function_exists("json_encode")) { + $err_msg = "PHP: json functions not found."; + } + + if (DB_TYPE == "mysql" && !function_exists("mysql_connect")) { + $err_msg = "PHP: MySQL functions not found."; + } + + if (DB_TYPE == "pgsql" && !function_exists("pg_connect")) { + $err_msg = "PHP: PostgreSQL functions not found."; + } + + if (!function_exists("mb_strlen")) { + $err_msg = "PHP: mbstring functions not found."; + } + + if (!function_exists("ctype_lower")) { + $err_msg = "PHP: ctype functions not found (required for HTMLPurifier)."; + } + + if (ini_get("safe_mode")) { + $err_msg = "php.ini: Safe mode is not supported. If you wish to continue, remove this test from sanity_check.php and proceeed at your own risk. Please note that your bug reports will not be accepted or reviewed."; + } + + if ((PUBSUBHUBBUB_HUB || PUBSUBHUBBUB_ENABLED) && !function_exists("curl_init")) { + $err_msg = "CURL is required for PubSubHubbub support."; + } + + if (!class_exists("DOMDocument")) { + $err_msg = "PHP: DOMDocument extension not found."; + } + + if (SELF_URL_PATH == "http://local.host/tt-rss") { + $err_msg = "config: please set SELF_URL_PATH to the correct value"; + } + + if (!ISCONFIGURED) { + $err_msg = "config: please read config.php completely."; + } + + if ($err_msg) { + print "Fatal Error: $err_msg\n"; + exit; + } + +?> diff --git a/include/sanity_config.php b/include/sanity_config.php new file mode 100644 index 000000000..51c9d52be --- /dev/null +++ b/include/sanity_config.php @@ -0,0 +1,3 @@ + diff --git a/include/sessions.php b/include/sessions.php new file mode 100644 index 000000000..8588f5807 --- /dev/null +++ b/include/sessions.php @@ -0,0 +1,108 @@ + diff --git a/include/version.php b/include/version.php new file mode 100644 index 000000000..61445c55f --- /dev/null +++ b/include/version.php @@ -0,0 +1,3 @@ + diff --git a/index.php b/index.php index e73671ed4..5b5560a9a 100644 --- a/index.php +++ b/index.php @@ -1 +1,211 @@ - + + + + + Tiny Tiny RSS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + +
    +
    + + + + + +
     
    + + + + +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    "> + +
    +
    + +
    + + + + + + + + + + + +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    + + +
    + + +
    +
    +
    +
    +
    + + + + + diff --git a/js/FeedTree.js b/js/FeedTree.js new file mode 100644 index 000000000..b5b757164 --- /dev/null +++ b/js/FeedTree.js @@ -0,0 +1,434 @@ +dojo.provide("fox.FeedTree"); +dojo.provide("fox.FeedStoreModel"); + +dojo.require("dijit.Tree"); +dojo.require("dijit.Menu"); + +dojo.declare("fox.FeedStoreModel", dijit.tree.ForestStoreModel, { + getItemsInCategory: function (id) { + if (!this.store._itemsByIdentity) return undefined; + + cat = this.store._itemsByIdentity['CAT:' + id]; + + if (cat && cat.items) + return cat.items; + else + return undefined; + + }, + getItemById: function(id) { + return this.store._itemsByIdentity[id]; + }, + getFeedValue: function(feed, is_cat, key) { + if (!this.store._itemsByIdentity) return undefined; + + if (is_cat) + treeItem = this.store._itemsByIdentity['CAT:' + feed]; + else + treeItem = this.store._itemsByIdentity['FEED:' + feed]; + + if (treeItem) + return this.store.getValue(treeItem, key); + }, + getFeedName: function(feed, is_cat) { + return this.getFeedValue(feed, is_cat, 'name'); + }, + getFeedUnread: function(feed, is_cat) { + var unread = parseInt(this.getFeedValue(feed, is_cat, 'unread')); + return (isNaN(unread)) ? 0 : unread; + }, + setFeedUnread: function(feed, is_cat, unread) { + return this.setFeedValue(feed, is_cat, 'unread', parseInt(unread)); + }, + setFeedValue: function(feed, is_cat, key, value) { + if (!value) value = ''; + if (!this.store._itemsByIdentity) return undefined; + + if (is_cat) + treeItem = this.store._itemsByIdentity['CAT:' + feed]; + else + treeItem = this.store._itemsByIdentity['FEED:' + feed]; + + if (treeItem) + return this.store.setValue(treeItem, key, value); + }, + getNextUnreadFeed: function (feed, is_cat) { + if (!this.store._itemsByIdentity) + return null; + + if (is_cat) { + treeItem = this.store._itemsByIdentity['CAT:' + feed]; + items = this.store._arrayOfTopLevelItems; + } else { + treeItem = this.store._itemsByIdentity['FEED:' + feed]; + items = this.store._arrayOfAllItems; + } + + for (var i = 0; i < items.length; i++) { + if (items[i] == treeItem) { + + for (var j = i+1; j < items.length; j++) { + var unread = this.store.getValue(items[j], 'unread'); + var id = this.store.getValue(items[j], 'id'); + + if (unread > 0 && (is_cat || id.match("FEED:"))) return items[j]; + } + + for (var j = 0; j < i; j++) { + var unread = this.store.getValue(items[j], 'unread'); + var id = this.store.getValue(items[j], 'id'); + + if (unread > 0 && (is_cat || id.match("FEED:"))) return items[j]; + } + } + } + + return null; + }, + hasCats: function() { + if (this.store && this.store._itemsByIdentity) + return this.store._itemsByIdentity['CAT:-1'] != undefined; + else + return false; + }, +}); + +dojo.declare("fox.FeedTree", dijit.Tree, { + _createTreeNode: function(args) { + var tnode = new dijit._TreeNode(args); + + if (args.item.icon) + tnode.iconNode.src = args.item.icon[0]; + + var id = args.item.id[0]; + var bare_id = parseInt(id.substr(id.indexOf(':')+1)); + + if (bare_id < -10) { + var span = dojo.doc.createElement('span'); + var fg_color = args.item.fg_color[0]; + var bg_color = args.item.bg_color[0]; + + span.innerHTML = "α"; + span.className = 'labelColorIndicator'; + span.setStyle({ + color: fg_color, + backgroundColor: bg_color}); + + dojo.place(span, tnode.iconNode, 'replace'); + } + + if (id.match("FEED:") && bare_id > 0) { + var menu = new dijit.Menu(); + menu.row_id = bare_id; + + menu.addChild(new dijit.MenuItem({ + label: __("Mark as read"), + onClick: function() { + catchupFeed(this.getParent().row_id); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Edit feed"), + onClick: function() { + editFeed(this.getParent().row_id, false); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Update feed"), + onClick: function() { + scheduleFeedUpdate(this.getParent().row_id, false); + }})); + + menu.bindDomNode(tnode.domNode); + tnode._menu = menu; + } + + if (id.match("CAT:") && bare_id > 0) { + var menu = new dijit.Menu(); + menu.row_id = bare_id; + + menu.addChild(new dijit.MenuItem({ + label: __("Mark as read"), + onClick: function() { + catchupFeed(this.getParent().row_id, true); + }})); + + menu.bindDomNode(tnode.domNode); + tnode._menu = menu; + } + + //tnode.labelNode.innerHTML = args.label; + return tnode; + }, + getIconClass: function (item, opened) { + return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon"; + }, + getLabelClass: function (item, opened) { + return (item.unread == 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread"; + }, + getRowClass: function (item, opened) { + return (!item.error || item.error == '') ? "dijitTreeRow" : + "dijitTreeRow Error"; + }, + getLabel: function(item) { + var name = String(item.name); + + /* Horrible */ + name = name.replace(/"/g, "\""); + name = name.replace(/&/g, "&"); + name = name.replace(/—/g, "-"); + name = name.replace(/</g, "<"); + name = name.replace(/>/g, ">"); + + if (item.unread > 0) { + return name + " (" + item.unread + ")"; + } else { + return name; + } + }, + selectFeed: function(feed, is_cat) { + if (is_cat) + treeNode = this._itemNodesMap['CAT:' + feed]; + else + treeNode = this._itemNodesMap['FEED:' + feed]; + + if (treeNode) { + treeNode = treeNode[0]; + if (!is_cat) this._expandNode(treeNode); + this.set("selectedNodes", [treeNode]); + } + }, + setFeedIcon: function(feed, is_cat, src) { + if (is_cat) + treeNode = this._itemNodesMap['CAT:' + feed]; + else + treeNode = this._itemNodesMap['FEED:' + feed]; + + if (treeNode) { + treeNode = treeNode[0]; + treeNode.iconNode.src = src; + return true; + } + return false; + }, + setFeedExpandoIcon: function(feed, is_cat, src) { + if (is_cat) + treeNode = this._itemNodesMap['CAT:' + feed]; + else + treeNode = this._itemNodesMap['FEED:' + feed]; + + if (treeNode) { + treeNode = treeNode[0]; + treeNode.expandoNode.src = src; + return true; + } + + return false; + }, + hasCats: function() { + return this.model.hasCats(); + }, + hideRead: function (hide, show_special) { + if (this.hasCats()) { + + var tree = this; + var cats = this.model.store._arrayOfTopLevelItems; + + cats.each(function(cat) { + var cat_unread = tree.hideReadFeeds(cat.items, hide, show_special); + + var id = String(cat.id); + var node = tree._itemNodesMap[id]; + var bare_id = parseInt(id.substr(id.indexOf(":")+1)); + + if (node) { + var check_unread = tree.model.getFeedUnread(bare_id, true); + + if (hide && cat_unread == 0 && check_unread == 0) { + Effect.Fade(node[0].rowNode, {duration : 0.3, + queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }}); + } else { + Element.show(node[0].rowNode); + ++cat_unread; + } + } + }); + + } else { + this.hideReadFeeds(this.model.store._arrayOfTopLevelItems, hide, + show_special); + } + }, + hideReadFeeds: function (items, hide, show_special) { + var tree = this; + var cat_unread = 0; + + items.each(function(feed) { + var id = String(feed.id); + var bare_id = parseInt(feed.bare_id);; + + var unread = feed.unread[0]; + var node = tree._itemNodesMap[id]; + + if (node) { + if (hide && unread == 0 && (bare_id > 0 || !show_special)) { + Effect.Fade(node[0].rowNode, {duration : 0.3, + queue: { position: 'end', scope: 'FFADE-' + id, limit: 1 }}); + } else { + Element.show(node[0].rowNode); + ++cat_unread; + } + } + }); + + return cat_unread; + }, + collapseCat: function(id) { + if (!this.model.hasCats()) return; + + var tree = this; + + var node = tree._itemNodesMap['CAT:' + id][0]; + var item = tree.model.store._itemsByIdentity['CAT:' + id]; + + if (node && item) { + var hidden = tree.model.store.getValue(item, 'hidden'); + + if (hidden) + tree._expandNode(node); + else + tree._collapseNode(node); + + tree.model.store.setValue(item, 'hidden', !hidden); + } + }, + collapseHiddenCats: function() { + if (!this.model.hasCats()) return; + + var cats = this.model.store._arrayOfTopLevelItems; + var tree = this; + + dojo.forEach(cats, function(cat) { + var hidden = tree.model.store.getValue(cat, 'hidden'); + var id = tree.model.store.getValue(cat, 'id'); + var node = tree._itemNodesMap[id][0]; + + if (hidden) + tree._collapseNode(node); + else + tree._expandNode(node); + + }); + }, + getVisibleUnreadFeeds: function() { + var items = this.model.store._arrayOfAllItems; + var rv = []; + + for (var i = 0; i < items.length; i++) { + var id = String(items[i].id); + var box = this._itemNodesMap[id]; + + if (box) { + var row = box[0].rowNode; + var cat = false; + + try { + cat = box[0].rowNode.parentNode.parentNode; + } catch (e) { } + + if (row) { + if (Element.visible(row) && (!cat || Element.visible(cat))) { + var feed_id = String(items[i].bare_id); + var is_cat = !id.match('FEED:'); + var unread = this.model.getFeedUnread(feed_id, is_cat); + + if (unread > 0) + rv.push([feed_id, is_cat]); + + } + } + } + } + + return rv; + }, + getNextFeed: function (feed, is_cat) { + if (is_cat) { + treeItem = this.model.store._itemsByIdentity['CAT:' + feed]; + } else { + treeItem = this.model.store._itemsByIdentity['FEED:' + feed]; + } + + items = this.model.store._arrayOfAllItems; + var item = items[0]; + + for (var i = 0; i < items.length; i++) { + if (items[i] == treeItem) { + + for (var j = i+1; j < items.length; j++) { + var id = String(items[j].id); + var box = this._itemNodesMap[id]; + + if (box) { + var row = box[0].rowNode; + var cat = box[0].rowNode.parentNode.parentNode; + + if (Element.visible(cat) && Element.visible(row)) { + item = items[j]; + break; + } + } + } + break; + } + } + + if (item) { + return [this.model.store.getValue(item, 'bare_id'), + !this.model.store.getValue(item, 'id').match('FEED:')]; + } else { + return false; + } + }, + getPreviousFeed: function (feed, is_cat) { + if (is_cat) { + treeItem = this.model.store._itemsByIdentity['CAT:' + feed]; + } else { + treeItem = this.model.store._itemsByIdentity['FEED:' + feed]; + } + + items = this.model.store._arrayOfAllItems; + var item = items[0]; + + for (var i = 0; i < items.length; i++) { + if (items[i] == treeItem) { + + for (var j = i-1; j > 0; j--) { + var id = String(items[j].id); + var box = this._itemNodesMap[id]; + + if (box) { + var row = box[0].rowNode; + var cat = box[0].rowNode.parentNode.parentNode; + + if (Element.visible(cat) && Element.visible(row)) { + item = items[j]; + break; + } + } + + } + break; + } + } + + if (item) { + return [this.model.store.getValue(item, 'bare_id'), + !this.model.store.getValue(item, 'id').match('FEED:')]; + } else { + return false; + } + + }, + +}); diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js new file mode 100644 index 000000000..4ea486609 --- /dev/null +++ b/js/PrefFeedTree.js @@ -0,0 +1,79 @@ +dojo.provide("fox.PrefFeedTree"); +dojo.provide("fox.PrefFeedStore"); + +dojo.require("lib.CheckBoxTree"); +dojo.require("dojo.data.ItemFileWriteStore"); + +dojo.declare("fox.PrefFeedStore", dojo.data.ItemFileWriteStore, { + + _saveEverything: function(saveCompleteCallback, saveFailedCallback, + newFileContentString) { + + dojo.xhrPost({ + url: "backend.php", + content: {op: "pref-feeds", subop: "savefeedorder", + payload: newFileContentString}, + error: saveFailedCallback, + load: saveCompleteCallback}); + }, + +}); + +dojo.declare("fox.PrefFeedTree", lib.CheckBoxTree, { + _createTreeNode: function(args) { + var tnode = this.inherited(arguments); + + if (args.item.icon) + tnode.iconNode.src = args.item.icon[0]; + + var param = this.model.store.getValue(args.item, 'param'); + + if (param) { + param = dojo.doc.createElement('span'); + param.className = 'feedParam'; + param.innerHTML = args.item.param[0]; + dojo.place(param, tnode.labelNode, 'after'); + } + + return tnode; + }, + onDndDrop: function() { + this.inherited(arguments); + this.tree.model.store.save(); + }, + getRowClass: function (item, opened) { + return (!item.error || item.error == '') ? "dijitTreeRow" : + "dijitTreeRow Error"; + }, + getIconClass: function (item, opened) { + return (!item || this.model.store.getValue(item, 'type') == 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon"; + }, + checkItemAcceptance: function(target, source, position) { + var item = dijit.getEnclosingWidget(target).item; + + // disable copying items + source.copyState = function() { return false; }; + + var source_item = false; + + source.forInSelectedItems(function(node) { + source_item = node.data.item; + }); + + if (!source_item || !item) return false; + + var id = this.tree.model.store.getValue(item, 'id'); + var source_id = source.tree.model.store.getValue(source_item, 'id'); + + //console.log(id + " " + position + " " + source_id); + + if (source_id.match("FEED:")) { + return ((id.match("CAT:") && position == "over") || + (id.match("FEED:") && position != "over")); + } else if (source_id.match("CAT:")) { + return ((id.match("CAT:") && position != "over") || + (id.match("root") && position == "over")); + } + }, +}); + diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js new file mode 100644 index 000000000..a4cf3dac8 --- /dev/null +++ b/js/PrefFilterTree.js @@ -0,0 +1,52 @@ +dojo.provide("fox.PrefFilterTree"); + +dojo.require("lib.CheckBoxTree"); + +dojo.declare("fox.PrefFilterTree", lib.CheckBoxTree, { + _createTreeNode: function(args) { + var tnode = this.inherited(arguments); + + var enabled = this.model.store.getValue(args.item, 'enabled'); + var param = this.model.store.getValue(args.item, 'param'); + + if (param) { + param = dojo.doc.createElement('span'); + param.className = (enabled != false) ? 'labelParam' : 'labelParam Disabled'; + param.innerHTML = args.item.param[0]; + dojo.place(param, tnode.labelNode, 'after'); + } + + return tnode; + }, + + getLabel: function(item) { + var label = item.name; + + var feed = this.model.store.getValue(item, 'feed'); + var inverse = this.model.store.getValue(item, 'inverse'); + + if (feed) + label += " (" + __("Feed:") + " " + feed + ")"; + + if (inverse) + label += " (" + __("Inverse") + ")"; + +/* if (item.param) + label = "" + label + + "" + item.param[0]; */ + + return label; + }, + getIconClass: function (item, opened) { + return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; + }, + getLabelClass: function (item, opened) { + var enabled = this.model.store.getValue(item, 'enabled'); + return (enabled != false) ? "dijitTreeLabel labelFixedLength" : "dijitTreeLabel labelFixedLength Disabled"; + }, + getRowClass: function (item, opened) { + return (!item.error || item.error == '') ? "dijitTreeRow" : + "dijitTreeRow Error"; + }, +}); + diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js new file mode 100644 index 000000000..05a0c15b6 --- /dev/null +++ b/js/PrefLabelTree.js @@ -0,0 +1,43 @@ +dojo.provide("fox.PrefLabelTree"); + +dojo.require("lib.CheckBoxTree"); +dojo.require("dijit.form.DropDownButton"); + +dojo.declare("fox.PrefLabelTree", lib.CheckBoxTree, { + setNameById: function (id, name) { + var item = this.model.store._itemsByIdentity['LABEL:' + id]; + + if (item) + this.model.store.setValue(item, 'name', name); + + }, + _createTreeNode: function(args) { + var tnode = this.inherited(arguments); + + var fg_color = this.model.store.getValue(args.item, 'fg_color'); + var bg_color = this.model.store.getValue(args.item, 'bg_color'); + var type = this.model.store.getValue(args.item, 'type'); + var bare_id = this.model.store.getValue(args.item, 'bare_id'); + + if (type == 'label') { + var span = dojo.doc.createElement('span'); + span.innerHTML = 'α'; + span.className = 'labelColorIndicator2'; + span.id = 'LICID-' + bare_id; + + span.setStyle({ + color: fg_color, + backgroundColor: bg_color}); + + tnode._labelIconNode = span; + + dojo.place(tnode._labelIconNode, tnode.labelNode, 'before'); + } + + return tnode; + }, + getIconClass: function (item, opened) { + return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; + }, +}); + diff --git a/js/deprecated.js b/js/deprecated.js new file mode 100644 index 000000000..1d04a1adc --- /dev/null +++ b/js/deprecated.js @@ -0,0 +1,29 @@ +function selectTableRow(r, do_select) { + + if (do_select) { + r.addClassName("Selected"); + } else { + r.removeClassName("Selected"); + } +} + +function selectTableRowById(elem_id, check_id, do_select) { + + try { + + var row = $(elem_id); + + if (row) { + selectTableRow(row, do_select); + } + + var check = $(check_id); + + if (check) { + check.checked = do_select; + } + } catch (e) { + exception_error("selectTableRowById", e); + } +} + diff --git a/js/digest.js b/js/digest.js new file mode 100644 index 000000000..7dba6d36e --- /dev/null +++ b/js/digest.js @@ -0,0 +1,841 @@ +var last_feeds = []; +var init_params = {}; + +var _active_feed_id = false; +var _update_timeout = false; +var _view_update_timeout = false; +var _feedlist_expanded = false; +var _update_seq = 1; + +function article_appear(article_id) { + try { + new Effect.Appear('A-' + article_id); + } catch (e) { + exception_error("article_appear", e); + } +} + +function catchup_feed(feed_id, callback) { + try { + + var fn = find_feed(last_feeds, feed_id).title; + + if (confirm(__("Mark all articles in %s as read?").replace("%s", fn))) { + + var is_cat = ""; + + if (feed_id < 0) is_cat = "true"; // KLUDGE + + var query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed_id + "&is_cat=" + is_cat; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) callback(transport); + + update(); + } }); + } + + } catch (e) { + exception_error("catchup_article", e); + } +} + +function get_visible_article_ids() { + try { + var elems = $("headlines-content").getElementsByTagName("LI"); + var ids = []; + + for (var i = 0; i < elems.length; i++) { + if (elems[i].id && elems[i].id.match("A-")) { + ids.push(elems[i].id.replace("A-", "")); + } + } + + return ids; + + } catch (e) { + exception_error("get_visible_article_ids", e); + } +} + +function catchup_visible_articles(callback) { + try { + + var ids = get_visible_article_ids(); + + if (confirm(__("Mark %d displayed articles as read?").replace("%d", ids.length))) { + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + param_escape(ids); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) callback(transport); + + viewfeed(_active_feed_id, 0); + } }); + + } + + } catch (e) { + exception_error("catchup_visible_articles", e); + } +} + +function catchup_article(article_id, callback) { + try { + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + article_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) callback(transport); + } }); + + } catch (e) { + exception_error("catchup_article", e); + } +} + +function set_selected_article(article_id) { + try { + $$("#headlines-content > li[id*=A-]").each(function(article) { + var id = article.id.replace("A-", ""); + + var cb = article.getElementsByTagName("INPUT")[0]; + + if (id == article_id) { + article.addClassName("selected"); + cb.checked = true; + } else { + article.removeClassName("selected"); + cb.checked = false; + } + + }); + + } catch (e) { + exception_error("mark_selected_feed", e); + } +} + + +function set_selected_feed(feed_id) { + try { + var feeds = $("feeds-content").getElementsByTagName("LI"); + + for (var i = 0; i < feeds.length; i++) { + if (feeds[i].id == "F-" + feed_id) + feeds[i].className = "selected"; + else + feeds[i].className = ""; + } + + _active_feed_id = feed_id; + + } catch (e) { + exception_error("mark_selected_feed", e); + } +} + +function load_more() { + try { + var pr = $("H-LOADING-IMG"); + + if (pr) Element.show(pr); + + var offset = $$("#headlines-content > li[id*=A-][class*=fresh],li[id*=A-][class*=unread]").length; + + viewfeed(false, offset, false, false, true, + function() { + var pr = $("H-LOADING-IMG"); + + if (pr) Element.hide(pr); + }); + } catch (e) { + exception_error("load_more", e); + } +} + +function update(callback) { + try { + console.log('updating feeds...'); + + window.clearTimeout(_update_timeout); + + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=digest-init", + onComplete: function(transport) { + fatal_error_check(transport); + parse_feeds(transport); + set_selected_feed(_active_feed_id); + + if (callback) callback(transport); + } }); + + _update_timeout = window.setTimeout('update()', 5*1000); + } catch (e) { + exception_error("update", e); + } +} + +function remove_headline_entry(article_id) { + try { + var elem = $('A-' + article_id); + + if (elem) { + elem.parentNode.removeChild(elem); + } + + } catch (e) { + exception_error("remove_headline_entry", e); + } +} + +function view_update() { + try { + viewfeed(_active_feed_id, _active_feed_offset, false, true, true); + update(); + } catch (e) { + exception_error("view_update", e); + } +} + +function view(article_id) { + try { + $("content").addClassName("move"); + + var a = $("A-" + article_id); + var h = $("headlines"); + + setTimeout(function() { + // below or above viewport, reposition headline + if (a.offsetTop > h.scrollTop + h.offsetHeight || a.offsetTop+a.offsetHeight < h.scrollTop+a.offsetHeight) + h.scrollTop = a.offsetTop - (h.offsetHeight/2 - a.offsetHeight/2); + }, 500); + + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=digest-get-contents&article_id=" + + article_id, + onComplete: function(transport) { + fatal_error_check(transport); + + var reply = JSON.parse(transport.responseText); + + if (reply) { + var article = reply['article']; + + var mark_part = ""; + var publ_part = ""; + + var tags_part = ""; + + if (article.tags.length > 0) { + tags_part = " " + __("in") + " "; + + for (var i = 0; i < Math.min(5, article.tags.length); i++) { + //tags_part += "" + + // article.tags[i] + ", "; + + tags_part += article.tags[i] + ", "; + } + + tags_part = tags_part.replace(/, $/, ""); + tags_part = "" + tags_part + ""; + + } + + if (article.marked) + mark_part = ""; + else + mark_part = ""; + + if (article.published) + publ_part = ""; + else + publ_part = ""; + + var tmp = "" + + "
    " + + "
    " + + mark_part + + publ_part + + "
    " + + "

    " + article.title + "

    " + + "
    " + + tags_part + + "
    " + + article.content + "
    "; + + $("article-content").innerHTML = tmp; + $("article").addClassName("visible"); + + set_selected_article(article.id); + + catchup_article(article_id, + function() { + $("A-" + article_id).addClassName("read"); + }); + + } else { + elem.innerHTML = __("Error: unable to load article."); + } + } + }); + + + return false; + } catch (e) { + exception_error("view", e); + } +} + +function close_article() { + $("content").removeClassName("move"); + $("article").removeClassName("visible"); +} + +function viewfeed(feed_id, offset, replace, no_effects, no_indicator, callback) { + try { + + if (!feed_id) feed_id = _active_feed_id; + if (offset == undefined) offset = 0; + if (replace == undefined) replace = (offset == 0); + + _update_seq = _update_seq + 1; + + if (!offset) $("headlines").scrollTop = 0; + + var query = "backend.php?op=rpc&subop=digest-update&feed_id=" + + param_escape(feed_id) + "&offset=" + offset + + "&seq=" + _update_seq; + + console.log(query); + + var img = false; + + if ($("F-" + feed_id)) { + img = $("F-" + feed_id).getElementsByTagName("IMG")[0]; + + if (img && !no_indicator) { + img.setAttribute("orig_src", img.src); + img.src = 'images/indicator_tiny.gif'; + } + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + Element.hide("overlay"); + + fatal_error_check(transport); + parse_headlines(transport, replace, no_effects); + set_selected_feed(feed_id); + _active_feed_offset = offset; + + if (img && !no_indicator) + img.src = img.getAttribute("orig_src"); + + if (callback) callback(transport); + + } }); + + } catch (e) { + exception_error("view", e); + } +} + +function find_article(articles, article_id) { + try { + for (var i = 0; i < articles.length; i++) { + if (articles[i].id == article_id) + return articles[i]; + } + + return false; + + } catch (e) { + exception_error("find_article", e); + } +} + +function find_feed(feeds, feed_id) { + try { + for (var i = 0; i < feeds.length; i++) { + if (feeds[i].id == feed_id) + return feeds[i]; + } + + return false; + + } catch (e) { + exception_error("find_feed", e); + } +} + +function get_feed_icon(feed) { + try { + if (feed.has_icon) + return getInitParam('icons_url') + "/" + feed.id + '.ico'; + + if (feed.id == -1) + return 'images/mark_set.png'; + + if (feed.id == -2) + return 'images/pub_set.png'; + + if (feed.id == -3) + return 'images/fresh.png'; + + if (feed.id == -4) + return 'images/tag.png'; + + if (feed.id < -10) + return 'images/label.png'; + + return 'images/blank_icon.gif'; + + } catch (e) { + exception_error("get_feed_icon", e); + } +} + +function add_feed_entry(feed) { + try { + var icon_part = ""; + + icon_part = ""; + + var tmp_html = "
  • " + + icon_part + feed.title + + "
    " + "" + feed.unread + "" + + "
    " + "
  • "; + + $("feeds-content").innerHTML += tmp_html; + + + } catch (e) { + exception_error("add_feed_entry", e); + } +} + +function add_headline_entry(article, feed, no_effects) { + try { + + var icon_part = ""; + + icon_part = ""; + + + var style = ""; + + //if (!no_effects) style = "style=\"display : none\""; + + if (article.excerpt.trim() == "") + article.excerpt = __("Click to expand article."); + + var li_class = "unread"; + + var fresh_max = getInitParam("fresh_article_max_age") * 60 * 60; + var d = new Date(); + + if (d.getTime() / 1000 - article.updated < fresh_max) + li_class = "fresh"; + + //"" + + + //"" + + + var checkbox_part = ""; + + var date = new Date(article.updated * 1000); + + var date_part = date.toString().substring(0,21); + + var tmp_html = "
  • " + + checkbox_part + + icon_part + + "" + + article.title + "" + + "
    " + + "
    " + + article.excerpt + "
    " + + "
    "; + +/* tmp_html += "" + + feed.title + " " + " @ "; */ + + tmp_html += date_part + "
    " + + "
  • "; + + $("headlines-content").innerHTML += tmp_html; + + if (!no_effects) + window.setTimeout('article_appear(' + article.id + ')', 100); + + } catch (e) { + exception_error("add_headline_entry", e); + } +} + +function expand_feeds() { + try { + _feedlist_expanded = true; + + redraw_feedlist(last_feeds); + + } catch (e) { + exception_error("expand_feeds", e); + } +} + +function redraw_feedlist(feeds) { + try { + + $('feeds-content').innerHTML = ""; + + var limit = 10; + + if (_feedlist_expanded) limit = feeds.length; + + for (var i = 0; i < Math.min(limit, feeds.length); i++) { + add_feed_entry(feeds[i]); + } + + if (feeds.length > limit) { + $('feeds-content').innerHTML += "
  • " + + "" + + "" + + __("%d more...").replace("%d", feeds.length-10) + + "" + "
  • "; + } + + if (feeds.length == 0) { + $('feeds-content').innerHTML = + "
    " + + __("No unread feeds.") + "
    "; + } + + if (_active_feed_id) + set_selected_feed(_active_feed_id); + + } catch (e) { + exception_error("redraw_feedlist", e); + } +} + +function parse_feeds(transport) { + try { + var reply = JSON.parse(transport.responseText); + + if (!reply) return; + + var feeds = reply['feeds']; + + if (feeds) { + + feeds.sort( function (a,b) + { + if (b.unread != a.unread) + return (b.unread - a.unread); + else + if (a.title > b.title) + return 1; + else if (a.title < b.title) + return -1; + else + return 0; + }); + + var all_articles = find_feed(feeds, -4); + + update_title(all_articles.unread); + + last_feeds = feeds; + + redraw_feedlist(feeds); + } + + } catch (e) { + exception_error("parse_feeds", e); + } +} + +function parse_headlines(transport, replace, no_effects) { + try { + var reply = JSON.parse(transport.responseText); + if (!reply) return; + + var seq = reply['seq']; + + if (seq) { + if (seq != _update_seq) { + console.log("parse_headlines: wrong sequence received."); + return; + } + } else { + return; + } + + var headlines = reply['headlines']['content']; + var headlines_title = reply['headlines']['title']; + + if (headlines && headlines_title) { + + if (replace) { + $('headlines-content').innerHTML = ''; + } + + var pr = $('H-MORE-PROMPT'); + + if (pr) pr.parentNode.removeChild(pr); + + var inserted = false; + + for (var i = 0; i < headlines.length; i++) { + + if (!$('A-' + headlines[i].id)) { + add_headline_entry(headlines[i], + find_feed(last_feeds, headlines[i].feed_id), !no_effects); + + } + } + + console.log(inserted.id); + + var ids = get_visible_article_ids(); + + if (ids.length > 0) { + if (pr) { + $('headlines-content').appendChild(pr); + + } else { + $('headlines-content').innerHTML += "
  • " + + "
  • "; + } + } else { + // FIXME : display some kind of "nothing to see here" prompt here + } + +// if (replace && !no_effects) +// new Effect.Appear('headlines-content', {duration : 0.3}); + + //new Effect.Appear('headlines-content'); + } + + } catch (e) { + exception_error("parse_headlines", e); + } +} + +function init_second_stage() { + try { + new Ajax.Request("backend.php", { + parameters: "backend.php?op=rpc&subop=digest-init", + onComplete: function(transport) { + parse_feeds(transport); + Element.hide("overlay"); + + window.setTimeout('viewfeed(-4)', 100); + _update_timeout = window.setTimeout('update()', 5*1000); + } }); + + } catch (e) { + exception_error("init_second_stage", e); + } +} + +function init() { + try { + dojo.require("dijit.Dialog"); + + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=sanityCheck", + onComplete: function(transport) { + backend_sanity_check_callback(transport); + } }); + + } catch (e) { + exception_error("digest_init", e); + } +} + +function toggle_mark(img, id) { + + try { + + var query = "?op=rpc&id=" + id + "&subop=mark"; + + if (!img) return; + + if (img.src.match("mark_unset")) { + img.src = img.src.replace("mark_unset", "mark_set"); + img.alt = __("Unstar article"); + query = query + "&mark=1"; + } else { + img.src = img.src.replace("mark_set", "mark_unset"); + img.alt = __("Star article"); + query = query + "&mark=0"; + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + update(); + } }); + + } catch (e) { + exception_error("toggle_mark", e); + } +} + +function toggle_pub(img, id, note) { + + try { + + var query = "?op=rpc&id=" + id + "&subop=publ"; + + if (note != undefined) { + query = query + "¬e=" + param_escape(note); + } else { + query = query + "¬e=undefined"; + } + + if (!img) return; + + if (img.src.match("pub_unset") || note != undefined) { + img.src = img.src.replace("pub_unset", "pub_set"); + img.alt = __("Unpublish article"); + query = query + "&pub=1"; + + } else { + img.src = img.src.replace("pub_set", "pub_unset"); + img.alt = __("Publish article"); + query = query + "&pub=0"; + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + update(); + } }); + + } catch (e) { + exception_error("toggle_pub", e); + } +} + +function fatal_error(code, msg) { + try { + + if (code == 6) { + window.location.href = "digest.php"; + } else if (code == 5) { + window.location.href = "db-updater.php"; + } else { + + if (msg == "") msg = "Unknown error"; + + console.error("Fatal error: " + code + "\n" + + msg); + + } + + } catch (e) { + exception_error("fatalError", e); + } +} + +function fatal_error_check(transport) { + try { + if (transport.responseXML) { + var error = transport.responseXML.getElementsByTagName("error")[0]; + + if (error) { + var code = error.getAttribute("error-code"); + var msg = error.getAttribute("error-msg"); + if (code != 0) { + fatal_error(code, msg); + return false; + } + } + } + } catch (e) { + exception_error("fatal_error_check", e); + } + return true; +} + +function update_title(unread) { + try { + document.title = "Tiny Tiny RSS"; + + if (unread > 0) + document.title += " (" + unread + ")"; + + } catch (e) { + exception_error("update_title", e); + } +} + +function tweet_article(id) { + try { + + var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id); + + console.log(query); + + var d = new Date(); + var ts = d.getTime(); + + var w = window.open('backend.php?op=loading', 'ttrss_tweet', + "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0"); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var ti = JSON.parse(transport.responseText); + + var share_url = "http://twitter.com/share?_=" + ts + + "&text=" + param_escape(ti.title) + + "&url=" + param_escape(ti.link); + + w.location.href = share_url; + + } }); + + } catch (e) { + exception_error("tweet_article", e); + } +} + +function toggle_select_article(elem) { + try { + var article = elem.parentNode; + + if (article.hasClassName("selected")) + article.removeClassName("selected"); + else + article.addClassName("selected"); + + } catch (e) { + exception_error("toggle_select_article", e); + } +} diff --git a/js/feedlist.js b/js/feedlist.js new file mode 100644 index 000000000..62c44b494 --- /dev/null +++ b/js/feedlist.js @@ -0,0 +1,505 @@ +var _infscroll_disable = 0; +var _infscroll_request_sent = 0; +var _search_query = false; + +var counter_timeout_id = false; + +var counters_last_request = 0; + +function viewCategory(cat) { + viewfeed(cat, '', true); + return false; +} + +function loadMoreHeadlines() { + try { + console.log("loadMoreHeadlines"); + + var offset = 0; + + var view_mode = document.forms["main_toolbar_form"].view_mode.value; + var num_unread = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length; + var num_all = $$("#headlines-frame > div[id*=RROW]").length; + + // TODO implement marked & published + + if (view_mode == "marked") { + console.warn("loadMoreHeadlines: marked is not implemented, falling back."); + offset = num_all; + } else if (view_mode == "published") { + console.warn("loadMoreHeadlines: published is not implemented, falling back."); + offset = num_all; + } else if (view_mode == "unread") { + offset = num_unread; + } else if (view_mode == "adaptive") { + if (num_unread > 0) + offset = num_unread; + else + offset = num_all; + } else { + offset = num_all; + } + + viewfeed(getActiveFeedId(), '', activeFeedIsCat(), offset, false, true); + + } catch (e) { + exception_error("viewNextFeedPage", e); + } +} + + +function viewfeed(feed, subop, is_cat, offset, background, infscroll_req) { + try { + if (is_cat == undefined) + is_cat = false; + else + is_cat = !!is_cat; + + if (subop == undefined) subop = ''; + if (offset == undefined) offset = 0; + if (background == undefined) background = false; + if (infscroll_req == undefined) infscroll_req = false; + + last_requested_article = 0; + + var cached_headlines = false; + + if (feed == getActiveFeedId()) { + cache_delete("feed:" + feed + ":" + is_cat); + } else { + cached_headlines = cache_get("feed:" + feed + ":" + is_cat); + + // switching to a different feed, we might as well catchup stuff visible + // in headlines buffer (if any) + if (!background && getInitParam("cdm_auto_catchup") == 1 && parseInt(getActiveFeedId()) > 0) { + + $$("#headlines-frame > div[id*=RROW][class*=Unread]").each( + function(child) { + var hf = $("headlines-frame"); + + if (hf.scrollTop + hf.offsetHeight >= + child.offsetTop + child.offsetHeight) { + + var id = child.id.replace("RROW-", ""); + + if (catchup_id_batch.indexOf(id) == -1) + catchup_id_batch.push(id); + + } + + if (catchup_id_batch.length > 0) { + window.clearTimeout(catchup_timeout_id); + + if (!_infscroll_request_sent) { + catchup_timeout_id = window.setTimeout('catchupBatchedArticles()', + 2000); + } + } + + }); + } + } + + if (offset == 0 && !background) + dijit.byId("content-tabs").selectChild( + dijit.byId("content-tabs").getChildren()[0]); + + if (!background) { + if (getActiveFeedId() != feed || offset == 0) { + active_post_id = 0; + _infscroll_disable = 0; + } + + if (!offset && !subop && cached_headlines && !background) { + try { + render_local_headlines(feed, is_cat, JSON.parse(cached_headlines)); + return; + } catch (e) { + console.warn("render_local_headlines failed: " + e); + } + } + + if (offset != 0 && !subop) { + var date = new Date(); + var timestamp = Math.round(date.getTime() / 1000); + + if (_infscroll_request_sent && _infscroll_request_sent + 30 > timestamp) { + //console.log("infscroll request in progress, aborting"); + return; + } + + _infscroll_request_sent = timestamp; + } + + hideAuxDlg(); + } + + Form.enable("main_toolbar_form"); + + var toolbar_query = Form.serialize("main_toolbar_form"); + + var query = "?op=viewfeed&feed=" + feed + "&" + + toolbar_query + "&subop=" + param_escape(subop); + + if (!background) { + if (_search_query) { + force_nocache = true; + query = query + "&" + _search_query; + _search_query = false; + } + + if (subop == "MarkAllRead") { + + var show_next_feed = getInitParam("on_catchup_show_next_feed") == "1"; + + if (show_next_feed) { + var nuf = getNextUnreadFeed(feed, is_cat); + + if (nuf) { + var cached_nuf = cache_get("feed:" + nuf + ":false"); + + if (cached_nuf) { + + render_local_headlines(nuf, false, JSON.parse(cached_nuf)); + + var catchup_query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed + "&is_cat=" + is_cat; + + console.log(catchup_query); + + new Ajax.Request("backend.php", { + parameters: catchup_query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + return; + } else { + query += "&nuf=" + param_escape(nuf); + } + } + } + } + + if (offset != 0) { + query = query + "&skip=" + offset; + + // to prevent duplicate feed titles when showing grouped vfeeds + if (vgroup_last_feed) { + query = query + "&vgrlf=" + param_escape(vgroup_last_feed); + } + } + + Form.enable("main_toolbar_form"); + + if (!offset) + if (!is_cat) { + if (!setFeedExpandoIcon(feed, is_cat, 'images/indicator_white.gif')) + notify_progress("Loading, please wait...", true); + } else { + notify_progress("Loading, please wait...", true); + } + } + + query += "&cat=" + is_cat; + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + setFeedExpandoIcon(feed, is_cat, 'images/blank_icon.gif'); + headlines_callback2(transport, offset, background, infscroll_req); + } }); + + } catch (e) { + exception_error("viewfeed", e); + } +} + +function feedlist_init() { + try { + console.log("in feedlist init"); + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + document.onkeydown = hotkey_handler; + setTimeout("hotkey_prefix_timeout()", 5*1000); + + if (!getActiveFeedId()) { + setTimeout("viewfeed(-3)", 100); + } + + console.log("T:" + + getInitParam("cdm_auto_catchup") + " " + getFeedUnread(-3)); + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + + setTimeout("timeout()", 5000); + setTimeout("precache_headlines_idle()", 3000); + + } catch (e) { + exception_error("feedlist/init", e); + } +} + +function request_counters_real() { + try { + console.log("requesting counters..."); + + var query = "?op=rpc&subop=getAllCounters&seq=" + next_seq(); + + query = query + "&omode=flc"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + try { + handle_rpc_json(transport); + } catch (e) { + exception_error("viewfeed/getcounters", e); + } + } }); + + } catch (e) { + exception_error("request_counters_real", e); + } +} + + +function request_counters() { + + try { + + if (getInitParam("bw_limit") == "1") return; + + var date = new Date(); + var timestamp = Math.round(date.getTime() / 1000); + + if (timestamp - counters_last_request > 5) { + console.log("scheduling request of counters..."); + + window.clearTimeout(counter_timeout_id); + counter_timeout_id = window.setTimeout("request_counters_real()", 1000); + + counters_last_request = timestamp; + } else { + console.log("request_counters: rate limit reached: " + (timestamp - counters_last_request)); + } + + } catch (e) { + exception_error("request_counters", e); + } +} + +function displayNewContentPrompt(id) { + try { + + var msg = "" + + __("New articles available in this feed (click to show)") + ""; + + msg = msg.replace("%s", getFeedName(id)); + + $('auxDlg').innerHTML = msg; + + new Effect.Appear('auxDlg', {duration : 0.5}); + + } catch (e) { + exception_error("displayNewContentPrompt", e); + } +} + +function parse_counters(elems, scheduled_call) { + try { + for (var l = 0; l < elems.length; l++) { + + var id = elems[l].id; + var kind = elems[l].kind; + var ctr = parseInt(elems[l].counter); + var error = elems[l].error; + var has_img = elems[l].has_img; + var updated = elems[l].updated; + + if (id == "global-unread") { + global_unread = ctr; + updateTitle(); + continue; + } + + if (id == "subscribed-feeds") { + feeds_found = ctr; + continue; + } + + // TODO: enable new content notification for categories + + if (!activeFeedIsCat() && id == getActiveFeedId() + && ctr > getFeedUnread(id) && scheduled_call) { + displayNewContentPrompt(id); + } + + if (getFeedUnread(id, (kind == "cat")) != ctr) + cache_delete("feed:" + id + ":" + (kind == "cat")); + + setFeedUnread(id, (kind == "cat"), ctr); + + if (kind != "cat") { + setFeedValue(id, false, 'error', error); + setFeedValue(id, false, 'updated', updated); + + if (id > 0) { + if (has_img) { + setFeedIcon(id, false, + getInitParam("icons_url") + "/" + id + ".ico"); + } else { + setFeedIcon(id, false, 'images/blank_icon.gif'); + } + } + } + } + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + + } catch (e) { + exception_error("parse_counters", e); + } +} + +function getFeedUnread(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.getFeedUnread(feed, is_cat); + + } catch (e) { + // + } + + return -1; +} + +function hideOrShowFeeds(hide) { + var tree = dijit.byId("feedTree"); + + if (tree) + return tree.hideRead(hide, getInitParam("hide_read_shows_special")); +} + +function getFeedName(feed, is_cat) { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.getFeedValue(feed, is_cat, 'name'); +} + +function getFeedValue(feed, is_cat, key) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.getFeedValue(feed, is_cat, key); + + } catch (e) { + // + } + return ''; +} + +function setFeedUnread(feed, is_cat, unread) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.setFeedUnread(feed, is_cat, unread); + + } catch (e) { + exception_error("setFeedUnread", e); + } +} + +function setFeedValue(feed, is_cat, key, value) { + try { + var tree = dijit.byId("feedTree"); + + if (tree && tree.model) + return tree.model.setFeedValue(feed, is_cat, key, value); + + } catch (e) { + // + } +} + +function selectFeed(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + + if (tree) return tree.selectFeed(feed, is_cat); + + } catch (e) { + exception_error("selectFeed", e); + } +} + +function setFeedIcon(feed, is_cat, src) { + try { + var tree = dijit.byId("feedTree"); + + if (tree) return tree.setFeedIcon(feed, is_cat, src); + + } catch (e) { + exception_error("setFeedIcon", e); + } +} + +function setFeedExpandoIcon(feed, is_cat, src) { + try { + var tree = dijit.byId("feedTree"); + + if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src); + + } catch (e) { + exception_error("setFeedIcon", e); + } + return false; +} + +function getNextUnreadFeed(feed, is_cat) { + try { + var tree = dijit.byId("feedTree"); + var nuf = tree.model.getNextUnreadFeed(feed, is_cat); + + if (nuf) + return tree.model.store.getValue(nuf, 'bare_id'); + + } catch (e) { + exception_error("getNextUnreadFeed", e); + } +} + +function catchupFeed(feed, is_cat) { + try { + var str = __("Mark all articles in %s as read?"); + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + var catchup_query = "?op=rpc&subop=catchupFeed&feed_id=" + + feed + "&is_cat=" + is_cat; + + notify_progress("Loading, please wait...", true); + + new Ajax.Request("backend.php", { + parameters: catchup_query, + onComplete: function(transport) { + handle_rpc_json(transport); + notify(""); + } }); + + } catch (e) { + exception_error("catchupFeed", e); + } +} diff --git a/js/functions.js b/js/functions.js new file mode 100644 index 000000000..02fbadf53 --- /dev/null +++ b/js/functions.js @@ -0,0 +1,1657 @@ +var notify_silent = false; +var loading_progress = 0; +var sanity_check_done = false; + +/* add method to remove element from array */ + +Array.prototype.remove = function(s) { + for (var i=0; i < this.length; i++) { + if (s == this[i]) this.splice(i, 1); + } +}; + +/* create console.log if it doesn't exist */ + +if (!window.console) console = {}; +console.log = console.log || function(msg) { }; +console.warn = console.warn || function(msg) { }; +console.error = console.error || function(msg) { }; + +function exception_error(location, e, ext_info) { + var msg = format_exception_error(location, e); + + if (!ext_info) ext_info = false; + + try { + + if (ext_info) { + if (ext_info.responseText) { + ext_info = ext_info.responseText; + } + } + + var content = "
    " + + "
    " + msg + "
    "; + + content += "
    "; + + content += ""; + content += ""; + + if (ext_info) { + content += "
    Additional information:
    " + + ""; + } + + content += "
    Stack trace:
    " + + ""; + + content += "
    "; + + content += "
    "; + + content += "
    "; + + content += " "; + content += ""; + content += "
    "; + + if (dijit.byId("exceptionDlg")) + dijit.byId("exceptionDlg").destroyRecursive(); + + var dialog = new dijit.Dialog({ + id: "exceptionDlg", + title: "Unhandled exception", + style: "width: 600px", + report: function() { + if (confirm(__("Are you sure to report this exception to tt-rss.org? The report will include your browser information. Your IP would be saved in the database."))) { + + document.forms['exceptionForm'].params.value = $H({ + browserName: navigator.appName, + browserVersion: navigator.appVersion, + browserPlatform: navigator.platform, + browserCookies: navigator.cookieEnabled, + }).toQueryString(); + + document.forms['exceptionForm'].submit(); + + } + }, + content: content}); + + dialog.show(); + + } catch (e) { + alert(msg); + } + +} + +function format_exception_error(location, e) { + var msg; + + if (e.fileName) { + var base_fname = e.fileName.substring(e.fileName.lastIndexOf("/") + 1); + + msg = "Exception: " + e.name + ", " + e.message + + "\nFunction: " + location + "()" + + "\nLocation: " + base_fname + ":" + e.lineNumber; + + } else if (e.description) { + msg = "Exception: " + e.description + "\nFunction: " + location + "()"; + } else { + msg = "Exception: " + e + "\nFunction: " + location + "()"; + } + + console.error("EXCEPTION: " + msg); + + return msg; +} + +function param_escape(arg) { + if (typeof encodeURIComponent != 'undefined') + return encodeURIComponent(arg); + else + return escape(arg); +} + +function param_unescape(arg) { + if (typeof decodeURIComponent != 'undefined') + return decodeURIComponent(arg); + else + return unescape(arg); +} + +var notify_hide_timerid = false; + +function hide_notify() { + var n = $("notify"); + if (n) { + n.style.display = "none"; + } +} + +function notify_silent_next() { + notify_silent = true; +} + +function notify_real(msg, no_hide, n_type) { + + if (notify_silent) { + notify_silent = false; + return; + } + + var n = $("notify"); + var nb = $("notify_body"); + + if (!n || !nb) return; + + if (notify_hide_timerid) { + window.clearTimeout(notify_hide_timerid); + } + + if (msg == "") { + if (n.style.display == "block") { + notify_hide_timerid = window.setTimeout("hide_notify()", 0); + } + return; + } else { + n.style.display = "block"; + } + + /* types: + + 1 - generic + 2 - progress + 3 - error + 4 - info + + */ + + if (typeof __ != 'undefined') { + msg = __(msg); + } + + if (n_type == 1) { + n.className = "notify"; + } else if (n_type == 2) { + n.className = "notifyProgress"; + msg = " " + msg; + } else if (n_type == 3) { + n.className = "notifyError"; + msg = " " + msg; + } else if (n_type == 4) { + n.className = "notifyInfo"; + msg = " " + msg; + } + +// msg = " " + msg; + + nb.innerHTML = msg; + + if (!no_hide) { + notify_hide_timerid = window.setTimeout("hide_notify()", 3000); + } +} + +function notify(msg, no_hide) { + notify_real(msg, no_hide, 1); +} + +function notify_progress(msg, no_hide) { + notify_real(msg, no_hide, 2); +} + +function notify_error(msg, no_hide) { + notify_real(msg, no_hide, 3); + +} + +function notify_info(msg, no_hide) { + notify_real(msg, no_hide, 4); +} + +function setCookie(name, value, lifetime, path, domain, secure) { + + var d = false; + + if (lifetime) { + d = new Date(); + d.setTime(d.getTime() + (lifetime * 1000)); + } + + console.log("setCookie: " + name + " => " + value + ": " + d); + + int_setCookie(name, value, d, path, domain, secure); + +} + +function int_setCookie(name, value, expires, path, domain, secure) { + document.cookie= name + "=" + escape(value) + + ((expires) ? "; expires=" + expires.toGMTString() : "") + + ((path) ? "; path=" + path : "") + + ((domain) ? "; domain=" + domain : "") + + ((secure) ? "; secure" : ""); +} + +function delCookie(name, path, domain) { + if (getCookie(name)) { + document.cookie = name + "=" + + ((path) ? ";path=" + path : "") + + ((domain) ? ";domain=" + domain : "" ) + + ";expires=Thu, 01-Jan-1970 00:00:01 GMT"; + } +} + + +function getCookie(name) { + + var dc = document.cookie; + var prefix = name + "="; + var begin = dc.indexOf("; " + prefix); + if (begin == -1) { + begin = dc.indexOf(prefix); + if (begin != 0) return null; + } + else { + begin += 2; + } + var end = document.cookie.indexOf(";", begin); + if (end == -1) { + end = dc.length; + } + return unescape(dc.substring(begin + prefix.length, end)); +} + +function gotoPreferences() { + document.location.href = "prefs.php"; +} + +function gotoMain() { + document.location.href = "index.php"; +} + +function gotoExportOpml(filename, settings) { + tmp = settings ? 1 : 0; + document.location.href = "opml.php?op=Export&filename=" + filename + "&settings=" + tmp; +} + + +/** * @(#)isNumeric.js * * Copyright (c) 2000 by Sundar Dorai-Raj + * * @author Sundar Dorai-Raj + * * Email: sdoraira@vt.edu + * * This program is free software; you can redistribute it and/or + * * modify it under the terms of the GNU General Public License + * * as published by the Free Software Foundation; either version 2 + * * of the License, or (at your option) any later version, + * * provided that any use properly credits the author. + * * This program is distributed in the hope that it will be useful, + * * but WITHOUT ANY WARRANTY; without even the implied warranty of + * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * * GNU General Public License for more details at http://www.gnu.org * * */ + + var numbers=".0123456789"; + function isNumeric(x) { + // is x a String or a character? + if(x.length>1) { + // remove negative sign + x=Math.abs(x)+""; + for(var j=0;j=0) return true; + return false; + } + } + + +function toggleSelectRowById(sender, id) { + var row = $(id); + return toggleSelectRow(sender, row); +} + +function toggleSelectListRow(sender) { + var row = sender.parentNode; + return toggleSelectRow(sender, row); +} + +/* this is for dijit Checkbox */ +function toggleSelectListRow2(sender) { + var row = sender.domNode.parentNode; + return toggleSelectRow(sender, row); +} + +function tSR(sender, row) { + return toggleSelectRow(sender, row); +} + +/* this is for dijit Checkbox */ +function toggleSelectRow2(sender, row) { + + if (!row) row = sender.domNode.parentNode.parentNode; + + if (sender.checked && !row.hasClassName('Selected')) + row.addClassName('Selected'); + else + row.removeClassName('Selected'); +} + + +function toggleSelectRow(sender, row) { + + if (!row) row = sender.parentNode.parentNode; + + if (sender.checked && !row.hasClassName('Selected')) + row.addClassName('Selected'); + else + row.removeClassName('Selected'); +} + +function checkboxToggleElement(elem, id) { + if (elem.checked) { + Effect.Appear(id, {duration : 0.5}); + } else { + Effect.Fade(id, {duration : 0.5}); + } +} + +function dropboxSelect(e, v) { + for (var i = 0; i < e.length; i++) { + if (e[i].value == v) { + e.selectedIndex = i; + break; + } + } +} + +function getURLParam(param){ + return String(window.location.href).parseQuery()[param]; +} + +function leading_zero(p) { + var s = String(p); + if (s.length == 1) s = "0" + s; + return s; +} + +function make_timestamp() { + var d = new Date(); + + return leading_zero(d.getHours()) + ":" + leading_zero(d.getMinutes()) + + ":" + leading_zero(d.getSeconds()); +} + + +function closeInfoBox(cleanup) { + try { + dialog = dijit.byId("infoBox"); + + if (dialog) dialog.hide(); + + } catch (e) { + //exception_error("closeInfoBox", e); + } + return false; +} + + +function displayDlg(id, param, callback) { + + notify_progress("Loading, please wait...", true); + + var query = "?op=dlg&id=" + + param_escape(id) + "¶m=" + param_escape(param); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function (transport) { + infobox_callback2(transport); + if (callback) callback(transport); + } }); + + return false; +} + +function infobox_callback2(transport) { + try { + var dialog = false; + + if (dijit.byId("infoBox")) { + dialog = dijit.byId("infoBox"); + } + + //console.log("infobox_callback2"); + notify(''); + + var title = transport.responseXML.getElementsByTagName("title")[0]; + if (title) + title = title.firstChild.nodeValue; + + var content = transport.responseXML.getElementsByTagName("content")[0]; + + content = content.firstChild.nodeValue; + + if (!dialog) { + dialog = new dijit.Dialog({ + title: title, + id: 'infoBox', + style: "width: 600px", + onCancel: function() { + return true; + }, + onExecute: function() { + return true; + }, + onClose: function() { + return true; + }, + content: content}); + } else { + dialog.attr('title', title); + dialog.attr('content', content); + } + + dialog.show(); + + notify(""); + } catch (e) { + exception_error("infobox_callback2", e); + } +} + +function filterCR(e, f) +{ + var key; + + if(window.event) + key = window.event.keyCode; //IE + else + key = e.which; //firefox + + if (key == 13) { + if (typeof f != 'undefined') { + f(); + return false; + } else { + return false; + } + } else { + return true; + } +} + +function getInitParam(key) { + return init_params[key]; +} + +function setInitParam(key, value) { + init_params[key] = value; +} + +function fatalError(code, msg, ext_info) { + try { + + if (code == 6) { + window.location.href = "index.php"; + } else if (code == 5) { + window.location.href = "db-updater.php"; + } else { + + if (msg == "") msg = "Unknown error"; + + if (ext_info) { + if (ext_info.responseText) { + ext_info = ext_info.responseText; + } + } + + if (ERRORS && ERRORS[code] && !msg) { + msg = ERRORS[code]; + } + + var content = "
    Error code: " + code + "
    " + + "

    " + msg + "

    "; + + if (ext_info) { + content = content + "
    Additional information:
    " + + ""; + } + + var dialog = new dijit.Dialog({ + title: "Fatal error", + style: "width: 600px", + content: content}); + + dialog.show(); + + } + + return false; + + } catch (e) { + exception_error("fatalError", e); + } +} + +function filterDlgCheckType(sender) { + + try { + + var ftype = sender.value; + + // if selected filter type is 5 (Date) enable the modifier dropbox + if (ftype == 5) { + Element.show("filterDlg_dateModBox"); + Element.show("filterDlg_dateChkBox"); + } else { + Element.hide("filterDlg_dateModBox"); + Element.hide("filterDlg_dateChkBox"); + + } + + } catch (e) { + exception_error("filterDlgCheckType", e); + } + +} + +function filterDlgCheckAction(sender) { + + try { + + var action = sender.value; + + var action_param = $("filterDlg_paramBox"); + + if (!action_param) { + console.log("filterDlgCheckAction: can't find action param box!"); + return; + } + + // if selected action supports parameters, enable params field + if (action == 4 || action == 6 || action == 7) { + new Effect.Appear(action_param, {duration : 0.5}); + if (action != 7) { + Element.show(dijit.byId("filterDlg_actionParam").domNode); + Element.hide(dijit.byId("filterDlg_actionParamLabel").domNode); + } else { + Element.show(dijit.byId("filterDlg_actionParamLabel").domNode); + Element.hide(dijit.byId("filterDlg_actionParam").domNode); + } + } else { + Element.hide(action_param); + } + + } catch (e) { + exception_error("filterDlgCheckAction", e); + } + +} + +function filterDlgCheckDate() { + try { + var dialog = dijit.byId("filterEditDlg"); + + var reg_exp = dialog.attr('value').reg_exp; + + var query = "?op=rpc&subop=checkDate&date=" + reg_exp; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + var reply = JSON.parse(transport.responseText); + + if (reply['result'] == true) { + alert(__("Date syntax appears to be correct:") + " " + reply['date']); + return; + } else { + alert(__("Date syntax is incorrect.")); + } + + } }); + + + } catch (e) { + exception_error("filterDlgCheckDate", e); + } +} + +function explainError(code) { + return displayDlg("explainError", code); +} + +function displayHelpInfobox(topic_id) { + + var url = "backend.php?op=help&tid=" + param_escape(topic_id); + + window.open(url, "ttrss_help", + "status=0,toolbar=0,location=0,width=450,height=500,scrollbars=1,menubar=0"); + +} + +function loading_set_progress(p) { + try { + loading_progress += p; + + if (dijit.byId("loading_bar")) + dijit.byId("loading_bar").update({progress: loading_progress}); + + if (loading_progress >= 90) + remove_splash(); + + } catch (e) { + exception_error("loading_set_progress", e); + } +} + +function remove_splash() { + + if (Element.visible("overlay")) { + console.log("about to remove splash, OMG!"); + Element.hide("overlay"); + console.log("removed splash!"); + } +} + +function transport_error_check(transport) { + try { + if (transport.responseXML) { + var error = transport.responseXML.getElementsByTagName("error")[0]; + + if (error) { + var code = error.getAttribute("error-code"); + var msg = error.getAttribute("error-msg"); + if (code != 0) { + fatalError(code, msg); + return false; + } + } + } + } catch (e) { + exception_error("check_for_error_xml", e); + } + return true; +} + +function strip_tags(s) { + return s.replace(/<\/?[^>]+(>|$)/g, ""); +} + +function truncate_string(s, length) { + if (!length) length = 30; + var tmp = s.substring(0, length); + if (s.length > length) tmp += "…"; + return tmp; +} + +function hotkey_prefix_timeout() { + try { + + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + if (hotkey_prefix_pressed && ts - hotkey_prefix_pressed >= 5) { + console.log("hotkey_prefix seems to be stuck, aborting"); + hotkey_prefix_pressed = false; + hotkey_prefix = false; + Element.hide('cmdline'); + } + + setTimeout("hotkey_prefix_timeout()", 1000); + + } catch (e) { + exception_error("hotkey_prefix_timeout", e); + } +} + +function hideAuxDlg() { + try { + Element.hide('auxDlg'); + } catch (e) { + exception_error("hideAuxDlg", e); + } +} + + +function uploadIconHandler(rc) { + try { + switch (rc) { + case 0: + notify_info("Upload complete."); + if (inPreferences()) { + updateFeedList(); + } else { + setTimeout('updateFeedList(false, false)', 50); + } + break; + case 1: + notify_error("Upload failed: icon is too big."); + break; + case 2: + notify_error("Upload failed."); + break; + } + + } catch (e) { + exception_error("uploadIconHandler", e); + } +} + +function removeFeedIcon(id) { + + try { + + if (confirm(__("Remove stored feed icon?"))) { + var query = "backend.php?op=pref-feeds&subop=removeicon&feed_id=" + param_escape(id); + + console.log(query); + + notify_progress("Removing feed icon...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Feed icon removed."); + if (inPreferences()) { + updateFeedList(); + } else { + setTimeout('updateFeedList(false, false)', 50); + } + } }); + } + + return false; + } catch (e) { + exception_error("uploadFeedIcon", e); + } +} + +function uploadFeedIcon() { + + try { + + var file = $("icon_file"); + + if (file.value.length == 0) { + alert(__("Please select an image file to upload.")); + } else { + if (confirm(__("Upload new icon for this feed?"))) { + notify_progress("Uploading, please wait...", true); + return true; + } + } + + return false; + + } catch (e) { + exception_error("uploadFeedIcon", e); + } +} + +function addLabel(select, callback) { + + try { + + var caption = prompt(__("Please enter label caption:"), ""); + + if (caption != undefined) { + + if (caption == "") { + alert(__("Can't create label: missing caption.")); + return false; + } + + var query = "?op=pref-labels&subop=add&caption=" + + param_escape(caption); + + if (select) + query += "&output=select"; + + notify_progress("Loading, please wait...", true); + + if (inPreferences() && !select) active_tab = "labelConfig"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + if (callback) { + callback(transport); + } else if (inPreferences()) { + updateLabelList(); + } else { + updateFeedList(); + } + } }); + + } + + } catch (e) { + exception_error("addLabel", e); + } +} + +function quickAddFeed() { + try { + var query = "backend.php?op=dlg&id=quickAddFeed"; + + if (dijit.byId("feedAddDlg")) + dijit.byId("feedAddDlg").destroyRecursive(); + + var dialog = new dijit.Dialog({ + id: "feedAddDlg", + title: __("Subscribe to Feed"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + console.log(dojo.objectToQuery(this.attr('value'))); + + var feed_url = this.attr('value').feed; + + notify_progress(__("Subscribing to feed..."), true); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + try { + + var reply = JSON.parse(transport.responseText); + + var rc = parseInt(reply['result']); + + notify(''); + + console.log("GOT RC: " + rc); + + switch (rc) { + case 1: + dialog.hide(); + notify_info(__("Subscribed to %s").replace("%s", feed_url)); + + updateFeedList(); + break; + case 2: + alert(__("Specified URL seems to be invalid.")); + break; + case 3: + alert(__("Specified URL doesn't seem to contain any feeds.")); + break; + case 4: + notify_progress("Searching for feed urls...", true); + + new Ajax.Request("backend.php", { + parameters: 'op=rpc&subop=extractfeedurls&url=' + param_escape(feed_url), + onComplete: function(transport, dialog, feed_url) { + + notify(''); + + var reply = JSON.parse(transport.responseText); + + var feeds = reply['urls']; + + console.log(transport.responseText); + + var select = dijit.byId("feedDlg_feedContainerSelect"); + + while (select.getOptions().length > 0) + select.removeOption(0); + + var count = 0; + for (var feedUrl in feeds) { + select.addOption({value: feedUrl, label: feeds[feedUrl]}); + count++; + } + +// if (count > 5) count = 5; +// select.size = count; + + Effect.Appear('feedDlg_feedsContainer', {duration : 0.5}); + } + }); + break; + case 5: + alert(__("Couldn't download the specified URL.")); + break; + case 0: + alert(__("You are already subscribed to this feed.")); + break; + } + + } catch (e) { + exception_error("subscribeToFeed", e, transport); + } + + } }); + + } + }, + href: query}); + + dialog.show(); + } catch (e) { + exception_error("quickAddFeed", e); + } +} + +function quickAddFilter() { + try { + var query = "backend.php?op=dlg&id=quickAddFilter"; + + if (dijit.byId("filterEditDlg")) + dijit.byId("filterEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "filterEditDlg", + title: __("Create Filter"), + style: "width: 600px", + test: function() { + if (this.validate()) { + + if (dijit.byId("filterTestDlg")) + dijit.byId("filterTestDlg").destroyRecursive(); + + tdialog = new dijit.Dialog({ + id: "filterTestDlg", + title: __("Filter Test Results"), + style: "width: 600px", + href: "backend.php?savemode=test&" + + dojo.objectToQuery(dialog.attr('value')), + }); + + tdialog.show(); + + } + }, + execute: function() { + if (this.validate()) { + + var query = "?op=rpc&subop=verifyRegexp®_exp=" + + param_escape(dialog.attr('value').reg_exp); + + notify_progress("Verifying regular expression..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + + if (reply) { + notify(''); + + if (!reply['status']) { + alert("Match regular expression seems to be invalid."); + return; + } else { + notify_progress("Saving data...", true); + + console.log(dojo.objectToQuery(dialog.attr('value'))); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(dialog.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify_info(transport.responseText); + if (inPreferences()) { + updateFilterList(); + } + }}); + } + } + }}); + } + }, + href: query}); + + dialog.show(); + } catch (e) { + exception_error("quickAddFilter", e); + } +} + +function resetPubSub(feed_id, title) { + + var msg = __("Reset subscription? Tiny Tiny RSS will try to subscribe to the notification hub again on next feed update.").replace("%s", title); + + if (title == undefined || confirm(msg)) { + notify_progress("Loading, please wait..."); + + var query = "?op=pref-feeds&quiet=1&subop=resetPubSub&ids=" + feed_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + dijit.byId("pubsubReset_Btn").attr('disabled', true); + notify_info("Subscription reset."); + } }); + } + + return false; +} + + +function unsubscribeFeed(feed_id, title) { + + var msg = __("Unsubscribe from %s?").replace("%s", title); + + if (title == undefined || confirm(msg)) { + notify_progress("Removing feed..."); + + var query = "?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + if (dijit.byId("feedEditDlg")) dijit.byId("feedEditDlg").hide(); + + if (inPreferences()) { + updateFeedList(); + } else { + if (feed_id == getActiveFeedId()) + setTimeout("viewfeed(-5)", 100); + } + + } }); + } + + return false; +} + + +function backend_sanity_check_callback(transport) { + + try { + + if (sanity_check_done) { + fatalError(11, "Sanity check request received twice. This can indicate "+ + "presence of Firebug or some other disrupting extension. "+ + "Please disable it and try again."); + return; + } + + var reply = JSON.parse(transport.responseText); + + if (!reply) { + fatalError(3, "Sanity check: invalid RPC reply", transport.responseText); + return; + } + + var error_code = reply['error']['code']; + + if (error_code && error_code != 0) { + return fatalError(error_code, reply['error']['message']); + } + + console.log("sanity check ok"); + + var params = reply['init-params']; + + if (params) { + console.log('reading init-params...'); + + if (params) { + for (k in params) { + var v = params[k]; + console.log("IP: " + k + " => " + v); + } + } + + init_params = params; + } + + sanity_check_done = true; + + init_second_stage(); + + } catch (e) { + exception_error("backend_sanity_check_callback", e, transport); + } +} + +/*function has_local_storage() { + try { + return 'sessionStorage' in window && window['sessionStorage'] != null; + } catch (e) { + return false; + } +} */ + +function catSelectOnChange(elem) { + try { +/* var value = elem[elem.selectedIndex].value; + var def = elem.getAttribute('default'); + + if (value == "ADD_CAT") { + + if (def) + dropboxSelect(elem, def); + else + elem.selectedIndex = 0; + + quickAddCat(elem); + } */ + + } catch (e) { + exception_error("catSelectOnChange", e); + } +} + +function quickAddCat(elem) { + try { + var cat = prompt(__("Please enter category title:")); + + if (cat) { + + var query = "?op=rpc&subop=quickAddCat&cat=" + param_escape(cat); + + notify_progress("Loading, please wait...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function (transport) { + var response = transport.responseXML; + var select = response.getElementsByTagName("select")[0]; + var options = select.getElementsByTagName("option"); + + dropbox_replace_options(elem, options); + + notify(''); + + } }); + + } + + } catch (e) { + exception_error("quickAddCat", e); + } +} + +function genUrlChangeKey(feed, is_cat) { + + try { + var ok = confirm(__("Generate new syndication address for this feed?")); + + if (ok) { + + notify_progress("Trying to change address...", true); + + var query = "?op=rpc&subop=regenFeedKey&id=" + param_escape(feed) + + "&is_cat=" + param_escape(is_cat); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + var new_link = reply.link; + + var e = $('gen_feed_url'); + + if (new_link) { + + e.innerHTML = e.innerHTML.replace(/\&key=.*$/, + "&key=" + new_link); + + e.href = e.href.replace(/\&key=.*$/, + "&key=" + new_link); + + new Effect.Highlight(e); + + notify(''); + + } else { + notify_error("Could not change feed URL."); + } + } }); + } + } catch (e) { + exception_error("genUrlChangeKey", e); + } + return false; +} + +function labelSelectOnChange(elem) { + try { +/* var value = elem[elem.selectedIndex].value; + var def = elem.getAttribute('default'); + + if (value == "ADD_LABEL") { + + if (def) + dropboxSelect(elem, def); + else + elem.selectedIndex = 0; + + addLabel(elem, function(transport) { + + try { + + var response = transport.responseXML; + var select = response.getElementsByTagName("select")[0]; + var options = select.getElementsByTagName("option"); + + dropbox_replace_options(elem, options); + + notify(''); + } catch (e) { + exception_error("addLabel", e); + } + }); + } */ + + } catch (e) { + exception_error("labelSelectOnChange", e); + } +} + +function dropbox_replace_options(elem, options) { + + try { + while (elem.hasChildNodes()) + elem.removeChild(elem.firstChild); + + var sel_idx = -1; + + for (var i = 0; i < options.length; i++) { + var text = options[i].firstChild.nodeValue; + var value = options[i].getAttribute("value"); + + if (value == undefined) value = text; + + var issel = options[i].getAttribute("selected") == "1"; + + var option = new Option(text, value, issel); + + if (options[i].getAttribute("disabled")) + option.setAttribute("disabled", true); + + elem.insert(option); + + if (issel) sel_idx = i; + } + + // Chrome doesn't seem to just select stuff when you pass new Option(x, y, true) + if (sel_idx >= 0) elem.selectedIndex = sel_idx; + + } catch (e) { + exception_error("dropbox_replace_options", e); + } +} + +// mode = all, none, invert +function selectTableRows(id, mode) { + try { + var rows = $(id).rows; + + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; + var cb = false; + + if (row.id && row.className) { + var bare_id = row.id.replace(/^[A-Z]*?-/, ""); + var inputs = rows[i].getElementsByTagName("input"); + + for (var j = 0; j < inputs.length; j++) { + var input = inputs[j]; + + if (input.getAttribute("type") == "checkbox" && + input.id.match(bare_id)) { + + cb = input; + break; + } + } + + if (cb) { + var issel = row.hasClassName("Selected"); + + if (mode == "all" && !issel) { + row.addClassName("Selected"); + cb.checked = true; + } else if (mode == "none" && issel) { + row.removeClassName("Selected"); + cb.checked = false; + } else if (mode == "invert") { + + if (issel) { + row.removeClassName("Selected"); + cb.checked = false; + } else { + row.addClassName("Selected"); + cb.checked = true; + } + } + } + } + } + + } catch (e) { + exception_error("selectTableRows", e); + + } +} + +function getSelectedTableRowIds(id) { + var rows = []; + + try { + var elem_rows = $(id).rows; + + for (var i = 0; i < elem_rows.length; i++) { + if (elem_rows[i].hasClassName("Selected")) { + var bare_id = elem_rows[i].id.replace(/^[A-Z]*?-/, ""); + rows.push(bare_id); + } + } + + } catch (e) { + exception_error("getSelectedTableRowIds", e); + } + + return rows; +} + +function editFeed(feed, event) { + try { + if (feed <= 0) + return alert(__("You can't edit this kind of feed.")); + + var query = "backend.php?op=pref-feeds&subop=editfeed&id=" + + param_escape(feed); + + console.log(query); + + if (dijit.byId("feedEditDlg")) + dijit.byId("feedEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "feedEditDlg", + title: __("Edit Feed"), + style: "width: 600px", + execute: function() { + if (this.validate()) { +// console.log(dojo.objectToQuery(this.attr('value'))); + + notify_progress("Saving data...", true); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(dialog.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify(''); + updateFeedList(); + }}); + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editFeed", e); + } +} + +function feedBrowser() { + try { + var query = "backend.php?op=dlg&id=feedBrowser"; + + if (dijit.byId("feedAddDlg")) + dijit.byId("feedAddDlg").hide(); + + if (dijit.byId("feedBrowserDlg")) + dijit.byId("feedBrowserDlg").destroyRecursive(); + + var dialog = new dijit.Dialog({ + id: "feedBrowserDlg", + title: __("More Feeds"), + style: "width: 600px", + getSelectedFeedIds: function() { + var list = $$("#browseFeedList li[id*=FBROW]"); + var selected = new Array(); + + list.each(function(child) { + var id = child.id.replace("FBROW-", ""); + + if (child.hasClassName('Selected')) { + selected.push(id); + } + }); + + return selected; + }, + getSelectedFeeds: function() { + var list = $$("#browseFeedList li.Selected"); + var selected = new Array(); + + list.each(function(child) { + var title = child.getElementsBySelector("span.fb_feedTitle")[0].innerHTML; + var url = child.getElementsBySelector("a.fb_feedUrl")[0].href; + + selected.push([title,url]); + + }); + + return selected; + }, + + subscribe: function() { + var mode = this.attr('value').mode; + var selected = []; + + if (mode == "1") + selected = this.getSelectedFeeds(); + else + selected = this.getSelectedFeedIds(); + + if (selected.length > 0) { + dijit.byId("feedBrowserDlg").hide(); + + notify_progress("Loading, please wait...", true); + + // we use dojo.toJson instead of JSON.stringify because + // it somehow escapes everything TWICE, at least in Chrome 9 + + var query = "?op=rpc&subop=massSubscribe&payload="+ + param_escape(dojo.toJson(selected)) + "&mode=" + param_escape(mode); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + updateFeedList(); + } }); + + } else { + alert(__("No feeds are selected.")); + } + + }, + update: function() { + var query = dojo.objectToQuery(dialog.attr('value')); + + Element.show('feed_browser_spinner'); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + + Element.hide('feed_browser_spinner'); + + var c = $("browseFeedList"); + + var reply = JSON.parse(transport.responseText); + + var r = reply['content']; + var mode = reply['mode']; + + if (c && r) { + c.innerHTML = r; + } + + dojo.parser.parse("browseFeedList"); + + if (mode == 2) { + Element.show(dijit.byId('feed_archive_remove').domNode); + } else { + Element.hide(dijit.byId('feed_archive_remove').domNode); + } + + } }); + }, + removeFromArchive: function() { + var selected = this.getSelectedFeeds(); + + if (selected.length > 0) { + + var pr = __("Remove selected feeds from the archive? Feeds with stored articles will not be removed."); + + if (confirm(pr)) { + Element.show('feed_browser_spinner'); + + var query = "?op=rpc&subop=remarchived&ids=" + + param_escape(selected.toString());; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + dialog.update(); + } }); + } + } + }, + execute: function() { + if (this.validate()) { + this.subscribe(); + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editFeed", e); + } +} + +function showFeedsWithErrors() { + try { + var query = "backend.php?op=dlg&id=feedsWithErrors"; + + if (dijit.byId("errorFeedsDlg")) + dijit.byId("errorFeedsDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "errorFeedsDlg", + title: __("Feeds with update errors"), + style: "width: 600px", + getSelectedFeeds: function() { + return getSelectedTableRowIds("prefErrorFeedList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedFeeds(); + + console.log(sel_rows); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected feeds?")); + + if (ok) { + notify_progress("Removing selected feeds...", true); + + var query = "?op=pref-feeds&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + updateFeedList(); + } }); + } + + } else { + alert(__("No feeds are selected.")); + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("showFeedsWithErrors", e); + } + +} + +/* new support functions for SelectByTag */ + +function get_all_tags(selObj){ + try { + if( !selObj ) return ""; + + var result = ""; + var len = selObj.options.length; + + for (var i=0; i < len; i++){ + if (selObj.options[i].selected) { + result += selObj[i].value + "%2C"; // is really a comma + } + } + + if (result.length > 0){ + result = result.substr(0, result.length-3); // remove trailing %2C + } + + return(result); + + } catch (e) { + exception_error("get_all_tags", e); + } +} + +function get_radio_checked(radioObj) { + try { + if (!radioObj) return ""; + + var len = radioObj.length; + + if (len == undefined){ + if(radioObj.checked){ + return(radioObj.value); + } else { + return(""); + } + } + + for( var i=0; i < len; i++ ){ + if( radioObj[i].checked ){ + return( radioObj[i].value); + } + } + + } catch (e) { + exception_error("get_radio_checked", e); + } + return(""); +} diff --git a/js/prefs.js b/js/prefs.js new file mode 100644 index 000000000..e40d6d723 --- /dev/null +++ b/js/prefs.js @@ -0,0 +1,1967 @@ +var init_params = new Array(); + +var hotkey_prefix = false; +var hotkey_prefix_pressed = false; + +var seq = ""; + +function instancelist_callback2(transport) { + try { + dijit.byId('instanceConfigTab').attr('content', transport.responseText); + selectTab("instanceConfig", true); + notify(""); + } catch (e) { + exception_error("instancelist_callback2", e); + } +} + +function feedlist_callback2(transport) { + try { + dijit.byId('feedConfigTab').attr('content', transport.responseText); + selectTab("feedConfig", true); + notify(""); + } catch (e) { + exception_error("feedlist_callback2", e); + } +} + +function filterlist_callback2(transport) { + dijit.byId('filterConfigTab').attr('content', transport.responseText); + notify(""); +} + +function labellist_callback2(transport) { + try { + dijit.byId('labelConfigTab').attr('content', transport.responseText); + notify(""); + } catch (e) { + exception_error("labellist_callback2", e); + } +} + +function userlist_callback2(transport) { + try { + dijit.byId('userConfigTab').attr('content', transport.responseText); + + notify(""); + } catch (e) { + exception_error("userlist_callback2", e); + } +} + +function prefslist_callback2(transport) { + try { + dijit.byId('genConfigTab').attr('content', transport.responseText); + + notify(""); + } catch (e) { + exception_error("prefslist_callback2", e); + } +} + +function notify_callback2(transport) { + notify_info(transport.responseText); +} + +function updateFeedList(sort_key) { + + var user_search = $("feed_search"); + var search = ""; + if (user_search) { search = user_search.value; } + + new Ajax.Request("backend.php", { + parameters: "?op=pref-feeds&search=" + param_escape(search), + onComplete: function(transport) { + feedlist_callback2(transport); + } }); +} + +function updateInstanceList(sort_key) { + new Ajax.Request("backend.php", { + parameters: "?op=pref-instances&sort=" + param_escape(sort_key), + onComplete: function(transport) { + instancelist_callback2(transport); + } }); +} + +function updateUsersList(sort_key) { + + try { + + var user_search = $("user_search"); + var search = ""; + if (user_search) { search = user_search.value; } + + var query = "?op=pref-users&sort=" + + param_escape(sort_key) + + "&search=" + param_escape(search); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } catch (e) { + exception_error("updateUsersList", e); + } +} + +function addUser() { + + try { + + var login = prompt(__("Please enter login:"), ""); + + if (login == null) { + return false; + } + + if (login == "") { + alert(__("Can't create user: no login specified.")); + return false; + } + + notify_progress("Adding user..."); + + var query = "?op=pref-users&subop=add&login=" + + param_escape(login); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } catch (e) { + exception_error("addUser", e); + } +} + +function editUser(id, event) { + + try { + if (!event || !event.ctrlKey) { + + notify_progress("Loading, please wait..."); + + selectTableRows('prefUserList', 'none'); + selectTableRowById('UMRR-'+id, 'UMCHK-'+id, true); + + var query = "?op=pref-users&subop=edit&id=" + + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + infobox_callback2(transport); + document.forms['user_edit_form'].login.focus(); + } }); + + } else if (event.ctrlKey) { + var cb = $('UMCHK-' + id); + cb.checked = !cb.checked; + toggleSelectRow(cb); + } + + } catch (e) { + exception_error("editUser", e); + } + +} + +function editFilter(id) { + try { + + var query = "backend.php?op=pref-filters&subop=edit&id=" + param_escape(id); + + if (dijit.byId("filterEditDlg")) + dijit.byId("filterEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "filterEditDlg", + title: __("Edit Filter"), + style: "width: 600px", + removeFilter: function() { + var title = this.attr('value').reg_exp; + var msg = __("Remove filter %s?").replace("%s", title); + + if (confirm(msg)) { + this.hide(); + + notify_progress("Removing filter..."); + + var id = this.attr('value').id; + + var query = "?op=pref-filters&subop=remove&ids="+ + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFilterList(); + } }); + } + }, + test: function() { + if (this.validate()) { + + if (dijit.byId("filterTestDlg")) + dijit.byId("filterTestDlg").destroyRecursive(); + + tdialog = new dijit.Dialog({ + id: "filterTestDlg", + title: __("Filter Test Results"), + style: "width: 600px", + href: "backend.php?savemode=test&" + + dojo.objectToQuery(dialog.attr('value')), + }); + + tdialog.show(); + + } + }, + execute: function() { + if (this.validate()) { + + var query = "?op=rpc&subop=verifyRegexp®_exp=" + + param_escape(dialog.attr('value').reg_exp); + + notify_progress("Verifying regular expression..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + + if (reply) { + notify(''); + + if (!reply['status']) { + alert("Match regular expression seems to be invalid."); + return; + } else { + notify_progress("Saving data...", true); + + console.log(dojo.objectToQuery(dialog.attr('value'))); + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(dialog.attr('value')), + onComplete: function(transport) { + dialog.hide(); + updateFilterList(); + }}); + } + } + }}); + } + }, + href: query}); + + dialog.show(); + + + } catch (e) { + exception_error("editFilter", e); + } +} + +function getSelectedLabels() { + var tree = dijit.byId("labelTree"); + var items = tree.model.getCheckedItems(); + var rv = []; + + items.each(function(item) { + rv.push(tree.model.store.getValue(item, 'bare_id')); + }); + + return rv; +} + +function getSelectedUsers() { + return getSelectedTableRowIds("prefUserList"); +} + +function getSelectedFeeds() { + var tree = dijit.byId("feedTree"); + var items = tree.model.getCheckedItems(); + var rv = []; + + items.each(function(item) { + if (item.id[0].match("FEED:")) + rv.push(tree.model.store.getValue(item, 'bare_id')); + }); + + return rv; +} + +function getSelectedFilters() { + var tree = dijit.byId("filterTree"); + var items = tree.model.getCheckedItems(); + var rv = []; + + items.each(function(item) { + rv.push(tree.model.store.getValue(item, 'bare_id')); + }); + + return rv; + +} + +/* function getSelectedFeedCats() { + return getSelectedTableRowIds("prefFeedCatList"); +} */ + +function removeSelectedLabels() { + + var sel_rows = getSelectedLabels(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected labels?")); + + if (ok) { + notify_progress("Removing selected labels..."); + + var query = "?op=pref-labels&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + labellist_callback2(transport); + } }); + + } + } else { + alert(__("No labels are selected.")); + } + + return false; +} + +function removeSelectedUsers() { + + try { + + var sel_rows = getSelectedUsers(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed.")); + + if (ok) { + notify_progress("Removing selected users..."); + + var query = "?op=pref-users&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } + + } else { + alert(__("No users are selected.")); + } + + } catch (e) { + exception_error("removeSelectedUsers", e); + } + + return false; +} + +function removeSelectedFilters() { + + try { + + var sel_rows = getSelectedFilters(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected filters?")); + + if (ok) { + notify_progress("Removing selected filters..."); + + var query = "?op=pref-filters&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFilterList(); + } }); + } + } else { + alert(__("No filters are selected.")); + } + + } catch (e) { + exception_error("removeSelectedFilters", e); + } + + return false; +} + + +function removeSelectedFeeds() { + + try { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Unsubscribe from selected feeds?")); + + if (ok) { + + notify_progress("Unsubscribing from selected feeds...", true); + + var query = "?op=pref-feeds&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFeedList(); + } }); + } + + } else { + alert(__("No feeds are selected.")); + } + + } catch (e) { + exception_error("removeSelectedFeeds", e); + } + + return false; +} + +function clearSelectedFeeds() { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 1) { + alert(__("Please select only one feed.")); + return; + } + + if (sel_rows.length > 0) { + + var ok = confirm(__("Erase all non-starred articles in selected feed?")); + + if (ok) { + notify_progress("Clearing selected feed..."); + clearFeedArticles(sel_rows[0]); + } + + } else { + + alert(__("No feeds are selected.")); + + } + + return false; +} + +function purgeSelectedFeeds() { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 0) { + + var pr = prompt(__("How many days of articles to keep (0 - use default)?"), "0"); + + if (pr != undefined) { + notify_progress("Purging selected feed..."); + + var query = "?op=rpc&subop=purge&ids="+ + param_escape(sel_rows.toString()) + "&days=" + pr; + + console.log(query); + + new Ajax.Request("prefs.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + } }); + } + + } else { + + alert(__("No feeds are selected.")); + + } + + return false; +} + +function userEditCancel() { + closeInfoBox(); + return false; +} + +function userEditSave() { + + try { + + var login = document.forms["user_edit_form"].login.value; + + if (login.length == 0) { + alert(__("Login field cannot be blank.")); + return; + } + + notify_progress("Saving user..."); + + closeInfoBox(); + + var query = Form.serialize("user_edit_form"); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } catch (e) { + exception_error("userEditSave", e); + } + + return false; + +} + + +function editSelectedUser() { + var rows = getSelectedUsers(); + + if (rows.length == 0) { + alert(__("No users are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one user.")); + return; + } + + notify(""); + + editUser(rows[0]); +} + +function resetSelectedUserPass() { + + try { + + var rows = getSelectedUsers(); + + if (rows.length == 0) { + alert(__("No users are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one user.")); + return; + } + + var ok = confirm(__("Reset password of selected user?")); + + if (ok) { + notify_progress("Resetting password for selected user..."); + + var id = rows[0]; + + var query = "?op=pref-users&subop=resetPass&id=" + + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + userlist_callback2(transport); + } }); + + } + + } catch (e) { + exception_error("resetSelectedUserPass", e); + } +} + +function selectedUserDetails() { + + try { + + var rows = getSelectedUsers(); + + if (rows.length == 0) { + alert(__("No users are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one user.")); + return; + } + + notify_progress("Loading, please wait..."); + + var id = rows[0]; + + var query = "?op=pref-users&subop=user-details&id=" + id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + infobox_callback2(transport); + } }); + } catch (e) { + exception_error("selectedUserDetails", e); + } +} + + +function editSelectedFilter() { + var rows = getSelectedFilters(); + + if (rows.length == 0) { + alert(__("No filters are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one filter.")); + return; + } + + notify(""); + + editFilter(rows[0]); + +} + + +function editSelectedFeed() { + var rows = getSelectedFeeds(); + + if (rows.length == 0) { + alert(__("No feeds are selected.")); + return; + } + + if (rows.length > 1) { + return editSelectedFeeds(); + } + + notify(""); + + editFeed(rows[0], {}); + +} + +function editSelectedFeeds() { + + try { + var rows = getSelectedFeeds(); + + if (rows.length == 0) { + alert(__("No feeds are selected.")); + return; + } + + notify_progress("Loading, please wait..."); + + var query = "backend.php?op=pref-feeds&subop=editfeeds&ids=" + + param_escape(rows.toString()); + + console.log(query); + + if (dijit.byId("feedEditDlg")) + dijit.byId("feedEditDlg").destroyRecursive(); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + notify(""); + + var dialog = new dijit.Dialog({ + id: "feedEditDlg", + title: __("Edit Multiple Feeds"), + style: "width: 600px", + getChildByName: function (name) { + var rv = null; + this.getChildren().each( + function(child) { + if (child.name == name) { + rv = child; + return; + } + }); + return rv; + }, + toggleField: function (checkbox, elem, label) { + this.getChildByName(elem).attr('disabled', !checkbox.checked); + + if ($(label)) + if (checkbox.checked) + $(label).removeClassName('insensitive'); + else + $(label).addClassName('insensitive'); + + }, + execute: function() { + if (this.validate() && confirm(__("Save changes to selected feeds?"))) { + var query = dojo.objectToQuery(this.attr('value')); + + /* Form.serialize ignores unchecked checkboxes */ + + if (!query.match("&rtl_content=") && + this.getChildByName('rtl_content').attr('disabled') == false) { + query = query + "&rtl_content=false"; + } + + if (!query.match("&private=") && + this.getChildByName('private').attr('disabled') == false) { + query = query + "&private=false"; + } + + try { + if (!query.match("&cache_images=") && + this.getChildByName('cache_images').attr('disabled') == false) { + query = query + "&cache_images=false"; + } + } catch (e) { } + + if (!query.match("&include_in_digest=") && + this.getChildByName('include_in_digest').attr('disabled') == false) { + query = query + "&include_in_digest=false"; + } + + if (!query.match("&always_display_enclosures=") && + this.getChildByName('always_display_enclosures').attr('disabled') == false) { + query = query + "&always_display_enclosures=false"; + } + + if (!query.match("&mark_unread_on_update=") && + this.getChildByName('mark_unread_on_update').attr('disabled') == false) { + query = query + "&mark_unread_on_update=false"; + } + + if (!query.match("&update_on_checksum_change=") && + this.getChildByName('update_on_checksum_change').attr('disabled') == false) { + query = query + "&update_on_checksum_change=false"; + } + + console.log(query); + + notify_progress("Saving data...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + dialog.hide(); + updateFeedList(); + }}); + } + }, + content: transport.responseText}); + + dialog.show(); + + } }); + + } catch (e) { + exception_error("editSelectedFeeds", e); + } +} + +function piggie(enable) { + if (enable) { + console.log("I LOVEDED IT!"); + var piggie = $("piggie"); + + Element.show(piggie); + Position.Center(piggie); + Effect.Puff(piggie); + + } +} + +function opmlImportComplete(iframe) { + try { + if (!iframe.contentDocument.body.innerHTML) return false; + + notify(''); + + if (dijit.byId('opmlImportDlg')) + dijit.byId('opmlImportDlg').destroyRecursive(); + + var content = iframe.contentDocument.body.innerHTML; + + dialog = new dijit.Dialog({ + id: "opmlImportDlg", + title: __("OPML Import"), + style: "width: 600px", + onCancel: function() { + updateFeedList(); + }, + content: content}); + + dialog.show(); + + } catch (e) { + exception_error("opmlImportComplete", e); + } +} + +function opmlImport() { + + var opml_file = $("opml_file"); + + if (opml_file.value.length == 0) { + alert(__("Please choose an OPML file first.")); + return false; + } else { + notify_progress("Importing, please wait...", true); + return true; + } +} + +function updateFilterList() { + new Ajax.Request("backend.php", { + parameters: "?op=pref-filters", + onComplete: function(transport) { + filterlist_callback2(transport); + } }); +} + +function updateLabelList() { + new Ajax.Request("backend.php", { + parameters: "?op=pref-labels", + onComplete: function(transport) { + labellist_callback2(transport); + } }); +} + +function updatePrefsList() { + new Ajax.Request("backend.php", { + parameters: "?op=pref-prefs", + onComplete: function(transport) { + prefslist_callback2(transport); + } }); +} + +function selectTab(id, noupdate, subop) { + try { + if (!noupdate) { + notify_progress("Loading, please wait..."); + + if (id == "feedConfig") { + updateFeedList(); + } else if (id == "filterConfig") { + updateFilterList(); + } else if (id == "labelConfig") { + updateLabelList(); + } else if (id == "genConfig") { + updatePrefsList(); + } else if (id == "userConfig") { + updateUsersList(); + } + + var tab = dijit.byId(id + "Tab"); + dijit.byId("pref-tabs").selectChild(tab); + + } + + } catch (e) { + exception_error("selectTab", e); + } +} + +function init_second_stage() { + try { + + document.onkeydown = pref_hotkey_handler; + loading_set_progress(50); + notify(""); + + dojo.addOnLoad(function() { + var tab = getURLParam('tab'); + + if (tab) { + tab = dijit.byId(tab + "Tab"); + if (tab) dijit.byId("pref-tabs").selectChild(tab); + } + + var subop = getURLParam('subop'); + + if (subop == 'editFeed') { + var param = getURLParam('subopparam'); + + window.setTimeout('editFeed(' + param + ')', 100); + } + }); + + setTimeout("hotkey_prefix_timeout()", 5*1000); + + } catch (e) { + exception_error("init_second_stage", e); + } +} + +function init() { + + try { + dojo.registerModulePath("lib", ".."); + dojo.registerModulePath("fox", "../../js/"); + + dojo.require("lib.CheckBoxTree"); + dojo.require("fox.PrefFeedTree"); + dojo.require("fox.PrefFilterTree"); + dojo.require("fox.PrefLabelTree"); + + dojo.parser.parse(); + + dojo.addOnLoad(function() { + loading_set_progress(50); + + new Ajax.Request("backend.php", { + parameters: {op: "rpc", subop: "sanityCheck"}, + onComplete: function(transport) { + backend_sanity_check_callback(transport); + } }); + }); + + } catch (e) { + exception_error("init", e); + } +} + +function validatePrefsReset() { + try { + var ok = confirm(__("Reset to defaults?")); + + if (ok) { + + query = "?op=pref-prefs&subop=reset-config"; + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var msg = transport.responseText; + if (msg.match("PREFS_THEME_CHANGED")) { + window.location.reload(); + } else { + notify_info(msg); + selectTab(); + } + } }); + + } + + } catch (e) { + exception_error("validatePrefsReset", e); + } + + return false; + +} + + +function pref_hotkey_handler(e) { + try { + if (e.target.nodeName == "INPUT") return; + + var keycode = false; + var shift_key = false; + + var cmdline = $('cmdline'); + + try { + shift_key = e.shiftKey; + } catch (e) { + + } + + if (window.event) { + keycode = window.event.keyCode; + } else if (e) { + keycode = e.which; + } + + var keychar = String.fromCharCode(keycode); + + if (keycode == 27) { // escape + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + hotkey_prefix = false; + closeInfoBox(); + } + + if (keycode == 16) return; // ignore lone shift + if (keycode == 17) return; // ignore lone ctrl + + if ((keycode == 67 || keycode == 71) && !hotkey_prefix) { + hotkey_prefix = keycode; + + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + hotkey_prefix_pressed = ts; + + cmdline.innerHTML = keychar; + Element.show(cmdline); + + console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar); + return; + } + + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + + if (keycode == 13 || keycode == 27) { + seq = ""; + } else { + seq = seq + "" + keycode; + } + + /* Global hotkeys */ + + Element.hide(cmdline); + + if (!hotkey_prefix) { + + if ((keycode == 191 || keychar == '?') && shift_key) { // ? + if (!Element.visible("hotkey_help_overlay")) { + //Element.show("hotkey_help_overlay"); + Effect.Appear("hotkey_help_overlay", {duration : 0.3, to: 0.9}); + } else { + Element.hide("hotkey_help_overlay"); + } + return false; + } + + if (keycode == 191 || keychar == '/') { // / + var search_boxes = new Array("label_search", + "feed_search", "filter_search", "user_search", "feed_browser_search"); + + for (var i = 0; i < search_boxes.length; i++) { + var elem = $(search_boxes[i]); + if (elem) { + $(search_boxes[i]).focus(); + return false; + } + } + } + } + + /* Prefix c */ + + if (hotkey_prefix == 67) { // c + hotkey_prefix = false; + + if (keycode == 70) { // f + quickAddFilter(); + return false; + } + + if (keycode == 83) { // s + quickAddFeed(); + return false; + } + + if (keycode == 85) { // u + // no-op + } + + if (keycode == 67) { // c + editFeedCats(); + return false; + } + + if (keycode == 84 && shift_key) { // T + feedBrowser(); + return false; + } + + } + + /* Prefix g */ + + if (hotkey_prefix == 71) { // g + + hotkey_prefix = false; + + if (keycode == 49 && $("genConfigTab")) { // 1 + selectTab("genConfig"); + return false; + } + + if (keycode == 50 && $("feedConfigTab")) { // 2 + selectTab("feedConfig"); + return false; + } + + if (keycode == 51 && $("filterConfigTab")) { // 4 + selectTab("filterConfig"); + return false; + } + + if (keycode == 52 && $("labelConfigTab")) { // 5 + selectTab("labelConfig"); + return false; + } + + if (keycode == 53 && $("userConfigTab")) { // 6 + selectTab("userConfig"); + return false; + } + + if (keycode == 88) { // x + return gotoMain(); + } + + } + + if ($("piggie")) { + if (seq.match("8073717369")) { + seq = ""; + piggie(true); + } else { + piggie(false); + } + } + + if (hotkey_prefix) { + console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar); + } else { + console.log("KP: CODE=" + keycode + " CHAR=" + keychar); + } + + } catch (e) { + exception_error("pref_hotkey_handler", e); + } +} + +function editFeedCats() { + try { + var query = "backend.php?op=pref-feeds&subop=editCats"; + + if (dijit.byId("feedCatEditDlg")) + dijit.byId("feedCatEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "feedCatEditDlg", + title: __("Feed Categories"), + style: "width: 600px", + getSelectedCategories: function() { + return getSelectedTableRowIds("prefFeedCatList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedCategories(); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected categories?")); + + if (ok) { + notify_progress("Removing selected categories...", true); + + var query = "?op=pref-feeds&subop=editCats&action=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.attr('content', transport.responseText); + updateFeedList(); + } }); + + } + + } else { + alert(__("No categories are selected.")); + } + }, + addCategory: function() { + if (this.validate()) { + notify_progress("Creating category..."); + + var query = "?op=pref-feeds&subop=editCats&action=add&cat=" + + param_escape(this.attr('value').newcat); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.attr('content', transport.responseText); + updateFeedList(); + } }); + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editFeedCats", e); + } +} + +function showInactiveFeeds() { + try { + var query = "backend.php?op=dlg&id=inactiveFeeds"; + + if (dijit.byId("inactiveFeedsDlg")) + dijit.byId("inactiveFeedsDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "inactiveFeedsDlg", + title: __("Feeds without recent updates"), + style: "width: 600px", + getSelectedFeeds: function() { + return getSelectedTableRowIds("prefInactiveFeedList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedFeeds(); + + console.log(sel_rows); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected feeds?")); + + if (ok) { + notify_progress("Removing selected feeds...", true); + + var query = "?op=pref-feeds&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + updateFeedList(); + } }); + } + + } else { + alert(__("No feeds are selected.")); + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("showInactiveFeeds", e); + } + +} + +function opmlRegenKey() { + + try { + var ok = confirm(__("Replace current OPML publishing address with a new one?")); + + if (ok) { + + notify_progress("Trying to change address...", true); + + var query = "?op=rpc&subop=regenOPMLKey"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + + var new_link = reply.link; + + var e = $('pub_opml_url'); + + if (new_link) { + e.href = new_link; + e.innerHTML = new_link; + + new Effect.Highlight(e); + + notify(''); + + } else { + notify_error("Could not change feed URL."); + } + } }); + } + } catch (e) { + exception_error("opmlRegenKey", e); + } + return false; +} + +function feedActionChange() { + try { + var chooser = $("feedActionChooser"); + var opid = chooser[chooser.selectedIndex].value; + + chooser.selectedIndex = 0; + feedActionGo(opid); + } catch (e) { + exception_error("feedActionChange", e); + } +} + +function feedActionGo(op) { + try { + if (op == "facEdit") { + + var rows = getSelectedFeeds(); + + if (rows.length > 1) { + editSelectedFeeds(); + } else { + editSelectedFeed(); + } + } + + if (op == "facClear") { + clearSelectedFeeds(); + } + + if (op == "facPurge") { + purgeSelectedFeeds(); + } + + if (op == "facEditCats") { + editFeedCats(); + } + + if (op == "facRescore") { + rescoreSelectedFeeds(); + } + + if (op == "facUnsubscribe") { + removeSelectedFeeds(); + } + + } catch (e) { + exception_error("feedActionGo", e); + + } +} + +function clearFeedArticles(feed_id) { + + notify_progress("Clearing feed..."); + + var query = "?op=pref-feeds&quiet=1&subop=clear&id=" + feed_id; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + } }); + + return false; +} + +function rescoreSelectedFeeds() { + + var sel_rows = getSelectedFeeds(); + + if (sel_rows.length > 0) { + + //var ok = confirm(__("Rescore last 100 articles in selected feeds?")); + var ok = confirm(__("Rescore articles in selected feeds?")); + + if (ok) { + notify_progress("Rescoring selected feeds...", true); + + var query = "?op=pref-feeds&subop=rescore&quiet=1&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_callback2(transport); + } }); + + } + } else { + alert(__("No feeds are selected.")); + } + + return false; +} + +function rescore_all_feeds() { + var ok = confirm(__("Rescore all articles? This operation may take a lot of time.")); + + if (ok) { + notify_progress("Rescoring feeds...", true); + + var query = "?op=pref-feeds&subop=rescoreAll&quiet=1"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_callback2(transport); + } }); + } +} + +function labelColorReset() { + try { + var labels = getSelectedLabels(); + + if (labels.length > 0) { + var ok = confirm(__("Reset selected labels to default colors?")); + + if (ok) { + var query = "?op=pref-labels&subop=color-reset&ids="+ + param_escape(labels.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + labellist_callback2(transport); + } }); + } + + } else { + alert(__("No labels are selected.")); + } + + } catch (e) { + exception_error("labelColorReset", e); + } +} + + +function inPreferences() { + return true; +} + +function editProfiles() { + try { + + if (dijit.byId("profileEditDlg")) + dijit.byId("profileEditDlg").destroyRecursive(); + + var query = "backend.php?op=dlg&id=editPrefProfiles"; + + dialog = new dijit.Dialog({ + id: "profileEditDlg", + title: __("Settings Profiles"), + style: "width: 600px", + getSelectedProfiles: function() { + return getSelectedTableRowIds("prefFeedProfileList"); + }, + removeSelected: function() { + var sel_rows = this.getSelectedProfiles(); + + if (sel_rows.length > 0) { + var ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed.")); + + if (ok) { + notify_progress("Removing selected profiles...", true); + + var query = "?op=rpc&subop=remprofiles&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + editProfiles(); + } }); + + } + + } else { + alert(__("No profiles are selected.")); + } + }, + activateProfile: function() { + var sel_rows = this.getSelectedProfiles(); + + if (sel_rows.length == 1) { + + var ok = confirm(__("Activate selected profile?")); + + if (ok) { + notify_progress("Loading, please wait..."); + + var query = "?op=rpc&subop=setprofile&id="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + window.location.reload(); + } }); + } + + } else { + alert(__("Please choose a profile to activate.")); + } + }, + addProfile: function() { + if (this.validate()) { + notify_progress("Creating profile...", true); + + var query = "?op=rpc&subop=addprofile&title=" + + param_escape(dialog.attr('value').newprofile); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + editProfiles(); + } }); + + } + }, + execute: function() { + if (this.validate()) { + } + }, + href: query}); + + dialog.show(); + } catch (e) { + exception_error("editProfiles", e); + } +} + +function activatePrefProfile() { + + var sel_rows = getSelectedFeedCats(); + + if (sel_rows.length == 1) { + + var ok = confirm(__("Activate selected profile?")); + + if (ok) { + notify_progress("Loading, please wait..."); + + var query = "?op=rpc&subop=setprofile&id="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + window.location.reload(); + } }); + } + + } else { + alert(__("Please choose a profile to activate.")); + } + + return false; +} + +function clearFeedAccessKeys() { + + var ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?")); + + if (ok) { + notify_progress("Clearing URLs..."); + + var query = "?op=rpc&subop=clearKeys"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Generated URLs cleared."); + } }); + } + + return false; +} + +function clearArticleAccessKeys() { + + var ok = confirm(__("This will invalidate all previously shared article URLs. Continue?")); + + if (ok) { + notify_progress("Clearing URLs..."); + + var query = "?op=rpc&subop=clearArticleKeys"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Shared URLs cleared."); + } }); + } + + return false; +} +function resetFeedOrder() { + try { + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: "?op=pref-feeds&subop=feedsortreset", + onComplete: function(transport) { + updateFeedList(); + } }); + + + } catch (e) { + exception_error("resetFeedOrder"); + } +} + +function resetCatOrder() { + try { + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: "?op=pref-feeds&subop=catsortreset", + onComplete: function(transport) { + updateFeedList(); + } }); + + + } catch (e) { + exception_error("resetCatOrder"); + } +} + +function editCat(id, item, event) { + try { + var new_name = prompt(__('Rename category to:'), item.name); + + if (new_name && new_name != item.name) { + + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: { + op: 'pref-feeds', + subop: 'renamecat', + id: id, + title: new_name, + }, + onComplete: function(transport) { + updateFeedList(); + } }); + } + + } catch (e) { + exception_error("editCat", e); + } +} + +function editLabel(id, event) { + try { + var query = "backend.php?op=pref-labels&subop=edit&id=" + + param_escape(id); + + if (dijit.byId("labelEditDlg")) + dijit.byId("labelEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "labelEditDlg", + title: __("Label Editor"), + style: "width: 600px", + setLabelColor: function(id, fg, bg) { + + var kind = ''; + var color = ''; + + if (fg && bg) { + kind = 'both'; + } else if (fg) { + kind = 'fg'; + color = fg; + } else if (bg) { + kind = 'bg'; + color = bg; + } + + var query = "?op=pref-labels&subop=color-set&kind="+kind+ + "&ids=" + param_escape(id) + "&fg=" + param_escape(fg) + + "&bg=" + param_escape(bg) + "&color=" + param_escape(color); + + // console.log(query); + + var e = $("LICID-" + id); + + if (e) { + if (fg) e.style.color = fg; + if (bg) e.style.backgroundColor = bg; + } + + new Ajax.Request("backend.php", { parameters: query }); + + updateFilterList(); + }, + execute: function() { + if (this.validate()) { + var caption = this.attr('value').caption; + var fg_color = this.attr('value').fg_color; + var bg_color = this.attr('value').bg_color; + var query = dojo.objectToQuery(this.attr('value')); + + dijit.byId('labelTree').setNameById(id, caption); + this.setLabelColor(id, fg_color, bg_color); + this.hide(); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + updateFilterList(); + } }); + } + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("editLabel", e); + } +} + +function clearTwitterCredentials() { + try { + var ok = confirm(__("This will clear your stored authentication information for Twitter. Continue?")); + + if (ok) { + notify_progress("Clearing credentials..."); + + var query = "?op=pref-feeds&subop=remtwitterinfo"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify_info("Twitter credentials have been cleared."); + updateFeedList(); + } }); + } + + } catch (e) { + exception_error("clearTwitterCredentials", e); + } +} + +function customizeCSS() { + try { + var query = "backend.php?op=dlg&id=customizeCSS"; + + if (dijit.byId("cssEditDlg")) + dijit.byId("cssEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "cssEditDlg", + title: __("Customize stylesheet"), + style: "width: 600px", + execute: function() { + notify_progress('Saving data...', true); + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + notify(''); + window.location.reload(); + } }); + + }, + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("customizeCSS", e); + } +} + +function insertSSLserial(value) { + try { + dijit.byId("SSL_CERT_SERIAL").attr('value', value); + } catch (e) { + exception_error("insertSSLcerial", e); + } +} + +function getSelectedInstances() { + return getSelectedTableRowIds("prefInstanceList"); +} + +function addInstance() { + try { + var query = "backend.php?op=dlg&id=addInstance"; + + if (dijit.byId("instanceAddDlg")) + dijit.byId("instanceAddDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "instanceAddDlg", + title: __("Link Instance"), + style: "width: 600px", + regenKey: function() { + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=genHash", + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + if (reply) + dijit.byId('instance_add_key').attr('value', reply.hash); + + } }); + }, + execute: function() { + if (this.validate()) { + console.warn(dojo.objectToQuery(this.attr('value'))); + + notify_progress('Saving data...', true); + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify(''); + updateInstanceList(); + } }); + } + }, + href: query, + }); + + dialog.show(); + + } catch (e) { + exception_error("addInstance", e); + } +} + +function editInstance(id, event) { + try { + if (!event || !event.ctrlKey) { + + selectTableRows('prefInstanceList', 'none'); + selectTableRowById('LIRR-'+id, 'LICHK-'+id, true); + + var query = "backend.php?op=pref-instances&subop=edit&id=" + + param_escape(id); + + if (dijit.byId("instanceEditDlg")) + dijit.byId("instanceEditDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "instanceEditDlg", + title: __("Edit Instance"), + style: "width: 600px", + regenKey: function() { + new Ajax.Request("backend.php", { + parameters: "?op=rpc&subop=genHash", + onComplete: function(transport) { + var reply = JSON.parse(transport.responseText); + if (reply) + dijit.byId('instance_edit_key').attr('value', reply.hash); + + } }); + }, + execute: function() { + if (this.validate()) { +// console.warn(dojo.objectToQuery(this.attr('value'))); + + notify_progress('Saving data...', true); + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + dialog.hide(); + notify(''); + updateInstanceList(); + } }); + } + }, + href: query, + }); + + dialog.show(); + + } else if (event.ctrlKey) { + var cb = $('LICHK-' + id); + cb.checked = !cb.checked; + toggleSelectRow(cb); + } + + + } catch (e) { + exception_error("editInstance", e); + } +} + +function removeSelectedInstances() { + try { + var sel_rows = getSelectedInstances(); + + if (sel_rows.length > 0) { + + var ok = confirm(__("Remove selected instances?")); + + if (ok) { + notify_progress("Removing selected instances..."); + + var query = "?op=pref-instances&subop=remove&ids="+ + param_escape(sel_rows.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + updateInstanceList(); + } }); + } + + } else { + alert(__("No instances are selected.")); + } + + } catch (e) { + exception_error("removeInstance", e); + } +} + +function editSelectedInstance() { + var rows = getSelectedInstances(); + + if (rows.length == 0) { + alert(__("No instances are selected.")); + return; + } + + if (rows.length > 1) { + alert(__("Please select only one instance.")); + return; + } + + notify(""); + + editInstance(rows[0]); +} + diff --git a/js/tt-rss.js b/js/tt-rss.js new file mode 100644 index 000000000..589091959 --- /dev/null +++ b/js/tt-rss.js @@ -0,0 +1,1164 @@ +var total_unread = 0; +var global_unread = -1; +var firsttime_update = true; +var _active_feed_id = 0; +var _active_feed_is_cat = false; +var hotkey_prefix = false; +var hotkey_prefix_pressed = false; +var init_params = {}; +var _force_scheduled_update = false; +var last_scheduled_update = false; + +var _rpc_seq = 0; + +function next_seq() { + _rpc_seq += 1; + return _rpc_seq; +} + +function get_seq() { + return _rpc_seq; +} + +function activeFeedIsCat() { + return _active_feed_is_cat; +} + +function getActiveFeedId() { + try { + //console.log("gAFID: " + _active_feed_id); + return _active_feed_id; + } catch (e) { + exception_error("getActiveFeedId", e); + } +} + +function setActiveFeedId(id, is_cat) { + try { + _active_feed_id = id; + + if (is_cat != undefined) { + _active_feed_is_cat = is_cat; + } + + selectFeed(id, is_cat); + + } catch (e) { + exception_error("setActiveFeedId", e); + } +} + + +function updateFeedList() { + try { + +// $("feeds-holder").innerHTML = "
    " + +// __("Loading, please wait...") + "
    "; + + Element.show("feedlistLoading"); + + if (dijit.byId("feedTree")) { + dijit.byId("feedTree").destroyRecursive(); + } + + var store = new dojo.data.ItemFileWriteStore({ + url: "backend.php?op=feeds"}); + + var treeModel = new fox.FeedStoreModel({ + store: store, + query: { + "type": "feed" + }, + rootId: "root", + rootLabel: "Feeds", + childrenAttrs: ["items"] + }); + + var tree = new fox.FeedTree({ + persist: false, + model: treeModel, + onOpen: function (item, node) { + var id = String(item.id); + var cat_id = id.substr(id.indexOf(":")+1); + + new Ajax.Request("backend.php", + { parameters: "backend.php?op=feeds&subop=collapse&cid=" + + param_escape(cat_id) + "&mode=0" } ); + }, + onClose: function (item, node) { + var id = String(item.id); + var cat_id = id.substr(id.indexOf(":")+1); + + new Ajax.Request("backend.php", + { parameters: "backend.php?op=feeds&subop=collapse&cid=" + + param_escape(cat_id) + "&mode=1" } ); + + }, + onClick: function (item, node) { + var id = String(item.id); + var is_cat = id.match("^CAT:"); + var feed = id.substr(id.indexOf(":")+1); + viewfeed(feed, '', is_cat); + return false; + }, + openOnClick: false, + showRoot: false, + id: "feedTree", + }, "feedTree"); + +/* var menu = new dijit.Menu({id: 'feedMenu'}); + + menu.addChild(new dijit.MenuItem({ + label: "Simple menu item" + })); + +// menu.bindDomNode(tree.domNode); */ + + var tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) { + console.log(dijit.getEnclosingWidget(event.target)); + dojo.disconnect(tmph); + }); + + $("feeds-holder").appendChild(tree.domNode); + + var tmph = dojo.connect(tree, 'onLoad', function() { + dojo.disconnect(tmph); + Element.hide("feedlistLoading"); + + tree.collapseHiddenCats(); + + feedlist_init(); + +// var node = dijit.byId("feedTree")._itemNodesMap['FEED:-2'][0].domNode +// menu.bindDomNode(node); + + loading_set_progress(25); + }); + + tree.startup(); + + } catch (e) { + exception_error("updateFeedList", e); + } +} + +function catchupAllFeeds() { + + var str = __("Mark all articles as read?"); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + + var query_str = "backend.php?op=feeds&subop=catchupAll"; + + notify_progress("Marking all feeds as read..."); + + //console.log("catchupAllFeeds Q=" + query_str); + + new Ajax.Request("backend.php", { + parameters: query_str, + onComplete: function(transport) { + feedlist_callback2(transport); + } }); + + global_unread = 0; + updateTitle(""); + } +} + +function viewCurrentFeed(subop) { + + if (getActiveFeedId() != undefined) { + viewfeed(getActiveFeedId(), subop, activeFeedIsCat()); + } + return false; // block unneeded form submits +} + +function timeout() { + if (getInitParam("bw_limit") == "1") return; + + try { + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + if (ts - last_scheduled_update > 10 || _force_scheduled_update) { + + //console.log("timeout()"); + + window.clearTimeout(counter_timeout_id); + + var query_str = "?op=rpc&subop=getAllCounters&seq=" + next_seq(); + + var omode; + + if (firsttime_update && !navigator.userAgent.match("Opera")) { + firsttime_update = false; + omode = "T"; + } else { + omode = "flc"; + } + + query_str = query_str + "&omode=" + omode; + + if (!_force_scheduled_update) + query_str = query_str + "&last_article_id=" + getInitParam("last_article_id"); + + //console.log("[timeout]" + query_str); + + new Ajax.Request("backend.php", { + parameters: query_str, + onComplete: function(transport) { + handle_rpc_json(transport, !_force_scheduled_update); + _force_scheduled_update = false; + } }); + + last_scheduled_update = ts; + } + + } catch (e) { + exception_error("timeout", e); + } + + setTimeout("timeout()", 3000); +} + +function search() { + var query = "backend.php?op=dlg&id=search¶m=" + + param_escape(getActiveFeedId() + ":" + activeFeedIsCat()); + + if (dijit.byId("searchDlg")) + dijit.byId("searchDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "searchDlg", + title: __("Search"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + _search_query = dojo.objectToQuery(this.attr('value')); + this.hide(); + viewCurrentFeed(); + } + }, + href: query}); + + dialog.show(); +} + +function updateTitle() { + var tmp = "Tiny Tiny RSS"; + + if (global_unread > 0) { + tmp = tmp + " (" + global_unread + ")"; + } + + if (window.fluid) { + if (global_unread > 0) { + window.fluid.dockBadge = global_unread; + } else { + window.fluid.dockBadge = ""; + } + } + + document.title = tmp; +} + +function genericSanityCheck() { + setCookie("ttrss_test", "TEST"); + + if (getCookie("ttrss_test") != "TEST") { + return fatalError(2); + } + + return true; +} + +function init() { + try { + dojo.registerModulePath("fox", "../../js/"); + + dojo.require("fox.FeedTree"); + + if (typeof themeBeforeLayout == 'function') { + themeBeforeLayout(); + } + + dojo.parser.parse(); + + dojo.addOnLoad(function() { + updateFeedList(); + closeArticlePanel(); + + if (typeof themeAfterLayout == 'function') { + themeAfterLayout(); + } + + }); + + if (!genericSanityCheck()) + return false; + + loading_set_progress(20); + + var hasAudio = !!((myAudioTag = document.createElement('audio')).canPlayType); + + new Ajax.Request("backend.php", { + parameters: {op: "rpc", subop: "sanityCheck", hasAudio: hasAudio}, + onComplete: function(transport) { + backend_sanity_check_callback(transport); + } }); + + } catch (e) { + exception_error("init", e); + } +} + +function init_second_stage() { + + try { + + delCookie("ttrss_test"); + + var toolbar = document.forms["main_toolbar_form"]; + + dijit.getEnclosingWidget(toolbar.view_mode).attr('value', + getInitParam("default_view_mode")); + + dijit.getEnclosingWidget(toolbar.order_by).attr('value', + getInitParam("default_view_order_by")); + + feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1; + + loading_set_progress(30); + + // can't use cache_clear() here because viewfeed might not have initialized yet + if ('sessionStorage' in window && window['sessionStorage'] !== null) + sessionStorage.clear(); + + console.log("second stage ok"); + + } catch (e) { + exception_error("init_second_stage", e); + } +} + +function quickMenuGo(opid) { + try { + if (opid == "qmcPrefs") { + gotoPreferences(); + } + + if (opid == "qmcTagCloud") { + displayDlg("printTagCloud"); + } + + if (opid == "qmcTagSelect") { + displayDlg("printTagSelect"); + } + + if (opid == "qmcSearch") { + search(); + return; + } + + if (opid == "qmcAddFeed") { + quickAddFeed(); + return; + } + + if (opid == "qmcDigest") { + window.location.href = "digest.php"; + return; + } + + if (opid == "qmcEditFeed") { + if (activeFeedIsCat()) + alert(__("You can't edit this kind of feed.")); + else + editFeed(getActiveFeedId()); + return; + } + + if (opid == "qmcRemoveFeed") { + var actid = getActiveFeedId(); + + if (activeFeedIsCat()) { + alert(__("You can't unsubscribe from the category.")); + return; + } + + if (!actid) { + alert(__("Please select some feed first.")); + return; + } + + var fn = getFeedName(actid); + + var pr = __("Unsubscribe from %s?").replace("%s", fn); + + if (confirm(pr)) { + unsubscribeFeed(actid); + } + + return; + } + + if (opid == "qmcCatchupAll") { + catchupAllFeeds(); + return; + } + + if (opid == "qmcShowOnlyUnread") { + toggleDispRead(); + return; + } + + if (opid == "qmcAddFilter") { + quickAddFilter(); + return; + } + + if (opid == "qmcAddLabel") { + addLabel(); + return; + } + + if (opid == "qmcRescoreFeed") { + rescoreCurrentFeed(); + return; + } + + if (opid == "qmcHKhelp") { + //Element.show("hotkey_help_overlay"); + Effect.Appear("hotkey_help_overlay", {duration : 0.3}); + } + + if (opid == "qmcAbout") { + dialog = new dijit.Dialog({ + title: __("About..."), + style: "width: 400px", + href: "backend.php?op=dlg&id=about", + }); + + dialog.show(); + } + + } catch (e) { + exception_error("quickMenuGo", e); + } +} + +function toggleDispRead() { + try { + + var hide = !(getInitParam("hide_read_feeds") == "1"); + + hideOrShowFeeds(hide); + + var query = "?op=rpc&subop=setpref&key=HIDE_READ_FEEDS&value=" + + param_escape(hide); + + setInitParam("hide_read_feeds", hide); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + } }); + + } catch (e) { + exception_error("toggleDispRead", e); + } +} + +function parse_runtime_info(data) { + + //console.log("parsing runtime info..."); + + for (k in data) { + var v = data[k]; + +// console.log("RI: " + k + " => " + v); + + if (k == "new_version_available") { + var icon = $("newVersionIcon"); + if (icon) { + if (v == "1") { + icon.style.display = "inline"; + } else { + icon.style.display = "none"; + } + } + return; + } + + if (k == "daemon_is_running" && v != 1) { + notify_error("Update daemon is not running.", true); + return; + } + + if (k == "daemon_stamp_ok" && v != 1) { + notify_error("Update daemon is not updating feeds.", true); + return; + } + + if (k == "max_feed_id" || k == "num_feeds") { + if (init_params[k] != v) { + console.log("feed count changed, need to reload feedlist."); + updateFeedList(); + } + } + + init_params[k] = v; + notify(''); + } +} + +function catchupCurrentFeed() { + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + var str = __("Mark all articles in %s as read?").replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + return viewCurrentFeed('MarkAllRead'); + } +} + +function catchupFeedInGroup(id) { + + try { + + var title = getFeedName(id); + + var str = __("Mark all articles in %s as read?").replace("%s", title); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + return viewCurrentFeed('MarkAllReadGR:' + id); + } + + } catch (e) { + exception_error("catchupFeedInGroup", e); + } +} + +function collapse_feedlist() { + try { + + if (!Element.visible('feeds-holder')) { + Element.show('feeds-holder'); + Element.show('feeds-holder_splitter'); + $("collapse_feeds_btn").innerHTML = "<<"; + } else { + Element.hide('feeds-holder'); + Element.hide('feeds-holder_splitter'); + $("collapse_feeds_btn").innerHTML = ">>"; + } + + dijit.byId("main").resize(); + + query = "?op=rpc&subop=setpref&key=_COLLAPSED_FEEDLIST&value=true"; + new Ajax.Request("backend.php", { parameters: query }); + + } catch (e) { + exception_error("collapse_feedlist", e); + } +} + +function viewModeChanged() { + return viewCurrentFeed(''); +} + +function viewLimitChanged() { + return viewCurrentFeed(''); +} + +/* function adjustArticleScore(id, score) { + try { + + var pr = prompt(__("Assign score to article:"), score); + + if (pr != undefined) { + var query = "?op=rpc&subop=setScore&id=" + id + "&score=" + pr; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + viewCurrentFeed(); + } }); + + } + } catch (e) { + exception_error("adjustArticleScore", e); + } +} */ + +function rescoreCurrentFeed() { + + var actid = getActiveFeedId(); + + if (activeFeedIsCat() || actid < 0) { + alert(__("You can't rescore this kind of feed.")); + return; + } + + if (!actid) { + alert(__("Please select some feed first.")); + return; + } + + var fn = getFeedName(actid); + var pr = __("Rescore articles in %s?").replace("%s", fn); + + if (confirm(pr)) { + notify_progress("Rescoring articles..."); + + var query = "?op=pref-feeds&subop=rescore&quiet=1&ids=" + actid; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + viewCurrentFeed(); + } }); + } +} + +function hotkey_handler(e) { + try { + + if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return; + + var keycode = false; + var shift_key = false; + + var cmdline = $('cmdline'); + + try { + shift_key = e.shiftKey; + } catch (e) { + + } + + if (window.event) { + keycode = window.event.keyCode; + } else if (e) { + keycode = e.which; + } + + var keychar = String.fromCharCode(keycode); + + if (keycode == 27) { // escape + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + hotkey_prefix = false; + } + + if (keycode == 16) return; // ignore lone shift + if (keycode == 17) return; // ignore lone ctrl + + if ((keycode == 70 || keycode == 67 || keycode == 71 || keycode == 65) + && !hotkey_prefix) { + + var date = new Date(); + var ts = Math.round(date.getTime() / 1000); + + hotkey_prefix = keycode; + hotkey_prefix_pressed = ts; + + cmdline.innerHTML = keychar; + Element.show(cmdline); + + console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar + " TS=" + ts); + return true; + } + + if (Element.visible("hotkey_help_overlay")) { + Element.hide("hotkey_help_overlay"); + } + + /* Global hotkeys */ + + Element.hide(cmdline); + + if (!hotkey_prefix) { + + if (keycode == 27) { // escape + closeArticlePanel(); + return; + } + + if (keycode == 69) { // e + var id = getActiveArticleId(); + emailArticle(id); + } + + if ((keycode == 191 || keychar == '?') && shift_key) { // ? + if (!Element.visible("hotkey_help_overlay")) { + Effect.Appear("hotkey_help_overlay", {duration : 0.3, to : 0.9}); + } else { + Element.hide("hotkey_help_overlay"); + } + return false; + } + + if (keycode == 191 || keychar == '/') { // / + search(); + return false; + } + + if (keycode == 74 && !shift_key) { // j + var rv = dijit.byId("feedTree").getPreviousFeed( + getActiveFeedId(), activeFeedIsCat()); + + if (rv) viewfeed(rv[0], '', rv[1]); + + return; + } + + if (keycode == 75) { // k + var rv = dijit.byId("feedTree").getNextFeed( + getActiveFeedId(), activeFeedIsCat()); + + if (rv) viewfeed(rv[0], '', rv[1]); + + return; + } + + if (shift_key && keycode == 40) { // shift-down + catchupRelativeToArticle(1); + return; + } + + if (shift_key && keycode == 38) { // shift-up + catchupRelativeToArticle(0); + return; + } + + if (shift_key && keycode == 78) { // N + scrollArticle(50); + return; + } + + if (shift_key && keycode == 80) { // P + scrollArticle(-50); + return; + } + + if (keycode == 68 && shift_key) { // shift-D + dismissSelectedArticles(); + return; + } + + if (keycode == 88 && shift_key) { // shift-X + dismissReadArticles(); + return; + } + + if (keycode == 78 || keycode == 40) { // n, down + if (typeof moveToPost != 'undefined') { + moveToPost('next'); + return false; + } + } + + if (keycode == 80 || keycode == 38) { // p, up + if (typeof moveToPost != 'undefined') { + moveToPost('prev'); + return false; + } + } + + if (keycode == 83 && shift_key) { // S + selectionTogglePublished(undefined, false, true); + return; + } + + if (keycode == 83) { // s + selectionToggleMarked(undefined, false, true); + return; + } + + if (keycode == 85) { // u + selectionToggleUnread(undefined, false, true); + return; + } + + if (keycode == 84 && shift_key) { // T + var id = getActiveArticleId(); + if (id) { + editArticleTags(id, getActiveFeedId(), isCdmMode()); + return; + } + } + + if (keycode == 9) { // tab + var id = getArticleUnderPointer(); + if (id) { + var cb = $("RCHK-" + id); + + if (cb) { + cb.checked = !cb.checked; + toggleSelectRowById(cb, "RROW-" + id); + return false; + } + } + } + + if (keycode == 79) { // o + if (getActiveArticleId()) { + openArticleInNewWindow(getActiveArticleId()); + return; + } + } + + if (keycode == 81 && shift_key) { // Q + if (typeof catchupAllFeeds != 'undefined') { + catchupAllFeeds(); + return; + } + } + + if (keycode == 88 && !shift_key) { // x + if (activeFeedIsCat()) { + dijit.byId("feedTree").collapseCat(getActiveFeedId()); + return; + } + } + } + + /* Prefix a */ + + if (hotkey_prefix == 65) { // a + hotkey_prefix = false; + + if (keycode == 65) { // a + selectArticles('all'); + return; + } + + if (keycode == 85) { // u + selectArticles('unread'); + return; + } + + if (keycode == 73) { // i + selectArticles('invert'); + return; + } + + if (keycode == 78) { // n + selectArticles('none'); + return; + } + + } + + /* Prefix f */ + + if (hotkey_prefix == 70) { // f + + hotkey_prefix = false; + + if (keycode == 81) { // q + if (getActiveFeedId()) { + catchupCurrentFeed(); + return; + } + } + + if (keycode == 82) { // r + if (getActiveFeedId()) { + viewfeed(getActiveFeedId(), '', activeFeedIsCat()); + return; + } + } + + if (keycode == 65) { // a + toggleDispRead(); + return false; + } + + if (keycode == 85) { // u + if (getActiveFeedId()) { + viewfeed(getActiveFeedId(), ''); + return false; + } + } + + if (keycode == 69) { // e + + if (activeFeedIsCat()) + alert(__("You can't edit this kind of feed.")); + else + editFeed(getActiveFeedId()); + return; + + return false; + } + + if (keycode == 83) { // s + quickAddFeed(); + return false; + } + + if (keycode == 67 && shift_key) { // C + if (typeof catchupAllFeeds != 'undefined') { + catchupAllFeeds(); + return false; + } + } + + if (keycode == 67) { // c + if (getActiveFeedId()) { + catchupCurrentFeed(); + return false; + } + } + + if (keycode == 88) { // x + reverseHeadlineOrder(); + return; + } + } + + /* Prefix c */ + + if (hotkey_prefix == 67) { // c + hotkey_prefix = false; + + if (keycode == 70) { // f + quickAddFilter(); + return false; + } + + if (keycode == 76) { // l + addLabel(); + return false; + } + + if (keycode == 83) { // s + if (typeof collapse_feedlist != 'undefined') { + collapse_feedlist(); + return false; + } + } + + if (keycode == 77) { // m + // TODO: sortable feedlist + return; + } + + if (keycode == 78) { // n + catchupRelativeToArticle(1); + return; + } + + if (keycode == 80) { // p + catchupRelativeToArticle(0); + return; + } + + + } + + /* Prefix g */ + + if (hotkey_prefix == 71) { // g + + hotkey_prefix = false; + + + if (keycode == 65) { // a + viewfeed(-4); + return false; + } + + if (keycode == 83) { // s + viewfeed(-1); + return false; + } + + if (keycode == 80 && shift_key) { // P + gotoPreferences(); + return false; + } + + if (keycode == 80) { // p + viewfeed(-2); + return false; + } + + if (keycode == 70) { // f + viewfeed(-3); + return false; + } + + if (keycode == 84) { // t + displayDlg("printTagCloud"); + return false; + } + } + + /* Cmd */ + + if (hotkey_prefix == 224 || hotkey_prefix == 91) { // f + hotkey_prefix = false; + return; + } + + if (hotkey_prefix) { + console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar); + } else { + console.log("KP: CODE=" + keycode + " CHAR=" + keychar); + } + + + } catch (e) { + exception_error("hotkey_handler", e); + } +} + +function inPreferences() { + return false; +} + +function reverseHeadlineOrder() { + try { + + var query_str = "?op=rpc&subop=togglepref&key=REVERSE_HEADLINES"; + + new Ajax.Request("backend.php", { + parameters: query_str, + onComplete: function(transport) { + viewCurrentFeed(); + } }); + + } catch (e) { + exception_error("reverseHeadlineOrder", e); + } +} + +function scheduleFeedUpdate(id, is_cat) { + try { + if (!id) { + id = getActiveFeedId(); + is_cat = activeFeedIsCat(); + } + + if (!id) { + alert(__("Please select some feed first.")); + return; + } + + var query = "?op=rpc&subop=scheduleFeedUpdate&id=" + + param_escape(id) + + "&is_cat=" + param_escape(is_cat); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + + var reply = JSON.parse(transport.responseText); + var message = reply['message']; + + if (message) { + notify_info(message); + return; + } + + } }); + + + } catch (e) { + exception_error("scheduleFeedUpdate", e); + } +} + +function newVersionDlg() { + try { + var query = "backend.php?op=dlg&id=newVersion"; + + if (dijit.byId("newVersionDlg")) + dijit.byId("newVersionDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "newVersionDlg", + title: __("New version available!"), + style: "width: 600px", + href: query, + }); + + dialog.show(); + + } catch (e) { + exception_error("newVersionDlg", e); + } +} + +function handle_rpc_json(transport, scheduled_call) { + try { + var reply = JSON.parse(transport.responseText); + + if (reply) { + + var error = reply['error']; + + if (error) { + var code = error['code']; + var msg = error['msg']; + + console.warn("[handle_rpc_json] received fatal error " + code + "/" + msg); + + if (code != 0) { + fatalError(code, msg); + return false; + } + } + + var seq = reply['seq']; + + if (seq) { + if (get_seq() != seq) { + console.log("[handle_rpc_json] sequence mismatch: " + seq + + " (want: " + get_seq() + ")"); + return true; + } + } + + var message = reply['message']; + + if (message) { + if (message == "UPDATE_COUNTERS") { + console.log("need to refresh counters..."); + setInitParam("last_article_id", -1); + _force_scheduled_update = true; + } + } + + var counters = reply['counters']; + + if (counters) + parse_counters(counters, scheduled_call); + + var runtime_info = reply['runtime-info'];; + + if (runtime_info) + parse_runtime_info(runtime_info); + + hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); + + } else { + notify_error("Error communicating with server."); + } + + } catch (e) { + notify_error("Error communicating with server."); + console.log(e); + //exception_error("handle_rpc_json", e, transport); + } + + return true; +} + diff --git a/js/viewfeed.js b/js/viewfeed.js new file mode 100644 index 000000000..9cb902315 --- /dev/null +++ b/js/viewfeed.js @@ -0,0 +1,2245 @@ +var active_post_id = false; + +var article_cache = new Array(); + +var vgroup_last_feed = false; +var post_under_pointer = false; + +var last_requested_article = false; + +var catchup_id_batch = []; +var catchup_timeout_id = false; +var feed_precache_timeout_id = false; +var precache_idle_timeout_id = false; + +var cids_requested = []; + +var has_storage = 'sessionStorage' in window && window['sessionStorage'] !== null; + +function headlines_callback2(transport, offset, background, infscroll_req) { + try { + handle_rpc_json(transport); + + loading_set_progress(25); + + console.log("headlines_callback2 [offset=" + offset + "] B:" + background + " I:" + infscroll_req); + + var is_cat = false; + var feed_id = false; + + var reply = false; + + try { + reply = JSON.parse(transport.responseText); + } catch (e) { + console.error(e); + } + + if (reply) { + + is_cat = reply['headlines']['is_cat']; + feed_id = reply['headlines']['id']; + + if (background) { + var content = reply['headlines']['content']; + + if (getInitParam("cdm_auto_catchup") == 1) { + content = content + "
    "; + } + + cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], content); + return; + } + + setActiveFeedId(feed_id, is_cat); + + try { + if (offset == 0 && infscroll_req == false) { + $("headlines-frame").scrollTop = 0; + } + } catch (e) { }; + + var headlines_count = reply['headlines-info']['count']; + + vgroup_last_feed = reply['headlines-info']['vgroup_last_feed']; + + if (parseInt(headlines_count) < getInitParam("default_article_limit")) { + _infscroll_disable = 1; + } else { + _infscroll_disable = 0; + } + + var counters = reply['counters']; + var articles = reply['articles']; + //var runtime_info = reply['runtime-info']; + + if (offset == 0 && infscroll_req == false) { + dijit.byId("headlines-frame").attr('content', + reply['headlines']['content']); + + dijit.byId("headlines-toolbar").attr('content', + reply['headlines']['toolbar']); + + + if (getInitParam("cdm_auto_catchup") == 1) { + var hsp = $("headlines-spacer"); + if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); + dijit.byId('headlines-frame').domNode.appendChild(hsp); + } + + initHeadlinesMenu(); + + } else { + + if (headlines_count > 0 && feed_id == getActiveFeedId() && is_cat == activeFeedIsCat()) { + console.log("adding some more headlines..."); + + var c = dijit.byId("headlines-frame"); + var ids = getSelectedArticleIds2(); + + $("headlines-tmp").innerHTML = reply['headlines']['content']; + + var hsp = $("headlines-spacer"); + + if (hsp) + c.domNode.removeChild(hsp); + + $$("#headlines-tmp > div").each(function(row) { + if ($$("#headlines-frame DIV[id="+row.id+"]").length == 0) { + row.style.display = 'none'; + c.domNode.appendChild(row); + } else { + row.parentNode.removeChild(row); + } + }); + + if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); + + fixHeadlinesOrder(getLoadedArticleIds()); + + if (getInitParam("cdm_auto_catchup") == 1) { + c.domNode.appendChild(hsp); + } + + console.log("restore selected ids: " + ids); + + for (var i = 0; i < ids.length; i++) { + markHeadline(ids[i]); + } + + initHeadlinesMenu(); + + $$("#headlines-frame > div[id*=RROW]").each( + function(child) { + if (!Element.visible(child)) + new Effect.Appear(child, { duration : 0.5 }); + }); + + } else { + console.log("no new headlines received"); + + var hsp = $("headlines-spacer"); + + if (hsp) hsp.innerHTML = ""; + } + } + + if (headlines_count > 0) + cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], $("headlines-frame").innerHTML); + + if (articles) { + for (var i = 0; i < articles.length; i++) { + var a_id = articles[i]['id']; + cache_set("article:" + a_id, articles[i]['content']); + } + } else { + console.log("no cached articles received"); + } + + // do not precache stuff after fresh feed + if (feed_id != -3) + precache_headlines(); + + if (counters) + parse_counters(counters); + else + request_counters(); + + } else { + console.error("Invalid object received: " + transport.responseText); + dijit.byId("headlines-frame").attr('content', "
    " + + __('Could not update headlines (invalid object received - see error console for details)') + + "
    "); + } + + _infscroll_request_sent = 0; + + notify(""); + + } catch (e) { + exception_error("headlines_callback2", e, transport); + } +} + +function render_article(article) { + try { + dijit.byId("headlines-wrap-inner").addChild( + dijit.byId("content-insert")); + + var c = dijit.byId("content-insert"); + + try { + c.domNode.scrollTop = 0; + } catch (e) { }; + + c.attr('content', article); + + correctHeadlinesOffset(getActiveArticleId()); + + try { + c.focus(); + } catch (e) { }; + + } catch (e) { + exception_error("render_article", e); + } +} + +function showArticleInHeadlines(id) { + + try { + + selectArticles("none"); + + var crow = $("RROW-" + id); + + if (!crow) return; + + var article_is_unread = crow.hasClassName("Unread"); + + crow.removeClassName("Unread"); + + selectArticles('none'); + + var upd_img_pic = $("FUPDPIC-" + id); + + var view_mode = false; + + try { + view_mode = document.forms['main_toolbar_form'].view_mode; + view_mode = view_mode[view_mode.selectedIndex].value; + } catch (e) { + // + } + + if (upd_img_pic && (upd_img_pic.src.match("updated.png") || + upd_img_pic.src.match("fresh_sign.png"))) { + + upd_img_pic.src = "images/blank_icon.gif"; + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + } else if (article_is_unread && view_mode == "all_articles") { + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + } + + markHeadline(id); + + if (article_is_unread) + _force_scheduled_update = true; + + } catch (e) { + exception_error("showArticleInHeadlines", e); + } +} + +function article_callback2(transport, id) { + try { + console.log("article_callback2 " + id); + + handle_rpc_json(transport); + + var reply = false; + + try { + reply = JSON.parse(transport.responseText); + } catch (e) { + console.error(e); + } + + if (reply) { + + var upic = $('FUPDPIC-' + id); + + if (upic) upic.src = 'images/blank_icon.gif'; + + reply.each(function(article) { + if (active_post_id == article['id']) { + render_article(article['content']); + } + cids_requested.remove(article['id']); + + cache_set("article:" + article['id'], article['content']); + }); + +// if (id != last_requested_article) { +// console.log("requested article id is out of sequence, aborting"); +// return; +// } + + } else { + console.error("Invalid object received: " + transport.responseText); + + render_article("
    " + + __('Could not display article (invalid object received - see error console for details)') + "
    "); + } + + request_counters(); + + try { + if (!_infscroll_disable && + $$("#headlines-frame > div[id*=RROW]").last().hasClassName("Selected")) { + + loadMoreHeadlines(); + } + } catch (e) { + console.warn(e); + } + + notify(""); + } catch (e) { + exception_error("article_callback2", e, transport); + } +} + +function view(id) { + try { + console.log("loading article: " + id); + + var cached_article = cache_get("article:" + id); + + console.log("cache check result: " + (cached_article != false)); + + hideAuxDlg(); + + var query = "?op=view&id=" + param_escape(id); + + var neighbor_ids = getRelativePostIds(id); + + /* only request uncached articles */ + + var cids_to_request = []; + + for (var i = 0; i < neighbor_ids.length; i++) { + if (cids_requested.indexOf(neighbor_ids[i]) == -1) + if (!cache_get("article:" + neighbor_ids[i])) { + cids_to_request.push(neighbor_ids[i]); + cids_requested.push(neighbor_ids[i]); + } + } + + console.log("additional ids: " + cids_to_request.toString()); + + query = query + "&cids=" + cids_to_request.toString(); + + var crow = $("RROW-" + id); + var article_is_unread = crow.hasClassName("Unread"); + + active_post_id = id; + showArticleInHeadlines(id); + + precache_headlines(); + + if (!cached_article) { + + var upic = $('FUPDPIC-' + id); + + if (upic) { + upic.src = getInitParam("sign_progress"); + } + + } else if (cached_article && article_is_unread) { + + query = query + "&mode=prefetch"; + + render_article(cached_article); + + } else if (cached_article) { + + query = query + "&mode=prefetch_old"; + render_article(cached_article); + + // if we don't need to request any relative ids, we might as well skip + // the server roundtrip altogether + if (cids_to_request.length == 0) { + + try { + if (!_infscroll_disable && + $$("#headlines-frame > div[id*=RROW]").last().hasClassName("Selected")) { + + loadMoreHeadlines(); + } + } catch (e) { + console.warn(e); + } + + return; + } + } + + last_requested_article = id; + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + article_callback2(transport, id); + } }); + + return false; + + } catch (e) { + exception_error("view", e); + } +} + +function toggleMark(id, client_only) { + try { + var query = "?op=rpc&id=" + id + "&subop=mark"; + + var img = $("FMPIC-" + id); + + if (!img) return; + + if (img.src.match("mark_unset")) { + img.src = img.src.replace("mark_unset", "mark_set"); + img.alt = __("Unstar article"); + query = query + "&mark=1"; + + } else { + img.src = img.src.replace("mark_set", "mark_unset"); + img.alt = __("Star article"); + query = query + "&mark=0"; + } + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + if (!client_only) { + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + } + + } catch (e) { + exception_error("toggleMark", e); + } +} + +function togglePub(id, client_only, no_effects, note) { + try { + var query = "?op=rpc&id=" + id + "&subop=publ"; + + if (note != undefined) { + query = query + "¬e=" + param_escape(note); + } else { + query = query + "¬e=undefined"; + } + + var img = $("FPPIC-" + id); + + if (!img) return; + + if (img.src.match("pub_unset") || note != undefined) { + img.src = img.src.replace("pub_unset", "pub_set"); + img.alt = __("Unpublish article"); + query = query + "&pub=1"; + + } else { + img.src = img.src.replace("pub_set", "pub_unset"); + img.alt = __("Publish article"); + + query = query + "&pub=0"; + } + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + if (!client_only) { + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + } + + } catch (e) { + exception_error("togglePub", e); + } +} + +function moveToPost(mode) { + + try { + + var rows = getVisibleArticleIds(); + + var prev_id = false; + var next_id = false; + + if (!$('RROW-' + active_post_id)) { + active_post_id = false; + } + + if (active_post_id == false) { + next_id = getFirstVisibleHeadlineId(); + prev_id = getLastVisibleHeadlineId(); + } else { + for (var i = 0; i < rows.length; i++) { + if (rows[i] == active_post_id) { + prev_id = rows[i-1]; + next_id = rows[i+1]; + } + } + } + + if (mode == "next") { + if (next_id) { + if (isCdmMode()) { + + cdmExpandArticle(next_id); + cdmScrollToArticleId(next_id); + + } else { + correctHeadlinesOffset(next_id); + view(next_id, getActiveFeedId()); + } + } + } + + if (mode == "prev") { + if (prev_id) { + if (isCdmMode()) { + cdmExpandArticle(prev_id); + cdmScrollToArticleId(prev_id); + } else { + correctHeadlinesOffset(prev_id); + view(prev_id, getActiveFeedId()); + } + } + } + + } catch (e) { + exception_error("moveToPost", e); + } +} + +function toggleSelected(id, force_on) { + try { + + var cb = $("RCHK-" + id); + var row = $("RROW-" + id); + + if (row) { + if (row.hasClassName('Selected') && !force_on) { + row.removeClassName('Selected'); + if (cb) cb.checked = false; + } else { + row.addClassName('Selected'); + if (cb) cb.checked = true; + } + } + } catch (e) { + exception_error("toggleSelected", e); + } +} + +function toggleUnread_afh(effect) { + try { + + var elem = effect.element; + elem.style.backgroundColor = ""; + + } catch (e) { + exception_error("toggleUnread_afh", e); + } +} + +function toggleUnread(id, cmode, effect) { + try { + + var row = $("RROW-" + id); + if (row) { + if (cmode == undefined || cmode == 2) { + if (row.hasClassName("Unread")) { + row.removeClassName("Unread"); + + if (effect) { + new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5", + afterFinish: toggleUnread_afh, + queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } ); + } + + } else { + row.addClassName("Unread"); + } + + } else if (cmode == 0) { + + row.removeClassName("Unread"); + + if (effect) { + new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5", + afterFinish: toggleUnread_afh, + queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } ); + } + + } else if (cmode == 1) { + row.addClassName("Unread"); + } + + if (cmode == undefined) cmode = 2; + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=" + param_escape(cmode) + "&ids=" + param_escape(id); + +// notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + + } catch (e) { + exception_error("toggleUnread", e); + } +} + +function selectionRemoveLabel(id, ids) { + try { + + if (!ids) ids = getSelectedArticleIds2(); + + if (ids.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var query = "?op=rpc&subop=removeFromLabel&ids=" + + param_escape(ids.toString()) + "&lid=" + param_escape(id); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + show_labels_in_headlines(transport); + } }); + + } catch (e) { + exception_error("selectionAssignLabel", e); + + } +} + +function selectionAssignLabel(id, ids) { + try { + + if (!ids) ids = getSelectedArticleIds2(); + + if (ids.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var query = "?op=rpc&subop=assignToLabel&ids=" + + param_escape(ids.toString()) + "&lid=" + param_escape(id); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + show_labels_in_headlines(transport); + } }); + + } catch (e) { + exception_error("selectionAssignLabel", e); + + } +} + +function selectionToggleUnread(set_state, callback, no_error) { + try { + var rows = getSelectedArticleIds2(); + + if (rows.length == 0 && !no_error) { + alert(__("No articles are selected.")); + return; + } + + for (var i = 0; i < rows.length; i++) { + var row = $("RROW-" + rows[i]); + if (row) { + if (set_state == undefined) { + if (row.hasClassName("Unread")) { + row.removeClassName("Unread"); + } else { + row.addClassName("Unread"); + } + } + + if (set_state == false) { + row.removeClassName("Unread"); + } + + if (set_state == true) { + row.addClassName("Unread"); + } + } + } + + if (rows.length > 0) { + + var cmode = ""; + + if (set_state == undefined) { + cmode = "2"; + } else if (set_state == true) { + cmode = "1"; + } else if (set_state == false) { + cmode = "0"; + } + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=" + cmode + "&ids=" + param_escape(rows.toString()); + + notify_progress("Loading, please wait..."); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + if (callback) callback(transport); + } }); + + } + + } catch (e) { + exception_error("selectionToggleUnread", e); + } +} + +function selectionToggleMarked() { + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + for (var i = 0; i < rows.length; i++) { + toggleMark(rows[i], true, true); + } + + if (rows.length > 0) { + + var query = "?op=rpc&subop=markSelected&ids=" + + param_escape(rows.toString()) + "&cmode=2"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + + } catch (e) { + exception_error("selectionToggleMarked", e); + } +} + +function selectionTogglePublished() { + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + for (var i = 0; i < rows.length; i++) { + togglePub(rows[i], true, true); + } + + if (rows.length > 0) { + + var query = "?op=rpc&subop=publishSelected&ids=" + + param_escape(rows.toString()) + "&cmode=2"; + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + + } catch (e) { + exception_error("selectionToggleMarked", e); + } +} + +function getSelectedArticleIds2() { + + var rv = []; + + $$("#headlines-frame > div[id*=RROW][class*=Selected]").each( + function(child) { + rv.push(child.id.replace("RROW-", "")); + }); + + return rv; +} + +function getLoadedArticleIds() { + var rv = []; + + var children = $$("#headlines-frame > div[id*=RROW-]"); + + children.each(function(child) { + rv.push(child.id.replace("RROW-", "")); + }); + + return rv; + +} + +// mode = all,none,unread,invert +function selectArticles(mode) { + try { + + var children = $$("#headlines-frame > div[id*=RROW]"); + + children.each(function(child) { + var id = child.id.replace("RROW-", ""); + var cb = $("RCHK-" + id); + + if (mode == "all") { + child.addClassName("Selected"); + cb.checked = true; + } else if (mode == "unread") { + if (child.hasClassName("Unread")) { + child.addClassName("Selected"); + cb.checked = true; + } else { + child.removeClassName("Selected"); + cb.checked = false; + } + } else if (mode == "invert") { + if (child.hasClassName("Selected")) { + child.removeClassName("Selected"); + cb.checked = false; + } else { + child.addClassName("Selected"); + cb.checked = true; + } + + } else { + child.removeClassName("Selected"); + cb.checked = false; + } + }); + + } catch (e) { + exception_error("selectArticles", e); + } +} + +function catchupPage() { + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + var str = __("Mark all visible articles in %s as read?"); + + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + selectArticles('all'); + selectionToggleUnread(false, 'viewCurrentFeed()', true); + selectArticles('none'); +} + +function deleteSelection() { + + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + var str; + + if (getActiveFeedId() != 0) { + str = __("Delete %d selected articles in %s?"); + } else { + str = __("Delete %d selected articles?"); + } + + str = str.replace("%d", rows.length); + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + query = "?op=rpc&subop=delete&ids=" + param_escape(rows); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + viewCurrentFeed(); + } }); + + } catch (e) { + exception_error("deleteSelection", e); + } +} + +function archiveSelection() { + + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + var str; + var op; + + if (getActiveFeedId() != 0) { + str = __("Archive %d selected articles in %s?"); + op = "archive"; + } else { + str = __("Move %d archived articles back?"); + op = "unarchive"; + } + + str = str.replace("%d", rows.length); + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + query = "?op=rpc&subop="+op+"&ids=" + param_escape(rows); + + console.log(query); + + for (var i = 0; i < rows.length; i++) { + cache_delete("article:" + rows[i]); + } + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + viewCurrentFeed(); + } }); + + } catch (e) { + exception_error("archiveSelection", e); + } +} + +function catchupSelection() { + + try { + + var rows = getSelectedArticleIds2(); + + if (rows.length == 0) { + alert(__("No articles are selected.")); + return; + } + + var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); + + var str = __("Mark %d selected articles in %s as read?"); + + str = str.replace("%d", rows.length); + str = str.replace("%s", fn); + + if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { + return; + } + + selectionToggleUnread(false, 'viewCurrentFeed()', true); + + } catch (e) { + exception_error("catchupSelection", e); + } +} + +function editArticleTags(id) { + var query = "backend.php?op=dlg&id=editArticleTags¶m=" + param_escape(id); + + if (dijit.byId("editTagsDlg")) + dijit.byId("editTagsDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "editTagsDlg", + title: __("Edit article Tags"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + var query = dojo.objectToQuery(this.attr('value')); + + notify_progress("Saving article tags...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + + var data = JSON.parse(transport.responseText); + + if (data) { + var tags_str = article.tags; + var id = tags_str.id; + + var tags = $("ATSTR-" + id); + var tooltip = dijit.byId("ATSTRTIP-" + id); + + if (tags) tags.innerHTML = tags_str.content; + if (tooltip) tooltip.attr('label', tags_str.content_full); + + cache_delete("article:" + id); + } + + }}); + } + }, + href: query, + }); + + var tmph = dojo.connect(dialog, 'onLoad', function() { + dojo.disconnect(tmph); + + new Ajax.Autocompleter('tags_str', 'tags_choices', + "backend.php?op=rpc&subop=completeTags", + { tokens: ',', paramName: "search" }); + }); + + dialog.show(); + +} + +function cdmScrollToArticleId(id) { + try { + var ctr = $("headlines-frame"); + var e = $("RROW-" + id); + + if (!e || !ctr) return; + + ctr.scrollTop = e.offsetTop; + + } catch (e) { + exception_error("cdmScrollToArticleId", e); + } +} + +function getActiveArticleId() { + return active_post_id; +} + +function postMouseIn(id) { + post_under_pointer = id; +} + +function postMouseOut(id) { + post_under_pointer = false; +} + +function headlines_scroll_handler(e) { + try { + var hsp = $("headlines-spacer"); + + if (!_infscroll_disable) { + if (hsp && (e.scrollTop + e.offsetHeight > hsp.offsetTop) || + e.scrollTop + e.offsetHeight > e.scrollHeight - 100) { + + if (hsp) + hsp.innerHTML = " " + + __("Loading, please wait..."); + + loadMoreHeadlines(); + return; + + } + } else { + if (hsp) hsp.innerHTML = ""; + } + + if (getInitParam("cdm_auto_catchup") == 1) { + + $$("#headlines-frame > div[id*=RROW][class*=Unread]").each( + function(child) { + if ($("headlines-frame").scrollTop > + (child.offsetTop + child.offsetHeight/2)) { + + var id = child.id.replace("RROW-", ""); + + if (catchup_id_batch.indexOf(id) == -1) + catchup_id_batch.push(id); + + //console.log("auto_catchup_batch: " + catchup_id_batch.toString()); + } + }); + + if (catchup_id_batch.length > 0) { + window.clearTimeout(catchup_timeout_id); + + if (!_infscroll_request_sent) { + catchup_timeout_id = window.setTimeout('catchupBatchedArticles()', + 2000); + } + } + } + + } catch (e) { + console.warn("headlines_scroll_handler: " + e); + } +} + +function catchupBatchedArticles() { + try { + if (catchup_id_batch.length > 0 && !_infscroll_request_sent) { + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + param_escape(catchup_id_batch.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + + catchup_id_batch.each(function(id) { + var elem = $("RROW-" + id); + if (elem) elem.removeClassName("Unread"); + }); + + catchup_id_batch = []; + } }); + } + + } catch (e) { + exception_error("catchupBatchedArticles", e); + } +} + +function catchupRelativeToArticle(below, id) { + + try { + + if (!id) id = getActiveArticleId(); + + if (!id) { + alert(__("No article is selected.")); + return; + } + + var visible_ids = getVisibleArticleIds(); + + var ids_to_mark = new Array(); + + if (!below) { + for (var i = 0; i < visible_ids.length; i++) { + if (visible_ids[i] != id) { + var e = $("RROW-" + visible_ids[i]); + + if (e && e.hasClassName("Unread")) { + ids_to_mark.push(visible_ids[i]); + } + } else { + break; + } + } + } else { + for (var i = visible_ids.length-1; i >= 0; i--) { + if (visible_ids[i] != id) { + var e = $("RROW-" + visible_ids[i]); + + if (e && e.hasClassName("Unread")) { + ids_to_mark.push(visible_ids[i]); + } + } else { + break; + } + } + } + + if (ids_to_mark.length == 0) { + alert(__("No articles found to mark")); + } else { + var msg = __("Mark %d article(s) as read?").replace("%d", ids_to_mark.length); + + if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg)) { + + for (var i = 0; i < ids_to_mark.length; i++) { + var e = $("RROW-" + ids_to_mark[i]); + e.removeClassName("Unread"); + } + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0" + "&ids=" + param_escape(ids_to_mark.toString()); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + } + } + + } catch (e) { + exception_error("catchupRelativeToArticle", e); + } +} + +function cdmExpandArticle(id) { + try { + + hideAuxDlg(); + + var elem = $("CICD-" + active_post_id); + + var upd_img_pic = $("FUPDPIC-" + id); + + if (upd_img_pic && (upd_img_pic.src.match("updated.png") || + upd_img_pic.src.match("fresh_sign.png"))) { + + upd_img_pic.src = "images/blank_icon.gif"; + } + + if (id == active_post_id && Element.visible(elem)) + return true; + + selectArticles("none"); + + var old_offset = $("RROW-" + id).offsetTop; + + if (active_post_id && elem && !getInitParam("cdm_expanded")) { + Element.hide(elem); + Element.show("CEXC-" + active_post_id); + } + + active_post_id = id; + + elem = $("CICD-" + id); + + if (!Element.visible(elem)) { + Element.show(elem); + Element.hide("CEXC-" + id); + + if ($("CWRAP-" + id).innerHTML == "") { + + $("FUPDPIC-" + id).src = "images/indicator_tiny.gif"; + + $("CWRAP-" + id).innerHTML = "
    " + + __("Loading, please wait...") + "
    "; + + var query = "?op=rpc&subop=cdmGetArticle&id=" + param_escape(id); + + var neighbor_ids = getRelativePostIds(id); + + /* only request uncached articles */ + var cids_to_request = []; + + for (var i = 0; i < neighbor_ids.length; i++) { + if (cids_requested.indexOf(neighbor_ids[i]) == -1) + if ($("CWRAP-" + neighbor_ids[i]).innerHTML == "") { + cids_to_request.push(neighbor_ids[i]); + cids_requested.push(neighbor_ids[i]); + } + } + + console.log("additional ids: " + cids_to_request.toString()); + + query = query + "&cids=" + cids_to_request.toString(); + + console.log(query); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + + $("FUPDPIC-" + id).src = 'images/blank_icon.gif'; + + handle_rpc_json(transport); + + var reply = JSON.parse(transport.responseText); + + reply.each(function(article) { + $("CWRAP-" + article['id']).innerHTML = article['content']; + cids_requested.remove(article['id']); + }); + }}); + + } + } + + var new_offset = $("RROW-" + id).offsetTop; + + $("headlines-frame").scrollTop += (new_offset-old_offset); + + if ($("RROW-" + id).offsetTop != old_offset) + $("headlines-frame").scrollTop = new_offset; + + toggleUnread(id, 0, true); + toggleSelected(id); + + } catch (e) { + exception_error("cdmExpandArticle", e); + } + + return false; +} + +function fixHeadlinesOrder(ids) { + try { + for (var i = 0; i < ids.length; i++) { + var e = $("RROW-" + ids[i]); + + if (e) { + if (i % 2 == 0) { + e.removeClassName("even"); + e.addClassName("odd"); + } else { + e.removeClassName("odd"); + e.addClassName("even"); + } + } + } + } catch (e) { + exception_error("fixHeadlinesOrder", e); + } +} + +function getArticleUnderPointer() { + return post_under_pointer; +} + +function zoomToArticle(event, id) { + try { + var cached_article = cache_get("article: " + id); + + if (dijit.byId("ATAB-" + id)) + if (!event || !event.shiftKey) + return dijit.byId("content-tabs").selectChild(dijit.byId("ATAB-" + id)); + + if (dijit.byId("ATSTRTIP-" + id)) + dijit.byId("ATSTRTIP-" + id).destroyRecursive(); + + if (cached_article) { + //closeArticlePanel(); + + var article_pane = new dijit.layout.ContentPane({ + title: __("Loading...") , content: cached_article, + style: 'padding : 0px;', + id: 'ATAB-' + id, + closable: true }); + + dijit.byId("content-tabs").addChild(article_pane); + + if (!event || !event.shiftKey) + dijit.byId("content-tabs").selectChild(article_pane); + + if ($("PTITLE-" + id)) + article_pane.attr('title', $("PTITLE-" + id).innerHTML); + + } else { + + var query = "?op=rpc&subop=getArticles&ids=" + param_escape(id); + + notify_progress("Loading, please wait...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + + var reply = JSON.parse(transport.responseText); + + if (reply) { + //closeArticlePanel(); + + var content = reply[0]['content']; + + var article_pane = new dijit.layout.ContentPane({ + title: "article-" + id , content: content, + style: 'padding : 0px;', + id: 'ATAB-' + id, + closable: true }); + + dijit.byId("content-tabs").addChild(article_pane); + + if (!event || !event.shiftKey) + dijit.byId("content-tabs").selectChild(article_pane); + + if ($("PTITLE-" + id)) + article_pane.attr('title', $("PTITLE-" + id).innerHTML); + } + + } }); + } + + } catch (e) { + exception_error("zoomToArticle", e); + } +} + +function scrollArticle(offset) { + try { + if (!isCdmMode()) { + var ci = $("content-insert"); + if (ci) { + ci.scrollTop += offset; + } + } else { + var hi = $("headlines-frame"); + if (hi) { + hi.scrollTop += offset; + } + + } + } catch (e) { + exception_error("scrollArticle", e); + } +} + +function show_labels_in_headlines(transport) { + try { + var data = JSON.parse(transport.responseText); + + if (data) { + data['info-for-headlines'].each(function(elem) { + var ctr = $("HLLCTR-" + elem.id); + + if (ctr) ctr.innerHTML = elem.labels; + }); + + cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); + + } + } catch (e) { + exception_error("show_labels_in_headlines", e); + } +} + +/* function toggleHeadlineActions() { + try { + var e = $("headlineActionsBody"); + var p = $("headlineActionsDrop"); + + if (!Element.visible(e)) { + Element.show(e); + } else { + Element.hide(e); + } + + e.scrollTop = 0; + e.style.left = (p.offsetLeft + 1) + "px"; + e.style.top = (p.offsetTop + p.offsetHeight + 2) + "px"; + + } catch (e) { + exception_error("toggleHeadlineActions", e); + } +} */ + +/* function publishWithNote(id, def_note) { + try { + if (!def_note) def_note = ''; + + var note = prompt(__("Please enter a note for this article:"), def_note); + + if (note != undefined) { + togglePub(id, false, false, note); + } + + } catch (e) { + exception_error("publishWithNote", e); + } +} */ + +function emailArticle(id) { + try { + if (!id) { + var ids = getSelectedArticleIds2(); + + if (ids.length == 0) { + alert(__("No articles are selected.")); + return; + } + + id = ids.toString(); + } + + if (dijit.byId("emailArticleDlg")) + dijit.byId("emailArticleDlg").destroyRecursive(); + + var query = "backend.php?op=dlg&id=emailArticle¶m=" + param_escape(id); + + dialog = new dijit.Dialog({ + id: "emailArticleDlg", + title: __("Forward article by email"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + + new Ajax.Request("backend.php", { + parameters: dojo.objectToQuery(this.attr('value')), + onComplete: function(transport) { + + var reply = JSON.parse(transport.responseText); + + var error = reply['error']; + + if (error) { + alert(__('Error sending email:') + ' ' + error); + } else { + notify_info('Your message has been sent.'); + dialog.hide(); + } + + } }); + } + }, + href: query}); + + var tmph = dojo.connect(dialog, 'onLoad', function() { + dojo.disconnect(tmph); + + new Ajax.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices', + "backend.php?op=rpc&subop=completeEmails", + { tokens: '', paramName: "search" }); + }); + + dialog.show(); + + /* displayDlg('emailArticle', id, + function () { + document.forms['article_email_form'].destination.focus(); + + new Ajax.Autocompleter('destination', 'destination_choices', + "backend.php?op=rpc&subop=completeEmails", + { tokens: '', paramName: "search" }); + + }); */ + + } catch (e) { + exception_error("emailArticle", e); + } +} + +function dismissArticle(id) { + try { + var elem = $("RROW-" + id); + + toggleUnread(id, 0, true); + + new Effect.Fade(elem, {duration : 0.5}); + + active_post_id = false; + + } catch (e) { + exception_error("dismissArticle", e); + } +} + +function dismissSelectedArticles() { + try { + + var ids = getVisibleArticleIds(); + var tmp = []; + var sel = []; + + for (var i = 0; i < ids.length; i++) { + var elem = $("RROW-" + ids[i]); + + if (elem.className && elem.hasClassName("Selected") && + ids[i] != active_post_id) { + new Effect.Fade(elem, {duration : 0.5}); + sel.push(ids[i]); + } else { + tmp.push(ids[i]); + } + } + + if (sel.length > 0) + selectionToggleUnread(false); + + fixHeadlinesOrder(tmp); + + } catch (e) { + exception_error("dismissSelectedArticles", e); + } +} + +function dismissReadArticles() { + try { + + var ids = getVisibleArticleIds(); + var tmp = []; + + for (var i = 0; i < ids.length; i++) { + var elem = $("RROW-" + ids[i]); + + if (elem.className && !elem.hasClassName("Unread") && + !elem.hasClassName("Selected")) { + + new Effect.Fade(elem, {duration : 0.5}); + } else { + tmp.push(ids[i]); + } + } + + fixHeadlinesOrder(tmp); + + } catch (e) { + exception_error("dismissSelectedArticles", e); + } +} + +function getVisibleArticleIds() { + var ids = []; + + try { + + getLoadedArticleIds().each(function(id) { + var elem = $("RROW-" + id); + if (elem && Element.visible(elem)) + ids.push(id); + }); + + } catch (e) { + exception_error("getVisibleArticleIds", e); + } + + return ids; +} + +function cdmClicked(event, id) { + try { + //var shift_key = event.shiftKey; + + hideAuxDlg(); + + if (!event.ctrlKey) { + + if (!getInitParam("cdm_expanded")) { + return cdmExpandArticle(id); + } else { + + selectArticles("none"); + toggleSelected(id); + + var elem = $("RROW-" + id); + + if (elem) + elem.removeClassName("Unread"); + + var upd_img_pic = $("FUPDPIC-" + id); + + if (upd_img_pic && (upd_img_pic.src.match("updated.png") || + upd_img_pic.src.match("fresh_sign.png"))) { + + upd_img_pic.src = "images/blank_icon.gif"; + } + + active_post_id = id; + + var query = "?op=rpc&subop=catchupSelected" + + "&cmode=0&ids=" + param_escape(id); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + handle_rpc_json(transport); + } }); + + return true; + } + + } else { + toggleSelected(id, true); + toggleUnread(id, 0, false); + zoomToArticle(event, id); + } + + } catch (e) { + exception_error("cdmClicked"); + } + + return false; +} + +function postClicked(event, id) { + try { + + if (!event.ctrlKey) { + return true; + } else { + postOpenInNewTab(event, id); + return false; + } + + } catch (e) { + exception_error("postClicked"); + } +} + +function hlOpenInNewTab(event, id) { + toggleUnread(id, 0, false); + zoomToArticle(event, id); +} + +function postOpenInNewTab(event, id) { + closeArticlePanel(id); + zoomToArticle(event, id); +} + +function hlClicked(event, id) { + try { + if (event.which == 2) { + view(id); + return true; + } else if (event.altKey) { + openArticleInNewWindow(id); + } else if (!event.ctrlKey) { + view(id); + return false; + } else { + toggleSelected(id); + toggleUnread(id, 0, false); + zoomToArticle(event, id); + return false; + } + + } catch (e) { + exception_error("hlClicked"); + } +} + +function getFirstVisibleHeadlineId() { + var rows = getVisibleArticleIds(); + return rows[0]; + +} + +function getLastVisibleHeadlineId() { + var rows = getVisibleArticleIds(); + return rows[rows.length-1]; +} + +function openArticleInNewWindow(id) { + toggleUnread(id, 0, false); + window.open("backend.php?op=la&id=" + id); +} + +function isCdmMode() { + return getInitParam("combined_display_mode"); +} + +function markHeadline(id) { + var row = $("RROW-" + id); + if (row) { + var check = $("RCHK-" + id); + + if (check) { + check.checked = true; + } + + row.addClassName("Selected"); + } +} + +function getRelativePostIds(id, limit) { + + var tmp = []; + + try { + + if (!limit) limit = 6; //3 + + var ids = getVisibleArticleIds(); + + for (var i = 0; i < ids.length; i++) { + if (ids[i] == id) { + for (var k = 1; k <= limit; k++) { + //if (i > k-1) tmp.push(ids[i-k]); + if (i < ids.length-k) tmp.push(ids[i+k]); + } + break; + } + } + + } catch (e) { + exception_error("getRelativePostIds", e); + } + + return tmp; +} + +function correctHeadlinesOffset(id) { + + try { + + var container = $("headlines-frame"); + var row = $("RROW-" + id); + + var viewport = container.offsetHeight; + + var rel_offset_top = row.offsetTop - container.scrollTop; + var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop; + + //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom); + //console.log("Vport: " + viewport); + + if (rel_offset_top <= 0 || rel_offset_top > viewport) { + container.scrollTop = row.offsetTop; + } else if (rel_offset_bottom > viewport) { + + /* doesn't properly work with Opera in some cases because + Opera fucks up element scrolling */ + + container.scrollTop = row.offsetTop + row.offsetHeight - viewport; + } + + } catch (e) { + exception_error("correctHeadlinesOffset", e); + } + +} + +function headlineActionsChange(elem) { + try { + eval(elem.value); + elem.attr('value', 'false'); + } catch (e) { + exception_error("headlineActionsChange", e); + } +} + +function closeArticlePanel() { + + var tabs = dijit.byId("content-tabs"); + var child = tabs.selectedChildWidget; + + if (child && tabs.getIndexOfChild(child) > 0) { + tabs.removeChild(child); + child.destroy(); + } else { + if (dijit.byId("content-insert")) + dijit.byId("headlines-wrap-inner").removeChild( + dijit.byId("content-insert")); + } +} + +function initHeadlinesMenu() { + try { + if (dijit.byId("headlinesMenu")) + dijit.byId("headlinesMenu").destroyRecursive(); + + var ids = []; + + if (!isCdmMode()) { + nodes = $$("#headlines-frame > div[id*=RROW]"); + } else { + nodes = $$("#headlines-frame span[id*=RTITLE]"); + } + + nodes.each(function(node) { + ids.push(node.id); + }); + + var menu = new dijit.Menu({ + id: "headlinesMenu", + targetNodeIds: ids, + }); + + var tmph = dojo.connect(menu, '_openMyself', function (event) { + var callerNode = event.target, match = null, tries = 0; + + while (match == null && callerNode && tries <= 3) { + match = callerNode.id.match("^[A-Z]+[-]([0-9]+)$"); + callerNode = callerNode.parentNode; + ++tries; + } + + if (match) this.callerRowId = parseInt(match[1]); + + }); + +/* if (!isCdmMode()) + menu.addChild(new dijit.MenuItem({ + label: __("View article"), + onClick: function(event) { + view(this.getParent().callerRowId); + }})); */ + + menu.addChild(new dijit.MenuItem({ + label: __("Open original article"), + onClick: function(event) { + openArticleInNewWindow(this.getParent().callerRowId); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("View in a tt-rss tab"), + onClick: function(event) { + hlOpenInNewTab(event, this.getParent().callerRowId); + }})); + + menu.addChild(new dijit.MenuSeparator()); + + menu.addChild(new dijit.MenuItem({ + label: __("Mark above as read"), + onClick: function(event) { + catchupRelativeToArticle(0, this.getParent().callerRowId); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Mark below as read"), + onClick: function(event) { + catchupRelativeToArticle(1, this.getParent().callerRowId); + }})); + + + var labels = dijit.byId("feedTree").model.getItemsInCategory(-2); + + if (labels) { + + menu.addChild(new dijit.MenuSeparator()); + + var labelAddMenu = new dijit.Menu({ownerMenu: menu}); + var labelDelMenu = new dijit.Menu({ownerMenu: menu}); + + labels.each(function(label) { + var id = label.id[0]; + var bare_id = id.substr(id.indexOf(":")+1); + var name = label.name[0]; + + bare_id = -11-bare_id; + + labelAddMenu.addChild(new dijit.MenuItem({ + label: name, + labelId: bare_id, + onClick: function(event) { + selectionAssignLabel(this.labelId, + [this.getParent().ownerMenu.callerRowId]); + }})); + + labelDelMenu.addChild(new dijit.MenuItem({ + label: name, + labelId: bare_id, + onClick: function(event) { + selectionRemoveLabel(this.labelId, + [this.getParent().ownerMenu.callerRowId]); + }})); + + }); + + menu.addChild(new dijit.PopupMenuItem({ + label: __("Assign label"), + popup: labelAddMenu, + })); + + menu.addChild(new dijit.PopupMenuItem({ + label: __("Remove label"), + popup: labelDelMenu, + })); + + } + + menu.startup(); + + } catch (e) { + exception_error("initHeadlinesMenu", e); + } +} + +function tweetArticle(id) { + try { + var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id); + + console.log(query); + + var d = new Date(); + var ts = d.getTime(); + + var w = window.open('backend.php?op=loading', 'ttrss_tweet', + "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0"); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + var ti = JSON.parse(transport.responseText); + + var share_url = "http://twitter.com/share?_=" + ts + + "&text=" + param_escape(ti.title) + + "&url=" + param_escape(ti.link); + + w.location.href = share_url; + + } }); + + + } catch (e) { + exception_error("tweetArticle", e); + } +} + +function editArticleNote(id) { + try { + + var query = "backend.php?op=dlg&id=editArticleNote¶m=" + param_escape(id); + + if (dijit.byId("editNoteDlg")) + dijit.byId("editNoteDlg").destroyRecursive(); + + dialog = new dijit.Dialog({ + id: "editNoteDlg", + title: __("Edit article note"), + style: "width: 600px", + execute: function() { + if (this.validate()) { + var query = dojo.objectToQuery(this.attr('value')); + + notify_progress("Saving article note...", true); + + new Ajax.Request("backend.php", { + parameters: query, + onComplete: function(transport) { + notify(''); + dialog.hide(); + + var reply = JSON.parse(transport.responseText); + + cache_delete("article:" + id); + + var elem = $("POSTNOTE-" + id); + + if (elem) { + Element.hide(elem); + elem.innerHTML = reply.note; + + if (reply.raw_length != 0) + new Effect.Appear(elem); + } + + }}); + } + }, + href: query, + }); + + dialog.show(); + + } catch (e) { + exception_error("editArticleNote", e); + } +} + +function player(elem) { + var aid = elem.getAttribute("audio-id"); + var status = elem.getAttribute("status"); + + var audio = $(aid); + + if (audio) { + if (status == 0) { + audio.play(); + status = 1; + elem.innerHTML = __("Playing..."); + elem.title = __("Click to pause"); + elem.addClassName("playing"); + } else { + audio.pause(); + status = 0; + elem.innerHTML = __("Play"); + elem.title = __("Click to play"); + elem.removeClassName("playing"); + } + + elem.setAttribute("status", status); + } else { + alert("Your browser doesn't seem to support HTML5 audio."); + } +} + +function cache_set(id, obj) { + //console.log("cache_set: " + id); + if (has_storage) + try { + sessionStorage[id] = obj; + } catch (e) { + sessionStorage.clear(); + } +} + +function cache_get(id) { + if (has_storage) + return sessionStorage[id]; +} + +function cache_clear() { + if (has_storage) + sessionStorage.clear(); +} + +function cache_delete(id) { + if (has_storage) + sessionStorage.removeItem(id); +} + +function cache_headlines(feed, is_cat, toolbar_obj, content_obj) { + if (toolbar_obj && content_obj) { + cache_set("feed:" + feed + ":" + is_cat, + JSON.stringify({toolbar: toolbar_obj, content: content_obj})); + } else { + try { + obj = cache_get("feed:" + feed + ":" + is_cat); + + if (obj) { + obj = JSON.parse(obj); + + if (toolbar_obj) obj.toolbar = toolbar_obj; + if (content_obj) obj.content = content_obj; + + cache_set("feed:" + feed + ":" + is_cat, JSON.stringify(obj)); + } + + } catch (e) { + console.warn("cache_headlines failed: " + e); + } + } +} + +function render_local_headlines(feed, is_cat, obj) { + try { + + dijit.byId("headlines-toolbar").attr('content', + obj.toolbar); + + dijit.byId("headlines-frame").attr('content', + obj.content); + + dojo.parser.parse('headlines-toolbar'); + + $("headlines-frame").scrollTop = 0; + selectArticles('none'); + setActiveFeedId(feed, is_cat); + initHeadlinesMenu(); + + precache_headlines(); + + } catch (e) { + exception_error("render_local_headlines", e); + } +} + +function precache_headlines_idle() { + try { + if (!feed_precache_timeout_id) { + var feeds = dijit.byId("feedTree").getVisibleUnreadFeeds(); + var uncached = []; + + feeds.each(function(item) { + if (parseInt(item[0]) > 0 && !cache_get("feed:" + item[0] + ":" + item[1])) + uncached.push(item); + }); + + if (uncached.length > 0) { + var rf = uncached[Math.floor(Math.random()*uncached.length)]; + viewfeed(rf[0], '', rf[1], 0, true); + } + } + precache_idle_timeout_id = setTimeout("precache_headlines_idle()", 1000*30); + + } catch (e) { + exception_error("precache_headlines_idle", e); + } +} + +function precache_headlines() { + try { + + if (!feed_precache_timeout_id) { + feed_precache_timeout_id = window.setTimeout(function() { + var nuf = getNextUnreadFeed(getActiveFeedId(), activeFeedIsCat()); + var nf = dijit.byId("feedTree").getNextFeed(getActiveFeedId(), activeFeedIsCat()); + + if (nuf && !cache_get("feed:" + nuf + ":" + activeFeedIsCat())) + viewfeed(nuf, '', activeFeedIsCat(), 0, true); + + if (nf != nuf && nf && !cache_get("feed:" + nf[0] + ":" + nf[1])) + viewfeed(nf[0], '', nf[1], 0, true); + + window.setTimeout(function() { + feed_precache_timeout_id = false; + }, 3000); + }, 1000); + } + + } catch (e) { + exception_error("precache_headlines", e); + } +} + +function shareArticle(id) { + try { + if (dijit.byId("shareArticleDlg")) + dijit.byId("shareArticleDlg").destroyRecursive(); + + var query = "backend.php?op=dlg&id=shareArticle¶m=" + param_escape(id); + + dialog = new dijit.Dialog({ + id: "shareArticleDlg", + title: __("Share article by URL"), + style: "width: 600px", + href: query}); + + dialog.show(); + + } catch (e) { + exception_error("emailArticle", e); + } +} + + diff --git a/localized_js.php b/localized_js.php index e51e97317..1cf38e573 100644 --- a/localized_js.php +++ b/localized_js.php @@ -1,4 +1,6 @@ diff --git a/login_form.php b/login_form.php deleted file mode 100644 index 04ca3e6d0..000000000 --- a/login_form.php +++ /dev/null @@ -1,199 +0,0 @@ - - - Tiny Tiny RSS : Login - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - -
    - -
    - -
    - - - - - - - - - - - - - - - - - - - - -
    - -
    - -
    - - - - - - - - -
    - -
    - -
    - -
    -
    - Tiny Tiny RSS - - v - - © 2005– Andrew Dolgov -
    - -
    - - diff --git a/messages.pot b/messages.pot deleted file mode 100644 index 5237e0ca3..000000000 --- a/messages.pot +++ /dev/null @@ -1,2751 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-11-23 10:40+0400\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: backend.php:83 -msgid "Use default" -msgstr "" - -#: backend.php:84 -msgid "Never purge" -msgstr "" - -#: backend.php:85 -msgid "1 week old" -msgstr "" - -#: backend.php:86 -msgid "2 weeks old" -msgstr "" - -#: backend.php:87 -msgid "1 month old" -msgstr "" - -#: backend.php:88 -msgid "2 months old" -msgstr "" - -#: backend.php:89 -msgid "3 months old" -msgstr "" - -#: backend.php:92 -msgid "Default interval" -msgstr "" - -#: backend.php:93 backend.php:103 -msgid "Disable updates" -msgstr "" - -#: backend.php:94 backend.php:104 -msgid "Each 15 minutes" -msgstr "" - -#: backend.php:95 backend.php:105 -msgid "Each 30 minutes" -msgstr "" - -#: backend.php:96 backend.php:106 -msgid "Hourly" -msgstr "" - -#: backend.php:97 backend.php:107 -msgid "Each 4 hours" -msgstr "" - -#: backend.php:98 backend.php:108 -msgid "Each 12 hours" -msgstr "" - -#: backend.php:99 backend.php:109 -msgid "Daily" -msgstr "" - -#: backend.php:100 backend.php:110 -msgid "Weekly" -msgstr "" - -#: backend.php:113 tt-rss.php:140 modules/pref-prefs.php:350 -msgid "Default" -msgstr "" - -#: backend.php:114 -msgid "Magpie" -msgstr "" - -#: backend.php:115 -msgid "SimplePie" -msgstr "" - -#: backend.php:116 -msgid "Twitter OAuth" -msgstr "" - -#: backend.php:125 modules/pref-users.php:131 -msgid "User" -msgstr "" - -#: backend.php:126 -msgid "Power User" -msgstr "" - -#: backend.php:127 -msgid "Administrator" -msgstr "" - -#: backend.php:179 prefs.php:93 modules/pref-feeds.php:45 -#: modules/pref-feeds.php:1278 modules/pref-feeds.php:1348 -msgid "Feeds" -msgstr "" - -#: backend.php:207 -msgid "Article not found." -msgstr "" - -#: backend.php:297 functions.php:5090 -msgid "Feed not found." -msgstr "" - -#: backend.php:457 digest.php:61 prefs.php:66 tt-rss.php:62 tt-rss.php:106 -#: tt-rss.php:191 modules/pref-feeds.php:1401 modules/pref-filters.php:550 -#: modules/pref-labels.php:293 viewfeed.js:1108 viewfeed.js:1278 -msgid "Loading, please wait..." -msgstr "" - -#: db-updater.php:16 -msgid "Your access level is insufficient to run this script." -msgstr "" - -#: db-updater.php:41 -msgid "Database Updater" -msgstr "" - -#: db-updater.php:82 -msgid "Could not update database" -msgstr "" - -#: db-updater.php:85 -msgid "Could not find necessary schema file, need version:" -msgstr "" - -#: db-updater.php:86 -msgid ", found: " -msgstr "" - -#: db-updater.php:89 -msgid "Tiny Tiny RSS database is up to date." -msgstr "" - -#: db-updater.php:91 db-updater.php:158 db-updater.php:171 register.php:189 -#: register.php:234 register.php:247 register.php:262 register.php:280 -#: register.php:365 register.php:375 register.php:387 twitter.php:108 -#: twitter.php:120 modules/pref-feeds.php:1096 -msgid "Return to Tiny Tiny RSS" -msgstr "" - -#: db-updater.php:97 -msgid "Please backup your database before proceeding." -msgstr "" - -#: db-updater.php:99 -#, php-format -msgid "" -"Your Tiny Tiny RSS database needs update to the latest version (%d to " -"%d)." -msgstr "" - -#: db-updater.php:113 -msgid "Perform updates" -msgstr "" - -#: db-updater.php:118 -msgid "Performing updates..." -msgstr "" - -#: db-updater.php:124 -#, php-format -msgid "Updating to version %d..." -msgstr "" - -#: db-updater.php:137 -msgid "Checking version... " -msgstr "" - -#: db-updater.php:143 -msgid "OK!" -msgstr "" - -#: db-updater.php:145 -msgid "ERROR!" -msgstr "" - -#: db-updater.php:153 -#, php-format -msgid "" -"Finished. Performed %d update(s) up to schema\n" -"\t\t\tversion %d." -msgstr "" - -#: db-updater.php:163 -msgid "Your database schema is from a newer version of Tiny Tiny RSS." -msgstr "" - -#: db-updater.php:165 -#, php-format -msgid "Found schema version: %d, required: %d." -msgstr "" - -#: db-updater.php:167 -msgid "" -"Schema upgrade impossible. Please update Tiny Tiny RSS files to the newer " -"version and continue." -msgstr "" - -#: digest.php:55 -msgid "" -"Your browser doesn't support Javascript, which is required\n" -"\t\t\tfor this application to function properly. Please check your\n" -"\t\t\tbrowser settings." -msgstr "" - -#: digest.php:69 tt-rss.php:72 -msgid "Hello," -msgstr "" - -#: digest.php:72 tt-rss.php:82 mobile/functions.php:59 -#: mobile/functions.php:234 -msgid "Logout" -msgstr "" - -#: errors.php:6 -msgid "" -"This program requires XmlHttpRequest to function properly. Your browser " -"doesn't seem to support it." -msgstr "" - -#: errors.php:9 -msgid "" -"This program requires cookies to function properly. Your browser doesn't " -"seem to support them." -msgstr "" - -#: errors.php:12 -msgid "Backend sanity check failed" -msgstr "" - -#: errors.php:14 -msgid "Frontend sanity check failed." -msgstr "" - -#: errors.php:16 -msgid "" -"Incorrect database schema version. <a href='db-updater.php'>Please " -"update</a>." -msgstr "" - -#: errors.php:18 -msgid "Request not authorized." -msgstr "" - -#: errors.php:20 -msgid "No operation to perform." -msgstr "" - -#: errors.php:22 -msgid "" -"Could not display feed: query failed. Please check label match syntax or " -"local configuration." -msgstr "" - -#: errors.php:24 -msgid "Denied. Your access level is insufficient to access this page." -msgstr "" - -#: errors.php:26 -msgid "Configuration check failed" -msgstr "" - -#: errors.php:28 -msgid "" -"Your version of MySQL is not currently supported. Please see\n" -"\t\tofficial site for more information." -msgstr "" - -#: errors.php:33 -msgid "SQL escaping test failed, check your database and PHP configuration" -msgstr "" - -#: functions.php:2073 -msgid "Session failed to validate (incorrect IP)" -msgstr "" - -#: functions.php:2147 -msgid "Incorrect username or password" -msgstr "" - -#: functions.php:3138 modules/popup-dialog.php:394 -msgid "All feeds" -msgstr "" - -#: functions.php:3170 functions.php:3213 functions.php:4568 functions.php:4577 -#: modules/pref-feeds.php:96 -msgid "Uncategorized" -msgstr "" - -#: functions.php:3203 functions.php:3907 mobile/functions.php:168 -msgid "Special" -msgstr "" - -#: functions.php:3205 functions.php:3909 prefs.php:99 -#: modules/pref-labels.php:89 help/4.php:12 mobile/functions.php:195 -msgid "Labels" -msgstr "" - -#: functions.php:3251 help/3.php:71 -msgid "Starred articles" -msgstr "" - -#: functions.php:3253 help/3.php:72 -msgid "Published articles" -msgstr "" - -#: functions.php:3255 help/3.php:70 -msgid "Fresh articles" -msgstr "" - -#: functions.php:3257 help/3.php:69 -msgid "All articles" -msgstr "" - -#: functions.php:3259 -msgid "Archived articles" -msgstr "" - -#: functions.php:4339 -msgid "Select:" -msgstr "" - -#: functions.php:4340 modules/pref-feeds.php:1342 modules/pref-filters.php:525 -#: modules/pref-instances.php:137 modules/pref-labels.php:272 -#: modules/pref-users.php:380 -msgid "All" -msgstr "" - -#: functions.php:4341 functions.php:4353 tt-rss.php:132 -msgid "Unread" -msgstr "" - -#: functions.php:4342 -msgid "Invert" -msgstr "" - -#: functions.php:4343 modules/pref-feeds.php:1344 modules/pref-filters.php:527 -#: modules/pref-instances.php:139 modules/pref-labels.php:274 -#: modules/pref-users.php:382 -msgid "None" -msgstr "" - -#: functions.php:4349 tt-rss.php:158 -msgid "Actions..." -msgstr "" - -#: functions.php:4351 -msgid "Selection toggle:" -msgstr "" - -#: functions.php:4354 tt-rss.php:130 -msgid "Starred" -msgstr "" - -#: functions.php:4355 tt-rss.php:131 -msgid "Published" -msgstr "" - -#: functions.php:4357 -msgid "Selection:" -msgstr "" - -#: functions.php:4359 functions.php:4385 localized_schema.php:10 -#: tt-rss.php:152 tt-rss.php:167 digest.js:624 FeedTree.js:125 FeedTree.js:151 -msgid "Mark as read" -msgstr "" - -#: functions.php:4362 -msgid "Archive" -msgstr "" - -#: functions.php:4364 -msgid "Move back" -msgstr "" - -#: functions.php:4365 -msgid "Delete" -msgstr "" - -#: functions.php:4369 functions.php:4946 functions.php:5590 -msgid "Forward by email" -msgstr "" - -#: functions.php:4383 PrefFilterTree.js:29 -msgid "Feed:" -msgstr "" - -#: functions.php:4387 modules/popup-dialog.php:881 -msgid "View as RSS" -msgstr "" - -#: functions.php:4397 functions.php:5009 -msgid "Visit the website" -msgstr "" - -#: functions.php:4430 -msgid "View as RSS feed" -msgstr "" - -#: functions.php:4757 viewfeed.js:2085 -msgid "Click to play" -msgstr "" - -#: functions.php:4758 viewfeed.js:2084 -msgid "Play" -msgstr "" - -#: functions.php:4884 -msgid " - " -msgstr "" - -#: functions.php:4913 functions.php:6537 modules/backend-rpc.php:381 -msgid "no tags" -msgstr "" - -#: functions.php:4923 functions.php:5550 -msgid "Edit tags for this article" -msgstr "" - -#: functions.php:4933 functions.php:5577 -msgid "Open article in new tab" -msgstr "" - -#: functions.php:4940 functions.php:5584 viewfeed.js:2027 -msgid "Edit article note" -msgstr "" - -#: functions.php:4953 functions.php:5597 -msgid "Share on Twitter" -msgstr "" - -#: functions.php:4959 -msgid "Share by URL" -msgstr "" - -#: functions.php:4964 digest.js:267 -msgid "Close this panel" -msgstr "" - -#: functions.php:4981 functions.php:5490 -msgid "Originally from:" -msgstr "" - -#: functions.php:4994 functions.php:5503 modules/popup-dialog.php:251 -#: modules/pref-feeds.php:319 -msgid "Feed URL" -msgstr "" - -#: functions.php:5033 modules/help.php:21 modules/popup-dialog.php:53 -#: modules/popup-dialog.php:172 modules/popup-dialog.php:196 -#: modules/popup-dialog.php:234 modules/popup-dialog.php:617 -#: modules/popup-dialog.php:679 modules/popup-dialog.php:736 -#: modules/popup-dialog.php:768 modules/popup-dialog.php:904 -#: modules/popup-dialog.php:934 modules/popup-dialog.php:1021 -#: modules/popup-dialog.php:1112 modules/pref-feeds.php:1269 -#: modules/pref-filters.php:405 modules/pref-filters.php:482 -#: modules/pref-users.php:99 -msgid "Close this window" -msgstr "" - -#: functions.php:5311 functions.php:5393 -msgid "mark as read" -msgstr "" - -#: functions.php:5603 -msgid "Dismiss article" -msgstr "" - -#: functions.php:5624 -msgid "No unread articles found to display." -msgstr "" - -#: functions.php:5627 -msgid "No updated articles found to display." -msgstr "" - -#: functions.php:5630 -msgid "No starred articles found to display." -msgstr "" - -#: functions.php:5634 -msgid "" -"No articles found to display. You can assign articles to labels manually " -"(see the Actions menu above) or use a filter." -msgstr "" - -#: functions.php:5636 -msgid "No articles found to display." -msgstr "" - -#: functions.php:5651 functions.php:6985 -#, php-format -msgid "Feeds last updated at %s" -msgstr "" - -#: functions.php:5661 functions.php:6995 -msgid "Some feeds have update errors (click for details)" -msgstr "" - -#: functions.php:6477 tt-rss.php:173 -msgid "Create label..." -msgstr "" - -#: functions.php:6491 -msgid "Remove:" -msgstr "" - -#: functions.php:6495 -msgid "Assign:" -msgstr "" - -#: functions.php:6562 -msgid "(edit note)" -msgstr "" - -#: functions.php:6975 -msgid "No feed selected." -msgstr "" - -#: functions.php:7159 -msgid "unknown type" -msgstr "" - -#: functions.php:7199 -msgid "Attachment:" -msgstr "" - -#: functions.php:7201 -msgid "Attachments:" -msgstr "" - -#: functions.php:7632 login_form.php:151 modules/backend-rpc.php:66 -#: modules/popup-dialog.php:109 -msgid "Default profile" -msgstr "" - -#: localized_schema.php:3 tt-rss.php:142 modules/popup-dialog.php:378 -msgid "Title" -msgstr "" - -#: localized_schema.php:4 -msgid "Title or Content" -msgstr "" - -#: localized_schema.php:5 -msgid "Link" -msgstr "" - -#: localized_schema.php:6 modules/popup-dialog.php:379 -msgid "Content" -msgstr "" - -#: localized_schema.php:7 -msgid "Article Date" -msgstr "" - -#: localized_schema.php:9 -msgid "Delete article" -msgstr "" - -#: localized_schema.php:11 -msgid "Set starred" -msgstr "" - -#: localized_schema.php:12 digest.js:262 digest.js:728 viewfeed.js:470 -msgid "Publish article" -msgstr "" - -#: localized_schema.php:13 -msgid "Assign tags" -msgstr "" - -#: localized_schema.php:14 viewfeed.js:1968 -msgid "Assign label" -msgstr "" - -#: localized_schema.php:16 -msgid "" -"This option is useful when you are reading several planet-type aggregators " -"with partially colliding userbase. When disabled, it forces same posts from " -"different feeds to appear only once." -msgstr "" - -#: localized_schema.php:17 -msgid "" -"Display expanded list of feed articles, instead of separate displays for " -"headlines and article content" -msgstr "" - -#: localized_schema.php:18 -msgid "" -"When \"Mark as read\" button is clicked in toolbar, automatically open next " -"feed with unread articles." -msgstr "" - -#: localized_schema.php:19 -msgid "" -"This option enables sending daily digest of new (and unread) headlines on " -"your configured e-mail address" -msgstr "" - -#: localized_schema.php:20 -msgid "" -"This option enables marking articles as read automatically while you scroll " -"article list." -msgstr "" - -#: localized_schema.php:21 -msgid "Strip all but most common HTML tags when reading articles." -msgstr "" - -#: localized_schema.php:22 -msgid "" -"When auto-detecting tags in articles these tags will not be applied (comma-" -"separated list)." -msgstr "" - -#: localized_schema.php:23 -msgid "" -"When this option is enabled, headlines in Special feeds and Labels are " -"grouped by feeds" -msgstr "" - -#: localized_schema.php:24 -msgid "Use feed-specified date to sort headlines instead of local import date." -msgstr "" - -#: localized_schema.php:25 -msgid "Customize CSS stylesheet to your liking" -msgstr "" - -#: localized_schema.php:26 -msgid "Click to register your SSL client certificate with tt-rss" -msgstr "" - -#: localized_schema.php:27 -msgid "Purge old posts after this number of days (0 - disables)" -msgstr "" - -#: localized_schema.php:28 -msgid "Default interval between feed updates" -msgstr "" - -#: localized_schema.php:29 -msgid "Amount of articles to display at once" -msgstr "" - -#: localized_schema.php:30 -msgid "Allow duplicate posts" -msgstr "" - -#: localized_schema.php:31 -msgid "Enable feed categories" -msgstr "" - -#: localized_schema.php:32 -msgid "Show content preview in headlines list" -msgstr "" - -#: localized_schema.php:33 -msgid "Short date format" -msgstr "" - -#: localized_schema.php:34 -msgid "Long date format" -msgstr "" - -#: localized_schema.php:35 -msgid "Combined feed display" -msgstr "" - -#: localized_schema.php:36 -msgid "Hide feeds with no unread messages" -msgstr "" - -#: localized_schema.php:37 -msgid "On catchup show next feed" -msgstr "" - -#: localized_schema.php:38 -msgid "Sort feeds by unread articles count" -msgstr "" - -#: localized_schema.php:39 -msgid "Reverse headline order (oldest first)" -msgstr "" - -#: localized_schema.php:40 -msgid "Enable e-mail digest" -msgstr "" - -#: localized_schema.php:41 -msgid "Confirm marking feed as read" -msgstr "" - -#: localized_schema.php:42 -msgid "Automatically mark articles as read" -msgstr "" - -#: localized_schema.php:43 -msgid "Strip unsafe tags from articles" -msgstr "" - -#: localized_schema.php:44 -msgid "Blacklisted tags" -msgstr "" - -#: localized_schema.php:45 -msgid "Maximum age of fresh articles (in hours)" -msgstr "" - -#: localized_schema.php:46 -msgid "Mark articles in e-mail digest as read" -msgstr "" - -#: localized_schema.php:47 -msgid "Automatically expand articles in combined mode" -msgstr "" - -#: localized_schema.php:48 -msgid "Purge unread articles" -msgstr "" - -#: localized_schema.php:49 -msgid "Show special feeds when hiding read feeds" -msgstr "" - -#: localized_schema.php:50 -msgid "Group headlines in virtual feeds" -msgstr "" - -#: localized_schema.php:51 -msgid "Do not show images in articles" -msgstr "" - -#: localized_schema.php:52 -msgid "Enable external API" -msgstr "" - -#: localized_schema.php:53 -msgid "User timezone" -msgstr "" - -#: localized_schema.php:54 -msgid "Sort headlines by feed date" -msgstr "" - -#: localized_schema.php:55 prefs.js:1784 -msgid "Customize stylesheet" -msgstr "" - -#: localized_schema.php:56 -msgid "Login with an SSL certificate" -msgstr "" - -#: login_form.php:131 mobile/login_form.php:38 -msgid "Login:" -msgstr "" - -#: login_form.php:135 mobile/login_form.php:43 -msgid "Password:" -msgstr "" - -#: login_form.php:139 -msgid "Language:" -msgstr "" - -#: login_form.php:148 -msgid "Profile:" -msgstr "" - -#: login_form.php:161 mobile/login_form.php:28 -msgid "Log in" -msgstr "" - -#: login_form.php:164 register.php:181 -msgid "Create new account" -msgstr "" - -#: login_form.php:178 -msgid "Use less traffic" -msgstr "" - -#: opml.php:163 opml.php:168 -msgid "OPML Utility" -msgstr "" - -#: opml.php:186 -msgid "Importing OPML..." -msgstr "" - -#: opml.php:191 -msgid "Return to preferences" -msgstr "" - -#: prefs.php:81 -msgid "Keyboard shortcuts" -msgstr "" - -#: prefs.php:82 help/4.php:14 -msgid "Exit preferences" -msgstr "" - -#: prefs.php:90 tt-rss.php:74 modules/pref-prefs.php:265 help/3.php:74 -#: help/4.php:8 -msgid "Preferences" -msgstr "" - -#: prefs.php:96 modules/pref-filters.php:90 help/4.php:11 -msgid "Filters" -msgstr "" - -#: prefs.php:103 help/4.php:13 -msgid "Users" -msgstr "" - -#: prefs.php:108 -msgid "Linked" -msgstr "" - -#: register.php:185 -msgid "New user registrations are administratively disabled." -msgstr "" - -#: register.php:210 -msgid "" -"Your temporary password will be sent to the specified email. Accounts, which " -"were not logged in once, are erased automatically 24 hours after temporary " -"password is sent." -msgstr "" - -#: register.php:216 -msgid "Desired login:" -msgstr "" - -#: register.php:219 -msgid "Check availability" -msgstr "" - -#: register.php:221 -msgid "Email:" -msgstr "" - -#: register.php:224 -msgid "How much is two plus two:" -msgstr "" - -#: register.php:227 -msgid "Submit registration" -msgstr "" - -#: register.php:245 -msgid "Your registration information is incomplete." -msgstr "" - -#: register.php:260 -msgid "Sorry, this username is already taken." -msgstr "" - -#: register.php:278 -msgid "Registration failed." -msgstr "" - -#: register.php:362 -msgid "Account created successfully." -msgstr "" - -#: register.php:384 -msgid "New user registrations are currently closed." -msgstr "" - -#: tt-rss.php:78 -msgid "Comments?" -msgstr "" - -#: tt-rss.php:88 -msgid "New version of Tiny Tiny RSS is available!" -msgstr "" - -#: tt-rss.php:113 -msgid "News" -msgstr "" - -#: tt-rss.php:122 -msgid "Collapse feedlist" -msgstr "" - -#: tt-rss.php:125 -msgid "Show articles" -msgstr "" - -#: tt-rss.php:128 -msgid "Adaptive" -msgstr "" - -#: tt-rss.php:129 -msgid "All Articles" -msgstr "" - -#: tt-rss.php:133 -msgid "Ignore Scoring" -msgstr "" - -#: tt-rss.php:134 -msgid "Updated" -msgstr "" - -#: tt-rss.php:137 -msgid "Sort articles" -msgstr "" - -#: tt-rss.php:141 modules/pref-filters.php:228 -msgid "Date" -msgstr "" - -#: tt-rss.php:143 -msgid "Score" -msgstr "" - -#: tt-rss.php:148 modules/pref-feeds.php:347 modules/pref-feeds.php:600 -msgid "Update" -msgstr "" - -#: tt-rss.php:160 -msgid "Search..." -msgstr "" - -#: tt-rss.php:161 -msgid "Feed actions:" -msgstr "" - -#: tt-rss.php:162 -msgid "Subscribe to feed..." -msgstr "" - -#: tt-rss.php:163 -msgid "Edit this feed..." -msgstr "" - -#: tt-rss.php:164 -msgid "Rescore feed" -msgstr "" - -#: tt-rss.php:165 modules/pref-feeds.php:530 modules/pref-feeds.php:1374 -msgid "Unsubscribe" -msgstr "" - -#: tt-rss.php:166 -msgid "All feeds:" -msgstr "" - -#: tt-rss.php:168 help/3.php:56 -msgid "(Un)hide read feeds" -msgstr "" - -#: tt-rss.php:169 -msgid "Other actions:" -msgstr "" - -#: tt-rss.php:170 -msgid "Switch to digest..." -msgstr "" - -#: tt-rss.php:171 -msgid "Show tag cloud..." -msgstr "" - -#: tt-rss.php:172 -msgid "Select by tags..." -msgstr "" - -#: tt-rss.php:174 -msgid "Create filter..." -msgstr "" - -#: tt-rss.php:175 -msgid "Keyboard shortcuts help" -msgstr "" - -#: tt-rss.php:176 tt-rss.js:437 -msgid "About..." -msgstr "" - -#: twitter.php:95 -msgid "Register with Twitter" -msgstr "" - -#: twitter.php:99 -msgid "Could not connect to Twitter. Refresh the page or try again later." -msgstr "" - -#: twitter.php:103 -msgid "Congratulations! You have successfully registered with Twitter." -msgstr "" - -#: twitter.php:115 modules/pref-prefs.php:464 -msgid "Register" -msgstr "" - -#: modules/backend-rpc.php:843 -msgid "Your request could not be completed." -msgstr "" - -#: modules/backend-rpc.php:847 -msgid "Feed update has been scheduled." -msgstr "" - -#: modules/backend-rpc.php:855 -msgid "Category update has been scheduled." -msgstr "" - -#: modules/backend-rpc.php:868 -msgid "Can't update this kind of feed." -msgstr "" - -#: modules/help.php:6 -msgid "Help" -msgstr "" - -#: modules/help.php:17 -msgid "Help topic not found." -msgstr "" - -#: modules/opml_domdoc.php:60 -#, php-format -msgid "
  • Adding category %s.
  • " -msgstr "" - -#: modules/opml_domdoc.php:82 -#, php-format -msgid "Setting preference key %s to %s" -msgstr "" - -#: modules/opml_domdoc.php:128 -msgid "is already imported." -msgstr "" - -#: modules/opml_domdoc.php:148 -msgid "OK" -msgstr "" - -#: modules/opml_domdoc.php:157 -msgid "Error while parsing document." -msgstr "" - -#: modules/opml_domdoc.php:161 -msgid "Error: please upload OPML file." -msgstr "" - -#: modules/popup-dialog.php:34 -msgid "Importing using DOMXML." -msgstr "" - -#: modules/popup-dialog.php:40 -msgid "Importing using DOMDocument." -msgstr "" - -#: modules/popup-dialog.php:45 -msgid "DOMXML extension is not found. It is required for PHP versions below 5." -msgstr "" - -#: modules/popup-dialog.php:80 -msgid "Create profile" -msgstr "" - -#: modules/popup-dialog.php:103 modules/popup-dialog.php:132 -msgid "(active)" -msgstr "" - -#: modules/popup-dialog.php:166 -msgid "Remove selected profiles" -msgstr "" - -#: modules/popup-dialog.php:168 -msgid "Activate profile" -msgstr "" - -#: modules/popup-dialog.php:179 -msgid "Public OPML URL" -msgstr "" - -#: modules/popup-dialog.php:184 -msgid "Your Public OPML URL is:" -msgstr "" - -#: modules/popup-dialog.php:193 modules/popup-dialog.php:901 -msgid "Generate new URL" -msgstr "" - -#: modules/popup-dialog.php:206 -msgid "Notice" -msgstr "" - -#: modules/popup-dialog.php:212 -msgid "" -"Update daemon is enabled in configuration, but daemon process is not " -"running, which prevents all feeds from updating. Please start the daemon " -"process or contact instance owner." -msgstr "" - -#: modules/popup-dialog.php:216 modules/popup-dialog.php:225 -msgid "Last update:" -msgstr "" - -#: modules/popup-dialog.php:221 -msgid "" -"Update daemon is taking too long to perform a feed update. This could " -"indicate a problem like crash or a hang. Please check the daemon process or " -"contact instance owner." -msgstr "" - -#: modules/popup-dialog.php:247 modules/pref-feeds.php:300 -#: modules/pref-feeds.php:561 -msgid "Feed" -msgstr "" - -#: modules/popup-dialog.php:257 modules/pref-feeds.php:339 -#: modules/pref-feeds.php:589 -msgid "Place in category:" -msgstr "" - -#: modules/popup-dialog.php:265 -msgid "Available feeds" -msgstr "" - -#: modules/popup-dialog.php:277 modules/pref-feeds.php:379 -#: modules/pref-feeds.php:632 modules/pref-prefs.php:205 -#: modules/pref-users.php:147 -msgid "Authentication" -msgstr "" - -#: modules/popup-dialog.php:281 modules/pref-feeds.php:389 -#: modules/pref-feeds.php:636 modules/pref-users.php:436 -msgid "Login" -msgstr "" - -#: modules/popup-dialog.php:284 modules/pref-feeds.php:397 -#: modules/pref-feeds.php:642 -msgid "Password" -msgstr "" - -#: modules/popup-dialog.php:294 -msgid "This feed requires authentication." -msgstr "" - -#: modules/popup-dialog.php:299 modules/popup-dialog.php:352 -msgid "Subscribe" -msgstr "" - -#: modules/popup-dialog.php:300 -msgid "More feeds" -msgstr "" - -#: modules/popup-dialog.php:301 modules/popup-dialog.php:354 -#: modules/popup-dialog.php:433 modules/popup-dialog.php:544 -#: modules/popup-dialog.php:715 modules/popup-dialog.php:873 -#: modules/popup-dialog.php:962 modules/popup-dialog.php:989 -#: modules/popup-dialog.php:1071 modules/pref-feeds.php:547 -#: modules/pref-feeds.php:704 modules/pref-filters.php:340 -#: modules/pref-instances.php:98 modules/pref-labels.php:80 -#: modules/pref-users.php:186 -msgid "Cancel" -msgstr "" - -#: modules/popup-dialog.php:324 modules/popup-dialog.php:432 -#: modules/pref-feeds.php:1335 modules/pref-users.php:367 tt-rss.js:233 -msgid "Search" -msgstr "" - -#: modules/popup-dialog.php:328 -msgid "Popular feeds" -msgstr "" - -#: modules/popup-dialog.php:329 -msgid "Feed archive" -msgstr "" - -#: modules/popup-dialog.php:332 -msgid "limit:" -msgstr "" - -#: modules/popup-dialog.php:353 modules/pref-feeds.php:520 -#: modules/pref-filters.php:330 modules/pref-filters.php:537 -#: modules/pref-instances.php:144 modules/pref-labels.php:281 -#: modules/pref-users.php:393 -msgid "Remove" -msgstr "" - -#: modules/popup-dialog.php:365 -msgid "Look for" -msgstr "" - -#: modules/popup-dialog.php:375 -msgid "match on" -msgstr "" - -#: modules/popup-dialog.php:380 -msgid "Title or content" -msgstr "" - -#: modules/popup-dialog.php:391 -msgid "Limit search to:" -msgstr "" - -#: modules/popup-dialog.php:407 -msgid "This feed" -msgstr "" - -#: modules/popup-dialog.php:455 modules/pref-filters.php:219 -msgid "Match" -msgstr "" - -#: modules/popup-dialog.php:462 modules/pref-filters.php:231 -msgid "before" -msgstr "" - -#: modules/popup-dialog.php:463 modules/pref-filters.php:232 -msgid "after" -msgstr "" - -#: modules/popup-dialog.php:478 modules/pref-filters.php:245 -msgid "Check it" -msgstr "" - -#: modules/popup-dialog.php:481 modules/pref-filters.php:248 -msgid "on field" -msgstr "" - -#: modules/popup-dialog.php:487 modules/pref-filters.php:254 digest.js:239 -msgid "in" -msgstr "" - -#: modules/popup-dialog.php:493 modules/pref-filters.php:260 -msgid "Perform Action" -msgstr "" - -#: modules/popup-dialog.php:510 modules/pref-filters.php:280 -msgid "with parameters:" -msgstr "" - -#: modules/popup-dialog.php:524 modules/pref-feeds.php:407 -#: modules/pref-feeds.php:648 modules/pref-filters.php:300 -#: modules/pref-users.php:169 -msgid "Options" -msgstr "" - -#: modules/popup-dialog.php:528 modules/pref-filters.php:312 -msgid "Enabled" -msgstr "" - -#: modules/popup-dialog.php:531 modules/pref-filters.php:321 -msgid "Inverse match" -msgstr "" - -#: modules/popup-dialog.php:538 modules/pref-filters.php:334 -msgid "Test" -msgstr "" - -#: modules/popup-dialog.php:541 -msgid "Create" -msgstr "" - -#: modules/popup-dialog.php:571 -msgid "" -"These feeds have not been updated with new content for 3 months (oldest " -"first):" -msgstr "" - -#: modules/popup-dialog.php:595 modules/popup-dialog.php:655 -msgid "Click to edit feed" -msgstr "" - -#: modules/popup-dialog.php:613 modules/popup-dialog.php:675 -msgid "Unsubscribe from selected feeds" -msgstr "" - -#: modules/popup-dialog.php:628 -msgid "These feeds have not been updated because of errors:" -msgstr "" - -#: modules/popup-dialog.php:688 -msgid "Tags for this article (separated by commas):" -msgstr "" - -#: modules/popup-dialog.php:713 modules/popup-dialog.php:960 -#: modules/popup-dialog.php:987 modules/pref-feeds.php:546 -#: modules/pref-feeds.php:701 modules/pref-filters.php:337 -#: modules/pref-instances.php:95 modules/pref-labels.php:78 -#: modules/pref-users.php:184 -msgid "Save" -msgstr "" - -#: modules/popup-dialog.php:721 -msgid "Tag Cloud" -msgstr "" - -#: modules/popup-dialog.php:743 -msgid "Select item(s) by tags" -msgstr "" - -#: modules/popup-dialog.php:746 -msgid "Match:" -msgstr "" - -#: modules/popup-dialog.php:751 -msgid "Which Tags?" -msgstr "" - -#: modules/popup-dialog.php:764 -msgid "Display entries" -msgstr "" - -#: modules/popup-dialog.php:813 modules/popup-dialog.php:819 -msgid "[Forwarded]" -msgstr "" - -#: modules/popup-dialog.php:813 -msgid "Multiple articles" -msgstr "" - -#: modules/popup-dialog.php:834 -msgid "From:" -msgstr "" - -#: modules/popup-dialog.php:843 -msgid "To:" -msgstr "" - -#: modules/popup-dialog.php:856 -msgid "Subject:" -msgstr "" - -#: modules/popup-dialog.php:872 -msgid "Send e-mail" -msgstr "" - -#: modules/popup-dialog.php:892 -msgid "You can view this feed as RSS using the following URL:" -msgstr "" - -#: modules/popup-dialog.php:919 -#, php-format -msgid "New version of Tiny Tiny RSS is available (%s)." -msgstr "" - -#: modules/popup-dialog.php:929 modules/pref-users.php:389 -msgid "Details" -msgstr "" - -#: modules/popup-dialog.php:931 -msgid "Download" -msgstr "" - -#: modules/popup-dialog.php:945 -#, php-format -msgid "" -"You can override colors, fonts and layout of your currently selected theme " -"with custom CSS declarations here. This file can be used as a baseline." -msgstr "" - -#: modules/popup-dialog.php:1030 modules/pref-instances.php:54 -msgid "Instance" -msgstr "" - -#: modules/popup-dialog.php:1036 modules/pref-feeds.php:317 -#: modules/pref-feeds.php:576 modules/pref-instances.php:62 -msgid "URL:" -msgstr "" - -#: modules/popup-dialog.php:1039 modules/pref-instances.php:65 -#: modules/pref-instances.php:162 -msgid "Instance URL" -msgstr "" - -#: modules/popup-dialog.php:1049 modules/pref-instances.php:76 -msgid "Access key:" -msgstr "" - -#: modules/popup-dialog.php:1052 modules/pref-instances.php:79 -#: modules/pref-instances.php:163 -msgid "Access key" -msgstr "" - -#: modules/popup-dialog.php:1056 modules/pref-instances.php:83 -msgid "Use one access key for both linked instances." -msgstr "" - -#: modules/popup-dialog.php:1064 modules/pref-instances.php:91 -msgid "Generate new key" -msgstr "" - -#: modules/popup-dialog.php:1068 -msgid "Create link" -msgstr "" - -#: modules/popup-dialog.php:1094 -msgid "You can share this article by the following unique URL:" -msgstr "" - -#: modules/pref-feeds.php:4 -msgid "Check to enable field" -msgstr "" - -#: modules/pref-feeds.php:83 modules/pref-feeds.php:121 -#: modules/pref-feeds.php:127 modules/pref-feeds.php:150 -#, php-format -msgid "(%d feeds)" -msgstr "" - -#: modules/pref-feeds.php:306 -msgid "Feed Title" -msgstr "" - -#: modules/pref-feeds.php:362 modules/pref-feeds.php:612 -msgid "using" -msgstr "" - -#: modules/pref-feeds.php:372 modules/pref-feeds.php:623 -msgid "Article purging:" -msgstr "" - -#: modules/pref-feeds.php:401 -msgid "" -"Hint: you need to fill in your login information if your feed " -"requires authentication, except for Twitter feeds." -msgstr "" - -#: modules/pref-feeds.php:421 modules/pref-feeds.php:652 -msgid "Hide from Popular feeds" -msgstr "" - -#: modules/pref-feeds.php:432 modules/pref-feeds.php:657 -msgid "Right-to-left content" -msgstr "" - -#: modules/pref-feeds.php:444 modules/pref-feeds.php:663 -msgid "Include in e-mail digest" -msgstr "" - -#: modules/pref-feeds.php:457 modules/pref-feeds.php:669 -msgid "Always display image attachments" -msgstr "" - -#: modules/pref-feeds.php:472 -msgid "Cache images locally (SimplePie only)" -msgstr "" - -#: modules/pref-feeds.php:485 modules/pref-feeds.php:686 -msgid "Mark updated articles as unread" -msgstr "" - -#: modules/pref-feeds.php:497 modules/pref-feeds.php:692 -msgid "Mark posts as updated on content change" -msgstr "" - -#: modules/pref-feeds.php:504 -msgid "Icon" -msgstr "" - -#: modules/pref-feeds.php:518 -msgid "Replace" -msgstr "" - -#: modules/pref-feeds.php:537 -msgid "Resubscribe to push updates" -msgstr "" - -#: modules/pref-feeds.php:544 -msgid "Resets PubSubHubbub subscription status for push-enabled feeds." -msgstr "" - -#: modules/pref-feeds.php:678 -msgid "Cache images locally" -msgstr "" - -#: modules/pref-feeds.php:942 modules/pref-feeds.php:995 -msgid "All done." -msgstr "" - -#: modules/pref-feeds.php:1027 -#, php-format -msgid "Subscribed to %s." -msgstr "" - -#: modules/pref-feeds.php:1030 -#, php-format -msgid "Could not subscribe to %s." -msgstr "" - -#: modules/pref-feeds.php:1033 -#, php-format -msgid "No feeds found in %s." -msgstr "" - -#: modules/pref-feeds.php:1036 -#, php-format -msgid "Already subscribed to %s." -msgstr "" - -#: modules/pref-feeds.php:1044 -#, php-format -msgid "Could not subscribe to %s.
    Can't download the Feed URL." -msgstr "" - -#: modules/pref-feeds.php:1066 -msgid "Subscribe to selected feed" -msgstr "" - -#: modules/pref-feeds.php:1091 -msgid "Edit subscription options" -msgstr "" - -#: modules/pref-feeds.php:1173 -#, php-format -msgid "Category $%s already exists in the database." -msgstr "" - -#: modules/pref-feeds.php:1189 -msgid "Create category" -msgstr "" - -#: modules/pref-feeds.php:1259 -msgid "No feed categories defined." -msgstr "" - -#: modules/pref-feeds.php:1265 -msgid "Remove selected categories" -msgstr "" - -#: modules/pref-feeds.php:1293 -msgid "Feeds with errors" -msgstr "" - -#: modules/pref-feeds.php:1316 -msgid "Inactive feeds" -msgstr "" - -#: modules/pref-feeds.php:1339 modules/pref-filters.php:522 -#: modules/pref-instances.php:134 modules/pref-labels.php:269 -#: modules/pref-users.php:377 -msgid "Select" -msgstr "" - -#: modules/pref-feeds.php:1351 help/3.php:57 help/4.php:22 -msgid "Subscribe to feed" -msgstr "" - -#: modules/pref-feeds.php:1353 -msgid "Edit selected feeds" -msgstr "" - -#: modules/pref-feeds.php:1355 modules/pref-feeds.php:1365 -msgid "Reset sort order" -msgstr "" - -#: modules/pref-feeds.php:1360 -msgid "Categories" -msgstr "" - -#: modules/pref-feeds.php:1363 -msgid "Edit categories" -msgstr "" - -#: modules/pref-feeds.php:1379 -msgid "More actions..." -msgstr "" - -#: modules/pref-feeds.php:1383 -msgid "Manual purge" -msgstr "" - -#: modules/pref-feeds.php:1387 -msgid "Clear feed data" -msgstr "" - -#: modules/pref-feeds.php:1388 modules/pref-filters.php:541 -msgid "Rescore articles" -msgstr "" - -#: modules/pref-feeds.php:1430 -msgid "Hint: you can drag feeds and categories around." -msgstr "" - -#: modules/pref-feeds.php:1438 -msgid "OPML" -msgstr "" - -#: modules/pref-feeds.php:1440 -msgid "" -"Using OPML you can export and import your feeds and Tiny Tiny RSS settings." -msgstr "" - -#: modules/pref-feeds.php:1442 -msgid "Note: Only main settings profile can be migrated using OPML." -msgstr "" - -#: modules/pref-feeds.php:1446 modules/pref-feeds.php:1459 -msgid "Import" -msgstr "" - -#: modules/pref-feeds.php:1461 modules/pref-feeds.php:1469 -msgid "Export" -msgstr "" - -#: modules/pref-feeds.php:1463 -msgid "Filename:" -msgstr "" - -#: modules/pref-feeds.php:1465 -msgid "Include settings" -msgstr "" - -#: modules/pref-feeds.php:1471 -msgid "Publish" -msgstr "" - -#: modules/pref-feeds.php:1473 -msgid "" -"Your OPML can be published publicly and can be subscribed by anyone who " -"knows the URL below." -msgstr "" - -#: modules/pref-feeds.php:1475 -msgid "" -"Note: Published OPML does not include your Tiny Tiny RSS settings, feeds " -"that require authentication or feeds hidden from Popular feeds." -msgstr "" - -#: modules/pref-feeds.php:1478 modules/pref-feeds.php:1526 -msgid "Display URL" -msgstr "" - -#: modules/pref-feeds.php:1485 -msgid "Firefox integration" -msgstr "" - -#: modules/pref-feeds.php:1487 -msgid "" -"This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the " -"link below." -msgstr "" - -#: modules/pref-feeds.php:1494 -msgid "Click here to register this site as a feed reader." -msgstr "" - -#: modules/pref-feeds.php:1502 -msgid "Subscribing using bookmarklet" -msgstr "" - -#: modules/pref-feeds.php:1504 -msgid "" -"Drag the link below to your browser toolbar, open the feed you're interested " -"in in your browser and click on the link to subscribe to it." -msgstr "" - -#: modules/pref-feeds.php:1508 -#, php-format -msgid "Subscribe to %s in Tiny Tiny RSS?" -msgstr "" - -#: modules/pref-feeds.php:1512 -msgid "Subscribe in Tiny Tiny RSS" -msgstr "" - -#: modules/pref-feeds.php:1516 -msgid "Published & shared articles and generated feeds" -msgstr "" - -#: modules/pref-feeds.php:1518 -msgid "Published articles and generated feeds" -msgstr "" - -#: modules/pref-feeds.php:1520 -msgid "" -"Published articles are exported as a public RSS feed and can be subscribed " -"by anyone who knows the URL specified below." -msgstr "" - -#: modules/pref-feeds.php:1529 -msgid "Clear all generated URLs" -msgstr "" - -#: modules/pref-feeds.php:1531 -msgid "Articles shared by URL" -msgstr "" - -#: modules/pref-feeds.php:1533 -msgid "You can disable all articles shared by unique URLs here." -msgstr "" - -#: modules/pref-feeds.php:1536 -msgid "Unshare all articles" -msgstr "" - -#: modules/pref-feeds.php:1542 -msgid "Twitter" -msgstr "" - -#: modules/pref-feeds.php:1551 -msgid "" -"Before you can update your Twitter feeds, you must register this instance of " -"Tiny Tiny RSS with Twitter.com." -msgstr "" - -#: modules/pref-feeds.php:1553 -msgid "" -"You have been successfully registered with Twitter.com and should be able to " -"access your Twitter feeds." -msgstr "" - -#: modules/pref-feeds.php:1557 -msgid "Register with Twitter.com" -msgstr "" - -#: modules/pref-feeds.php:1563 -msgid "Clear stored credentials" -msgstr "" - -#: modules/pref-feeds.php:1654 -#, php-format -msgid "%d archived articles" -msgstr "" - -#: modules/pref-feeds.php:1678 -msgid "No feeds found." -msgstr "" - -#: modules/pref-filters.php:38 -msgid "Articles matching this filter:" -msgstr "" - -#: modules/pref-filters.php:75 -msgid "No articles matching this filter has been found." -msgstr "" - -#: modules/pref-filters.php:470 -#, php-format -msgid "Created filter %s" -msgstr "" - -#: modules/pref-filters.php:531 help/3.php:34 help/4.php:25 -msgid "Create filter" -msgstr "" - -#: modules/pref-filters.php:534 modules/pref-instances.php:143 -#: modules/pref-users.php:391 -msgid "Edit" -msgstr "" - -#: modules/pref-instances.php:5 modules/pref-users.php:7 -msgid "Your access level is insufficient to open this tab." -msgstr "" - -#: modules/pref-instances.php:142 -msgid "Link instance" -msgstr "" - -#: modules/pref-instances.php:154 -msgid "" -"You can connect other instances of Tiny Tiny RSS to this one to share " -"Popular feeds. Link to this instance of Tiny Tiny RSS by using this URL:" -msgstr "" - -#: modules/pref-instances.php:164 -msgid "Last connected" -msgstr "" - -#: modules/pref-instances.php:165 -msgid "Stored feeds" -msgstr "" - -#: modules/pref-instances.php:183 modules/pref-users.php:467 -msgid "Click to edit" -msgstr "" - -#: modules/pref-labels.php:21 -msgid "Caption" -msgstr "" - -#: modules/pref-labels.php:36 -msgid "Colors" -msgstr "" - -#: modules/pref-labels.php:41 -msgid "Foreground:" -msgstr "" - -#: modules/pref-labels.php:41 -msgid "Background:" -msgstr "" - -#: modules/pref-labels.php:231 -#, php-format -msgid "Created label %s" -msgstr "" - -#: modules/pref-labels.php:278 help/3.php:33 help/4.php:26 -msgid "Create label" -msgstr "" - -#: modules/pref-labels.php:284 -msgid "Clear colors" -msgstr "" - -#: modules/pref-prefs.php:29 -msgid "Old password cannot be blank." -msgstr "" - -#: modules/pref-prefs.php:34 -msgid "New password cannot be blank." -msgstr "" - -#: modules/pref-prefs.php:39 -msgid "Entered passwords do not match." -msgstr "" - -#: modules/pref-prefs.php:63 -msgid "Password has been changed." -msgstr "" - -#: modules/pref-prefs.php:65 -msgid "Old password is incorrect." -msgstr "" - -#: modules/pref-prefs.php:93 -msgid "The configuration was saved." -msgstr "" - -#: modules/pref-prefs.php:109 -#, php-format -msgid "Unknown option: %s" -msgstr "" - -#: modules/pref-prefs.php:122 -msgid "Your personal data has been saved." -msgstr "" - -#: modules/pref-prefs.php:154 -msgid "Personal data" -msgstr "" - -#: modules/pref-prefs.php:181 -msgid "Full name" -msgstr "" - -#: modules/pref-prefs.php:185 -msgid "E-mail" -msgstr "" - -#: modules/pref-prefs.php:190 -msgid "Access level" -msgstr "" - -#: modules/pref-prefs.php:200 -msgid "Save data" -msgstr "" - -#: modules/pref-prefs.php:212 -msgid "Your password is at default value, please change it." -msgstr "" - -#: modules/pref-prefs.php:240 -msgid "Old password" -msgstr "" - -#: modules/pref-prefs.php:243 -msgid "New password" -msgstr "" - -#: modules/pref-prefs.php:248 -msgid "Confirm password" -msgstr "" - -#: modules/pref-prefs.php:258 -msgid "Change password" -msgstr "" - -#: modules/pref-prefs.php:344 -msgid "Select theme" -msgstr "" - -#: modules/pref-prefs.php:402 -msgid "Customize" -msgstr "" - -#: modules/pref-prefs.php:422 modules/pref-prefs.php:429 -#: modules/pref-prefs.php:434 -msgid "Yes" -msgstr "" - -#: modules/pref-prefs.php:424 modules/pref-prefs.php:434 -msgid "No" -msgstr "" - -#: modules/pref-prefs.php:468 -msgid "Clear" -msgstr "" - -#: modules/pref-prefs.php:494 -msgid "Save configuration" -msgstr "" - -#: modules/pref-prefs.php:497 -msgid "Manage profiles" -msgstr "" - -#: modules/pref-prefs.php:500 -msgid "Reset to defaults" -msgstr "" - -#: modules/pref-users.php:20 -msgid "User details" -msgstr "" - -#: modules/pref-users.php:34 -msgid "User not found" -msgstr "" - -#: modules/pref-users.php:53 modules/pref-users.php:438 -msgid "Registered" -msgstr "" - -#: modules/pref-users.php:54 -msgid "Last logged in" -msgstr "" - -#: modules/pref-users.php:61 -msgid "Subscribed feeds count" -msgstr "" - -#: modules/pref-users.php:65 -msgid "Subscribed feeds" -msgstr "" - -#: modules/pref-users.php:114 -msgid "User Editor" -msgstr "" - -#: modules/pref-users.php:150 -msgid "Access level: " -msgstr "" - -#: modules/pref-users.php:163 -msgid "Change password to" -msgstr "" - -#: modules/pref-users.php:172 -msgid "E-mail: " -msgstr "" - -#: modules/pref-users.php:206 -#, php-format -msgid "Changed password of user %s." -msgstr "" - -#: modules/pref-users.php:254 -#, php-format -msgid "Added user %s with password %s" -msgstr "" - -#: modules/pref-users.php:261 -#, php-format -msgid "Could not create user %s" -msgstr "" - -#: modules/pref-users.php:265 -#, php-format -msgid "User %s already exists." -msgstr "" - -#: modules/pref-users.php:285 -#, php-format -msgid "" -"Changed password of user %s\n" -"\t\t\t\t\t to %s" -msgstr "" - -#: modules/pref-users.php:289 -#, php-format -msgid "Notifying %s." -msgstr "" - -#: modules/pref-users.php:326 -msgid "[tt-rss] Password change notification" -msgstr "" - -#: modules/pref-users.php:385 help/4.php:27 -msgid "Create user" -msgstr "" - -#: modules/pref-users.php:395 -msgid "Reset password" -msgstr "" - -#: modules/pref-users.php:437 -msgid "Access Level" -msgstr "" - -#: modules/pref-users.php:439 -msgid "Last login" -msgstr "" - -#: modules/pref-users.php:487 -msgid "No users defined." -msgstr "" - -#: modules/pref-users.php:489 -msgid "No matching users found." -msgstr "" - -#: help/2.php:1 -msgid "Content filtering" -msgstr "" - -#: help/2.php:3 -msgid "" -"Tiny Tiny RSS has support for filtering (or processing) articles. Filtering " -"is done once, when new article is imported to the database from the " -"newsfeed, specified field is matched against regular expression and some " -"action is taken. Regular expression matching is case-insensitive." -msgstr "" - -#: help/2.php:5 -msgid "" -"Supported actions are: filter (do not import) article, mark article as read, " -"set starred, assign tag(s), and set score. Filters can be defined globally " -"and for some specific feed." -msgstr "" - -#: help/2.php:7 -msgid "" -"Multiple and inverse matching are supported. All matching filters are " -"considered when article is being imported and all actions executed in " -"sequence. Inverse matching reverts matching result, e.g. filter matching " -"XYZZY in title with inverse flag will match all articles, except those " -"containing string XYZZY in title." -msgstr "" - -#: help/2.php:9 -msgid "See also:" -msgstr "" - -#: help/3.php:1 help/4.php:1 -msgid "Keyboard Shortcuts" -msgstr "" - -#: help/3.php:5 -msgid "Navigation" -msgstr "" - -#: help/3.php:8 -msgid "Move between feeds" -msgstr "" - -#: help/3.php:9 -msgid "Move between articles" -msgstr "" - -#: help/3.php:10 -msgid "Show search dialog" -msgstr "" - -#: help/3.php:13 -msgid "Active article actions" -msgstr "" - -#: help/3.php:16 -msgid "Toggle starred" -msgstr "" - -#: help/3.php:17 -msgid "Toggle published" -msgstr "" - -#: help/3.php:18 -msgid "Toggle unread" -msgstr "" - -#: help/3.php:19 -msgid "Edit tags" -msgstr "" - -#: help/3.php:20 -msgid "Dismiss selected articles" -msgstr "" - -#: help/3.php:21 -msgid "Dismiss read articles" -msgstr "" - -#: help/3.php:22 -msgid "Open article in new window" -msgstr "" - -#: help/3.php:23 -msgid "Mark articles below/above active one as read" -msgstr "" - -#: help/3.php:24 -msgid "Scroll article content" -msgstr "" - -#: help/3.php:25 -msgid "Email article" -msgstr "" - -#: help/3.php:29 help/4.php:30 -msgid "Other actions" -msgstr "" - -#: help/3.php:32 -msgid "Select article under mouse cursor" -msgstr "" - -#: help/3.php:35 -msgid "Collapse sidebar" -msgstr "" - -#: help/3.php:36 -msgid "Toggle category reordering mode" -msgstr "" - -#: help/3.php:37 help/4.php:34 -msgid "Display this help dialog" -msgstr "" - -#: help/3.php:42 -msgid "Multiple articles actions" -msgstr "" - -#: help/3.php:45 -msgid "Select all articles" -msgstr "" - -#: help/3.php:46 -msgid "Select unread articles" -msgstr "" - -#: help/3.php:47 -msgid "Invert article selection" -msgstr "" - -#: help/3.php:48 -msgid "Deselect all articles" -msgstr "" - -#: help/3.php:51 -msgid "Feed actions" -msgstr "" - -#: help/3.php:54 -msgid "Refresh active feed" -msgstr "" - -#: help/3.php:55 -msgid "Update all feeds" -msgstr "" - -#: help/3.php:58 FeedTree.js:131 -msgid "Edit feed" -msgstr "" - -#: help/3.php:59 -msgid "Sort by name or unread count" -msgstr "" - -#: help/3.php:60 -msgid "Mark feed as read" -msgstr "" - -#: help/3.php:61 -msgid "Reverse headlines order" -msgstr "" - -#: help/3.php:62 -msgid "Mark all feeds as read" -msgstr "" - -#: help/3.php:63 -msgid "If viewing category, (un)collapse it" -msgstr "" - -#: help/3.php:66 help/4.php:5 -msgid "Go to..." -msgstr "" - -#: help/3.php:73 -msgid "Tag cloud" -msgstr "" - -#: help/3.php:80 -msgid "Other interface tips are available in the Tiny Tiny RSS wiki." -msgstr "" - -#: help/3.php:82 help/4.php:41 -msgid "Press any key to close this window." -msgstr "" - -#: help/4.php:9 -msgid "My Feeds" -msgstr "" - -#: help/4.php:10 -msgid "Other Feeds" -msgstr "" - -#: help/4.php:19 -msgid "Panel actions" -msgstr "" - -#: help/4.php:23 -msgid "Top 25 feeds" -msgstr "" - -#: help/4.php:24 -msgid "Edit feed categories" -msgstr "" - -#: help/4.php:33 -msgid "Focus search (if present)" -msgstr "" - -#: help/4.php:39 -msgid "" -"Note: not all actions may be available, depending on Tiny Tiny RSS " -"configuration and your access level." -msgstr "" - -#: mobile/functions.php:58 mobile/functions.php:134 mobile/functions.php:170 -#: mobile/functions.php:197 mobile/functions.php:233 mobile/functions.php:372 -#: mobile/prefs.php:25 -msgid "Home" -msgstr "" - -#: mobile/functions.php:408 -msgid "Nothing found (click to reload feed)." -msgstr "" - -#: mobile/prefs.php:30 -msgid "Enable categories" -msgstr "" - -#: mobile/prefs.php:31 mobile/prefs.php:36 mobile/prefs.php:42 -#: mobile/prefs.php:47 mobile/prefs.php:52 -msgid "ON" -msgstr "" - -#: mobile/prefs.php:31 mobile/prefs.php:36 mobile/prefs.php:42 -#: mobile/prefs.php:47 mobile/prefs.php:52 -msgid "OFF" -msgstr "" - -#: mobile/prefs.php:35 -msgid "Browse categories like folders" -msgstr "" - -#: mobile/prefs.php:41 -msgid "Show images in posts" -msgstr "" - -#: mobile/prefs.php:46 -msgid "Hide read articles and feeds" -msgstr "" - -#: mobile/prefs.php:51 -msgid "Sort feeds by unread count" -msgstr "" - -#: digest.js:23 feedlist.js:462 tt-rss.js:521 tt-rss.js:534 -msgid "Mark all articles in %s as read?" -msgstr "" - -#: digest.js:69 -msgid "Mark %d displayed articles as read?" -msgstr "" - -#: digest.js:255 digest.js:688 viewfeed.js:425 -msgid "Unstar article" -msgstr "" - -#: digest.js:257 digest.js:692 viewfeed.js:430 -msgid "Star article" -msgstr "" - -#: digest.js:260 digest.js:723 viewfeed.js:465 -msgid "Unpublish article" -msgstr "" - -#: digest.js:265 -msgid "Original article" -msgstr "" - -#: digest.js:290 -msgid "Error: unable to load article." -msgstr "" - -#: digest.js:442 -msgid "Click to expand article." -msgstr "" - -#: digest.js:517 -msgid "%d more..." -msgstr "" - -#: digest.js:524 -msgid "No unread feeds." -msgstr "" - -#: digest.js:626 -msgid "Load more..." -msgstr "" - -#: feedlist.js:269 -msgid "New articles available in this feed (click to show)" -msgstr "" - -#: FeedTree.js:137 -msgid "Update feed" -msgstr "" - -#: functions.js:72 -msgid "" -"Are you sure to report this exception to tt-rss.org? The report will include " -"your browser information. Your IP would be saved in the database." -msgstr "" - -#: functions.js:624 -msgid "Date syntax appears to be correct:" -msgstr "" - -#: functions.js:627 -msgid "Date syntax is incorrect." -msgstr "" - -#: functions.js:763 -msgid "Remove stored feed icon?" -msgstr "" - -#: functions.js:795 -msgid "Please select an image file to upload." -msgstr "" - -#: functions.js:797 -msgid "Upload new icon for this feed?" -msgstr "" - -#: functions.js:814 -msgid "Please enter label caption:" -msgstr "" - -#: functions.js:819 -msgid "Can't create label: missing caption." -msgstr "" - -#: functions.js:861 -msgid "Subscribe to Feed" -msgstr "" - -#: functions.js:869 -msgid "Subscribing to feed..." -msgstr "" - -#: functions.js:887 -msgid "Subscribed to %s" -msgstr "" - -#: functions.js:892 -msgid "Specified URL seems to be invalid." -msgstr "" - -#: functions.js:895 -msgid "Specified URL doesn't seem to contain any feeds." -msgstr "" - -#: functions.js:931 -msgid "Couldn't download the specified URL." -msgstr "" - -#: functions.js:934 -msgid "You are already subscribed to this feed." -msgstr "" - -#: functions.js:963 -msgid "Create Filter" -msgstr "" - -#: functions.js:973 prefs.js:214 -msgid "Filter Test Results" -msgstr "" - -#: functions.js:1031 -msgid "" -"Reset subscription? Tiny Tiny RSS will try to subscribe to the notification " -"hub again on next feed update." -msgstr "" - -#: functions.js:1052 tt-rss.js:396 -msgid "Unsubscribe from %s?" -msgstr "" - -#: functions.js:1159 -msgid "Please enter category title:" -msgstr "" - -#: functions.js:1190 -msgid "Generate new syndication address for this feed?" -msgstr "" - -#: functions.js:1374 tt-rss.js:375 tt-rss.js:892 -msgid "You can't edit this kind of feed." -msgstr "" - -#: functions.js:1386 -msgid "Edit Feed" -msgstr "" - -#: functions.js:1424 -msgid "More Feeds" -msgstr "" - -#: functions.js:1485 functions.js:1595 prefs.js:439 prefs.js:469 prefs.js:501 -#: prefs.js:659 prefs.js:679 prefs.js:1268 prefs.js:1413 -msgid "No feeds are selected." -msgstr "" - -#: functions.js:1527 -msgid "" -"Remove selected feeds from the archive? Feeds with stored articles will not " -"be removed." -msgstr "" - -#: functions.js:1566 -msgid "Feeds with update errors" -msgstr "" - -#: functions.js:1577 prefs.js:1250 -msgid "Remove selected feeds?" -msgstr "" - -#: PrefFilterTree.js:32 -msgid "Inverse" -msgstr "" - -#: prefs.js:114 -msgid "Please enter login:" -msgstr "" - -#: prefs.js:121 -msgid "Can't create user: no login specified." -msgstr "" - -#: prefs.js:183 -msgid "Edit Filter" -msgstr "" - -#: prefs.js:187 -msgid "Remove filter %s?" -msgstr "" - -#: prefs.js:321 -msgid "Remove selected labels?" -msgstr "" - -#: prefs.js:337 prefs.js:1454 -msgid "No labels are selected." -msgstr "" - -#: prefs.js:351 -msgid "" -"Remove selected users? Neither default admin nor your account will be " -"removed." -msgstr "" - -#: prefs.js:368 prefs.js:549 prefs.js:570 prefs.js:609 -msgid "No users are selected." -msgstr "" - -#: prefs.js:386 -msgid "Remove selected filters?" -msgstr "" - -#: prefs.js:401 prefs.js:639 -msgid "No filters are selected." -msgstr "" - -#: prefs.js:420 -msgid "Unsubscribe from selected feeds?" -msgstr "" - -#: prefs.js:454 -msgid "Please select only one feed." -msgstr "" - -#: prefs.js:460 -msgid "Erase all non-starred articles in selected feed?" -msgstr "" - -#: prefs.js:482 -msgid "How many days of articles to keep (0 - use default)?" -msgstr "" - -#: prefs.js:520 -msgid "Login field cannot be blank." -msgstr "" - -#: prefs.js:554 prefs.js:575 prefs.js:614 -msgid "Please select only one user." -msgstr "" - -#: prefs.js:579 -msgid "Reset password of selected user?" -msgstr "" - -#: prefs.js:644 -msgid "Please select only one filter." -msgstr "" - -#: prefs.js:701 -msgid "Edit Multiple Feeds" -msgstr "" - -#: prefs.js:725 -msgid "Save changes to selected feeds?" -msgstr "" - -#: prefs.js:815 -msgid "OPML Import" -msgstr "" - -#: prefs.js:834 -msgid "Please choose an OPML file first." -msgstr "" - -#: prefs.js:954 -msgid "Reset to defaults?" -msgstr "" - -#: prefs.js:1170 -msgid "Feed Categories" -msgstr "" - -#: prefs.js:1179 -msgid "Remove selected categories?" -msgstr "" - -#: prefs.js:1198 -msgid "No categories are selected." -msgstr "" - -#: prefs.js:1239 -msgid "Feeds without recent updates" -msgstr "" - -#: prefs.js:1288 -msgid "Replace current OPML publishing address with a new one?" -msgstr "" - -#: prefs.js:1397 -msgid "Rescore articles in selected feeds?" -msgstr "" - -#: prefs.js:1420 -msgid "Rescore all articles? This operation may take a lot of time." -msgstr "" - -#: prefs.js:1440 -msgid "Reset selected labels to default colors?" -msgstr "" - -#: prefs.js:1477 -msgid "Settings Profiles" -msgstr "" - -#: prefs.js:1486 -msgid "" -"Remove selected profiles? Active and default profiles will not be removed." -msgstr "" - -#: prefs.js:1504 -msgid "No profiles are selected." -msgstr "" - -#: prefs.js:1512 prefs.js:1565 -msgid "Activate selected profile?" -msgstr "" - -#: prefs.js:1528 prefs.js:1581 -msgid "Please choose a profile to activate." -msgstr "" - -#: prefs.js:1589 -msgid "This will invalidate all previously generated feed URLs. Continue?" -msgstr "" - -#: prefs.js:1608 -msgid "This will invalidate all previously shared article URLs. Continue?" -msgstr "" - -#: prefs.js:1691 -msgid "Label Editor" -msgstr "" - -#: prefs.js:1755 -msgid "" -"This will clear your stored authentication information for Twitter. Continue?" -msgstr "" - -#: prefs.js:1826 -msgid "Link Instance" -msgstr "" - -#: prefs.js:1877 -msgid "Edit Instance" -msgstr "" - -#: prefs.js:1926 -msgid "Remove selected instances?" -msgstr "" - -#: prefs.js:1943 prefs.js:1955 -msgid "No instances are selected." -msgstr "" - -#: prefs.js:1960 -msgid "Please select only one instance." -msgstr "" - -#: tt-rss.js:147 -msgid "Mark all articles as read?" -msgstr "" - -#: tt-rss.js:385 -msgid "You can't unsubscribe from the category." -msgstr "" - -#: tt-rss.js:390 tt-rss.js:606 tt-rss.js:1055 -msgid "Please select some feed first." -msgstr "" - -#: tt-rss.js:601 -msgid "You can't rescore this kind of feed." -msgstr "" - -#: tt-rss.js:611 -msgid "Rescore articles in %s?" -msgstr "" - -#: tt-rss.js:1095 -msgid "New version available!" -msgstr "" - -#: viewfeed.js:636 viewfeed.js:664 viewfeed.js:691 viewfeed.js:753 -#: viewfeed.js:785 viewfeed.js:901 viewfeed.js:945 viewfeed.js:995 -#: viewfeed.js:1509 -msgid "No articles are selected." -msgstr "" - -#: viewfeed.js:881 -msgid "Mark all visible articles in %s as read?" -msgstr "" - -#: viewfeed.js:910 -msgid "Delete %d selected articles in %s?" -msgstr "" - -#: viewfeed.js:912 -msgid "Delete %d selected articles?" -msgstr "" - -#: viewfeed.js:954 -msgid "Archive %d selected articles in %s?" -msgstr "" - -#: viewfeed.js:957 -msgid "Move %d archived articles back?" -msgstr "" - -#: viewfeed.js:1001 -msgid "Mark %d selected articles in %s as read?" -msgstr "" - -#: viewfeed.js:1025 -msgid "Edit article Tags" -msgstr "" - -#: viewfeed.js:1175 -msgid "No article is selected." -msgstr "" - -#: viewfeed.js:1210 -msgid "No articles found to mark" -msgstr "" - -#: viewfeed.js:1212 -msgid "Mark %d article(s) as read?" -msgstr "" - -#: viewfeed.js:1376 -msgid "Loading..." -msgstr "" - -#: viewfeed.js:1523 -msgid "Forward article by email" -msgstr "" - -#: viewfeed.js:1920 -msgid "Open original article" -msgstr "" - -#: viewfeed.js:1926 -msgid "View in a tt-rss tab" -msgstr "" - -#: viewfeed.js:1973 -msgid "Remove label" -msgstr "" - -#: viewfeed.js:2078 -msgid "Playing..." -msgstr "" - -#: viewfeed.js:2079 -msgid "Click to pause" -msgstr "" - -#: viewfeed.js:2223 -msgid "Share article by URL" -msgstr "" diff --git a/mobile/article.php b/mobile/article.php index cbdad84da..345767fbd 100644 --- a/mobile/article.php +++ b/mobile/article.php @@ -6,13 +6,7 @@ define('MOBILE_VERSION', true); require_once "../config.php"; - require_once "functions.php"; - require_once "../functions.php"; - - require_once "../sessions.php"; - - require_once "../version.php"; - require_once "../db-prefs.php"; + require_once "mobile-functions.php"; $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); diff --git a/mobile/cat.php b/mobile/cat.php index b75a4c046..74d488e1f 100644 --- a/mobile/cat.php +++ b/mobile/cat.php @@ -6,13 +6,7 @@ define('MOBILE_VERSION', true); require_once "../config.php"; - require_once "functions.php"; - require_once "../functions.php"; - - require_once "../sessions.php"; - - require_once "../version.php"; - require_once "../db-prefs.php"; + require_once "mobile-functions.php"; $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); diff --git a/mobile/classic/functions.php b/mobile/classic/functions.php deleted file mode 100644 index 5e1ba8009..000000000 --- a/mobile/classic/functions.php +++ /dev/null @@ -1,789 +0,0 @@ -"; - - if ($tags) { - print __("Tags")." - (".__("View feeds").", "; - } else { - print __("Feeds")." - (".__("View tags").", "; - } - - print "".__("Search").", "; - - print "".__("Logout").")"; - print "
    "; - - print "
      "; - - $owner_uid = $_SESSION["uid"]; - - if (!$tags) { - - /* virtual feeds */ - - if (get_pref($link, 'ENABLE_FEED_CATS')) { - - $collapsed = get_pref($link, "_COLLAPSED_SPECIAL"); - - if ($collapsed == "t" || $collapsed == "1") { - $holder_class = "invisible"; - $ellipsis = "..."; - } else { - $holder_class = "feedCatHolder"; - $ellipsis = ""; - } - - $tmp_category = __("Special"); - - print "
    • - $tmp_category$ellipsis -
    • "; - - print "
      • "; - } - - foreach (array(-4, -3, -1, -2, 0) as $i) { - printMobileFeedEntry($i, "virt", false, false, - false, $link); - } - - if (get_pref($link, 'ENABLE_FEED_CATS')) { - print "
      "; - } - - - $result = db_query($link, "SELECT id,caption FROM - ttrss_labels2 WHERE owner_uid = '$owner_uid' ORDER by caption"); - - if (db_num_rows($result) > 0) { - if (get_pref($link, 'ENABLE_FEED_CATS')) { - - $collapsed = get_pref($link, "_COLLAPSED_LABELS"); - - if ($collapsed == "t" || $collapsed == "1") { - $holder_class = "invisible"; - $ellipsis = "..."; - } else { - $holder_class = "feedCatHolder"; - $ellipsis = ""; - } - - $tmp_category = __("Labels"); - - print "
    • - $tmp_category$ellipsis -
    • "; - - print "
      • "; - } else { -// print "

      • "; - } - } - - while ($line = db_fetch_assoc($result)) { - - $count = getFeedUnread($link, -$line["id"]-11); - - $class = "label"; - - printMobileFeedEntry(-$line["id"]-11, - $class, $line["caption"], $count, false, $link); - - } - - if (db_num_rows($result) > 0) { - if (get_pref($link, 'ENABLE_FEED_CATS')) { - print "
      "; - } - } - - - if (get_pref($link, 'ENABLE_FEED_CATS')) { - $order_by_qpart = "category,title"; - } else { - $order_by_qpart = "title"; - } - - $result = db_query($link, "SELECT ttrss_feeds.*, - ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated_noms, - (SELECT COUNT(id) FROM ttrss_entries,ttrss_user_entries - WHERE feed_id = ttrss_feeds.id AND - ttrss_user_entries.ref_id = ttrss_entries.id AND - owner_uid = '$owner_uid') AS total, - (SELECT COUNT(id) FROM ttrss_entries,ttrss_user_entries - WHERE feed_id = ttrss_feeds.id AND unread = true - AND ttrss_user_entries.ref_id = ttrss_entries.id - AND owner_uid = '$owner_uid') as unread, - cat_id,last_error, - ttrss_feed_categories.title AS category, - ttrss_feed_categories.collapsed - FROM ttrss_feeds LEFT JOIN ttrss_feed_categories - ON (ttrss_feed_categories.id = cat_id) - WHERE - ttrss_feeds.owner_uid = '$owner_uid' - ORDER BY $order_by_qpart"); - - $actid = $_GET["actid"]; - - /* real feeds */ - - $lnum = 0; - - $category = ""; - - while ($line = db_fetch_assoc($result)) { - if (get_pref($link, 'HIDE_READ_FEEDS') && (int)$line['unread']==0) { - continue; - } - - $feed = db_unescape_string($line["title"]); - $feed_id = $line["id"]; - - $subop = $_GET["subop"]; - - $total = $line["total"]; - $unread = $line["unread"]; - - $rtl_content = sql_bool_to_bool($line["rtl_content"]); - - if ($rtl_content) { - $rtl_tag = "dir=\"RTL\""; - } else { - $rtl_tag = ""; - } - - $cat_id = $line["cat_id"]; - - $tmp_category = $line["category"]; - - if (!$tmp_category) { - $tmp_category = "Uncategorized"; - } - - // $class = ($lnum % 2) ? "even" : "odd"; - - if ($line["last_error"]) { - $class = "error"; - } else { - $class = "feed"; - } - - if ($category != $tmp_category && get_pref($link, 'ENABLE_FEED_CATS')) { - - if ($category) { - print "
    "; - } - - $category = $tmp_category; - - $collapsed = $line["collapsed"]; - - // workaround for NULL category - if ($category == "Uncategorized") { - $collapsed = get_pref($link, "_COLLAPSED_UNCAT"); - } - - if ($collapsed == "t" || $collapsed == "1") { - $holder_class = "invisible"; - $ellipsis = "..."; - } else { - $holder_class = "feedCatHolder"; - $ellipsis = ""; - } - - if ($cat_id) { - $cat_id_qpart = "cat_id = '$cat_id'"; - } else { - $cat_id_qpart = "cat_id IS NULL"; - } - - $cat_id = sprintf("%d", $cat_id); - $cat_unread = getCategoryUnread($link, $cat_id); - - if ($cat_unread > 0) { - $catctr_class = ""; - } else { - $catctr_class = "invisible"; - } - - print "
  • - $tmp_category - - ($cat_unread)$ellipsis -
  • "; - - print "
  • -
      "; - } - - printMobileFeedEntry($feed_id, $class, $feed, $unread, - false, $link, $rtl_content); - - ++$lnum; - } - - } else { - // tags - - $result = db_query($link, "SELECT tag_name,SUM((SELECT COUNT(int_id) - FROM ttrss_user_entries WHERE int_id = post_int_id - AND unread = true)) AS count FROM ttrss_tags - WHERE owner_uid = '".$_SESSION['uid']."' GROUP BY tag_name ORDER BY tag_name"); - - $tags = array(); - - while ($line = db_fetch_assoc($result)) { - $tags[$line["tag_name"]] += $line["count"]; - } - - foreach (array_keys($tags) as $tag) { - - $unread = $tags[$tag]; - - $class = "tag"; - - printMobileFeedEntry($tag, $class, $tag, $unread, - "../images/tag.png", $link); - - } - - - } - } - - function printMobileFeedEntry($feed_id, $class, $feed_title, $unread, $icon_file, $link, - $rtl_content = false) { - - if (!$feed_title) $feed_title = getFeedTitle($link, $feed_id, false); - if (!$unread) $unread = getFeedUnread($link, $feed_id); - - if ($unread > 0) $class .= "Unread"; - - if (!$icon_file) $icon_file = "../../" . getFeedIcon($feed_id); - - if (file_exists($icon_file) && filesize($icon_file) > 0) { - $feed_icon = ""; - } else { - $feed_icon = ""; - } - - if ($rtl_content) { - $rtl_tag = "dir=\"rtl\""; - } else { - $rtl_tag = "dir=\"ltr\""; - } - - $feed = "$feed_title"; - - print "
    • "; - print "$feed_icon"; - print "$feed "; - - if ($unread != 0) { - print "($unread)"; - } - - print "
    • "; - - } - - function render_headlines($link) { - - $feed = db_escape_string($_GET["id"]); - $limit = db_escape_string($_GET["limit"]); - $view_mode = db_escape_string($_GET["viewmode"]); - $cat_view = db_escape_string($_GET["cat"]); - $subop = $_GET["subop"]; - $catchup_op = $_GET["catchup_op"]; - - if (!$view_mode) { - if ($_SESSION["mobile:viewmode"]) { - $view_mode = $_SESSION["mobile:viewmode"]; - } else { - $view_mode = "adaptive"; - } - } - - $_SESSION["mobile:viewmode"] = $view_mode; - - if (!$limit) $limit = 30; - if (!$feed) $feed = 0; - - if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) { - - $result = db_query($link, "SELECT rtl_content FROM ttrss_feeds - WHERE id = '$feed' AND owner_uid = " . $_SESSION["uid"]); - - if (db_num_rows($result) == 1) { - $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content")); - } else { - $rtl_content = false; - } - - if ($rtl_content) { - $rtl_tag = "dir=\"RTL\""; - } else { - $rtl_tag = ""; - } - } else { - $rtl_content = false; - $rtl_tag = ""; - } - - print "
      "; - - if ($subop == "ForceUpdate" && sprintf("%d", $feed) > 0) { - update_generic_feed($link, $feed, $cat_view, true); - } - - if ($subop == "MarkAllRead" || $catchup_op == "feed") { - catchup_feed($link, $feed, $cat_view); - } - - if ($catchup_op == "selection") { - if (is_array($_GET["sel_ids"])) { - $ids_to_mark = array_keys($_GET["sel_ids"]); - if ($ids_to_mark) { - foreach ($ids_to_mark as $id) { - db_query($link, "UPDATE ttrss_user_entries SET - unread = false,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - } - } - } - } - - if ($subop == "MarkPageRead" || $catchup_op == "page") { - $ids_to_mark = $_SESSION["last_page_ids.$feed"]; - - if ($ids_to_mark) { - - foreach ($ids_to_mark as $id) { - db_query($link, "UPDATE ttrss_user_entries SET - unread = false,last_read = NOW() - WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]); - } - } - } - - - /// START ///////////////////////////////////////////////////////////////////////////////// - - $search = db_escape_string($_GET["query"]); - $search_mode = db_escape_string($_GET["search_mode"]); - $match_on = db_escape_string($_GET["match_on"]); - - if (!$match_on) { - $match_on = "both"; - } - - $real_offset = $offset * $limit; - - if ($_GET["debug"]) $timing_info = print_checkpoint("H0", $timing_info); - - $qfh_ret = queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, - $search, $search_mode, $match_on, false, $real_offset); - - if ($_GET["debug"]) $timing_info = print_checkpoint("H1", $timing_info); - - $result = $qfh_ret[0]; - $feed_title = $qfh_ret[1]; - $feed_site_url = $qfh_ret[2]; - $last_error = $qfh_ret[3]; - - /// STOP ////////////////////////////////////////////////////////////////////////////////// - - if (!$result) { - print "
      ". - __("Could not display feed (query failed). Please check label match syntax or local configuration."). - "
      "; - return; - } - - print "
      "; - # if (!$cat_view && file_exists("../icons/$feed.ico") && filesize("../icons/$feed.ico") > 0) { - # print ""; - # } - - print "$feed_title ("; - print "".__("Back").", "; - print "".__("Search").", "; - print "".__("Update").""; - -# print "Mark as read: "; -# print "Page, "; -# print "Feed"; - - print ")"; - - print " " . __('View:'); - - print "
      "; - - /* print ""; */ - - $sel_values = array( - "adaptive" => __("Adaptive"), - "all_articles" => __("All Articles"), - "unread" => __("Unread"), - "marked" => __("Starred")); - - print_select_hash("viewmode", $view_mode, $sel_values); - - print " - - - "; - print "
      "; - - print "
      "; - - if (db_num_rows($result) > 0) { - - print "
      "; - print ""; - print ""; - print ""; - - print "
        "; - - $page_art_ids = array(); - - $lnum = 0; - - error_reporting (DEFAULT_ERROR_LEVEL); - - $num_unread = 0; - - while ($line = db_fetch_assoc($result)) { - - $class = ($lnum % 2) ? "even" : "odd"; - - $id = $line["id"]; - $feed_id = $line["feed_id"]; - - array_push($page_art_ids, $id); - - if ($line["last_read"] == "" && - ($line["unread"] != "t" && $line["unread"] != "1")) { - - $update_pic = "\"".__("Updated")."\""; - } else { - $update_pic = "\"".__("Updated")."\""; - } - - if ($line["unread"] == "t" || $line["unread"] == "1") { - $class .= "Unread"; - ++$num_unread; - $is_unread = true; - } else { - $is_unread = false; - } - - if ($line["marked"] == "t" || $line["marked"] == "1") { - $marked_pic = "\"S\""; - } else { - $marked_pic = "\"s\""; - } - - if ($line["published"] == "t" || $line["published"] == "1") { - $published_pic = "\"P\""; - } else { - $published_pic = "\"p\""; - } - - $content_link = "" . - $line["title"] . ""; - - $updated_fmt = make_local_datetime($link, $line['updated'], false); - - print "
      • "; - - print ""; - - print "$marked_pic"; - print "$published_pic"; - - print $content_link; - - if ($line["feed_title"]) { - print " (". - $line["feed_title"].")"; - } - - print " ($updated_fmt)"; - - print "
      • "; - - - ++$lnum; - } - - print "
      "; - - print "
      "; - - $_SESSION["last_page_ids.$feed"] = $page_art_ids; - -/* print "Page, "; - print "Feed
      "; */ - - print "Select: - ".__("All").", - ".__("Unread").", - ".__("None").", - ".__("Invert").""; - - print " "; - - print " - - "; - - print "
      "; - - } else { - print "
      No articles found.
      "; - } - - } - - function render_article($link) { - - $id = db_escape_string($_GET["id"]); - $feed_id = db_escape_string($_GET["feed"]); - $ret_feed_id = db_escape_string($_GET["ret_feed"]); - $cat_view = db_escape_string($_GET["cat"]); - - $result = db_query($link, "SELECT rtl_content FROM ttrss_feeds - WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]); - - if (db_num_rows($result) == 1) { - $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content")); - } else { - $rtl_content = false; - } - - if ($rtl_content) { - $rtl_tag = "dir=\"RTL\""; - $rtl_class = "RTL"; - } else { - $rtl_tag = ""; - $rtl_class = ""; - } - - $result = db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE ref_id = '$id' AND feed_id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]); - - $result = db_query($link, "SELECT title,link,content,feed_id,comments,int_id, - marked,published, - ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, - (SELECT icon_url FROM ttrss_feeds WHERE id = feed_id) as icon_url, - num_comments, - author - FROM ttrss_entries,ttrss_user_entries - WHERE id = '$id' AND ref_id = id"); - - if ($result) { - - $line = db_fetch_assoc($result); - - $num_comments = $line["num_comments"]; - $entry_comments = ""; - - if ($num_comments > 0) { - if ($line["comments"]) { - $comments_url = $line["comments"]; - } else { - $comments_url = $line["link"]; - } - $entry_comments = "$num_comments comments"; - } else { - if ($line["comments"] && $line["link"] != $line["comments"]) { - $entry_comments = "comments"; - } - } - - $tmp_result = db_query($link, "SELECT DISTINCT tag_name FROM - ttrss_tags WHERE post_int_id = " . $line["int_id"] . " - ORDER BY tag_name"); - - $tags_str = ""; - $f_tags_str = ""; - - $num_tags = 0; - - while ($tmp_line = db_fetch_assoc($tmp_result)) { - $num_tags++; - $tag = $tmp_line["tag_name"]; - $tag_str = "$tag, "; - $tags_str .= $tag_str; - } - - $tags_str = preg_replace("/, $/", "", $tags_str); - - $parsed_updated = date(get_pref($link, 'SHORT_DATE_FORMAT'), - strtotime($line["updated"])); - - print "
      "; - - # if (file_exists("../icons/$feed_id.ico") && filesize("../icons/$feed_id.ico") > 0) { - # print ""; - # } - - if (!$cat_view) { - $feed_title = getFeedTitle($link, $ret_feed_id); - } else { - $feed_title = getCategoryTitle($link, $ret_feed_id); - $feed_title_native = getFeedTitle($link, $feed_id); - } - - if ($feed_title_native) { - $feed_link = "$feed_title_native"; - $feed_link .= " in - $feed_title"; - } else { - $feed_link = "$feed_title"; - } - - $feedlist = "".__('Back to feedlist').""; - - print "" . - truncate_string($line["title"], 30) . ""; - print " $parsed_updated ($feed_link, $feedlist)"; - print "
      "; - - if ($num_tags > 0) { - print "
      ".__("Tags:")." $tags_str
      "; - } - - if ($line["marked"] == "t" || $line["marked"] == "1") { - $marked_pic = ""; - } else { - $marked_pic = ""; - } - - if ($line["published"] == "t" || $line["published"] == "1") { - $published_pic = ""; - } else { - $published_pic = ""; - } - - - print "
      "; - print "$marked_pic"; - print "$published_pic"; - // Mark unread - print ""; - print "
      "; - - print sanitize_rss($link, $line["content"], true);; - - } - - print ""; - } - - function render_search_form($link, $active_feed_id = false, $is_cat = false) { - - print "
      "; - - print __("Search")." - (".__("Go back").")
      "; - - print "
      "; - - print ""; - print ""; - print ""; - - print ""; - - print ""; - - print "
      ".__('Search:').""; - print "
      ".__('Where:').""; - - print "
      ".__('Match on:').""; - - $search_fields = array( - "title" => __("Title"), - "content" => __("Content"), - "both" => __("Title or content")); - - print_select_hash("match_on", 3, $search_fields); - - print "
      "; - - print ""; - - print "
      "; - - print "
      "; - } - - function toggleMarked($link, $ts_id) { - $result = db_query($link, "UPDATE ttrss_user_entries SET marked = NOT marked - WHERE ref_id = '$ts_id' AND owner_uid = " . $_SESSION["uid"]); - } - - function togglePublished($link, $tp_id) { - $result = db_query($link, "UPDATE ttrss_user_entries SET published = NOT published - WHERE ref_id = '$tp_id' AND owner_uid = " . $_SESSION["uid"]); - } - - function markUnread($link, $mu_id) { - $result = db_query($link, "UPDATE ttrss_user_entries SET unread = true - WHERE ref_id = '$mu_id' AND owner_uid = " . $_SESSION["uid"]); - } - -?> diff --git a/mobile/classic/index.php b/mobile/classic/index.php deleted file mode 100644 index db2215b24..000000000 --- a/mobile/classic/index.php +++ /dev/null @@ -1,114 +0,0 @@ - - - - Tiny Tiny RSS - - - - - - - -
      - -
      - - - diff --git a/mobile/classic/login_form.php b/mobile/classic/login_form.php deleted file mode 100644 index bf4f77eac..000000000 --- a/mobile/classic/login_form.php +++ /dev/null @@ -1,79 +0,0 @@ - - - Tiny Tiny RSS : Login - - - - - - - - - - - - -
      -
      Tiny Tiny RSS
      - -
      - - - - -
      - - - - - - - - - - -
      - -
      - -
      -
      -
      - - - - diff --git a/mobile/classic/logout.php b/mobile/classic/logout.php deleted file mode 100644 index b20cfc9a6..000000000 --- a/mobile/classic/logout.php +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/mobile/classic/mobile.css b/mobile/classic/mobile.css deleted file mode 100644 index 786639efd..000000000 --- a/mobile/classic/mobile.css +++ /dev/null @@ -1,216 +0,0 @@ -body { - padding : 0px; - margin : 0px; - font-family : sans-serif; -} - -h1 { - font-size : medium; -} - -h2 { - font-size : medium; - font-weight : bold; - border-width : 0px 0px 1px 0px; - border-style : solid; - border-color : #88b0ff; -} - -h3 { - font-size : medium; - font-weight : bold; - border-width : 0px 0px 1px 0px; - border-style : solid; - border-color : #e0e0e0; -} - -#heading { - font-size : small; - - border-width : 0px 0px 1px 0px; - border-style : solid; - border-color : #f0f0f0; - - color : #88b0ff; - font-weight : bold; -} - -#content { - background : white; -} - -#footer { - font-size : x-small; - color : gray; - margin-top : 5px; -} - -form { - padding : 0px; - margin : 0px; -} - -li.feedCatHolder { - display : inline; -} - -ul.feedList { - list-style-type : none; - margin : 0px; - padding : 0px; - -} - -ul.feedList li.feedCat { - margin : 0px; - padding : 0px; - color : #707070; -} - -ul.feedList li.feedCat a { - color : #707070; -} - -ul.feedList li.feedCat a:hover { - color : #4684ff; -} - -ul.feedCatList { - list-style-type : none; - margin : 0px 0px 0px 1em; - padding : 0px; -} - -ul.feedCatList li { - margin : 0px; - padding : 0px; - color : black; -} - -ul.feedList li { - margin : 0px; -} - -hr { - border-width : 0px 0px 1px 0px; - border-style : dashed; - border-color : #e0e0e0; -} - -ul.headlines a, ul.feedList a { - color : black; -} - -a { - color : #4684ff; - text-decoration : none; -} - -a:hover { - color : black; -} - -ul.feedList img, img.tinyFeedIcon { - margin : 0px 3px 0px 0px; - width : 16px; - height : 16px; - border-width : 0px; -} - -ul.feedlist li.feedUnread, -ul.feedlist li.errorUnread, -ul.feedlist li.labelUnread, -ul.feedlist li.virtUnread, -ul.feedlist li.tagUnread { - font-weight : bold; -} - -.even { -/* background-color : #9bbdff; */ - border-width : 0px 0px 1px 0px; - border-color : #88b0ff; - border-style : solid; - background-color : #cbddff; -} - -.odd { - border-width : 0px 0px 1px 0px; - border-color : #88b0ff; - border-style : solid; -} - -.evenUnread { - border-width : 0px 0px 1px 0px; - border-color : #88b0ff; - border-style : solid; -/* background-color : #9bbdff; */ - font-weight : bold; - background-color : #cbddff; -} - -.oddUnread { - border-width : 0px 0px 1px 0px; - border-color : #88b0ff; - border-style : solid; - font-weight : bold; -} - -.evenSelected, .oddSelected, .evenUnreadSelected, .oddUnreadSelected { - background-color : #fff7d5; - border-width : 0px 0px 1px 0px; - border-color : #88b0ff; - border-style : solid; -} - -.evenUnreadSelected, .oddUnreadSelected { - font-weight : bold; -} - -.invisible { - display : none; -} - -ul.headlines { - list-style-type : none; - margin : 0px; - padding : 0px; -} - -ul.headlines span.hlUpdated { - color : gray; -} - -ul.headlines img.feedIcon { - width : 25px; - text-align : center; -} - -ul.headlines img.marked { - border-width : 0px; -} - -div.postStarOps img.marked { - border-width : 0px; -} - -div.postTags { - color : gray; - font-size : small; -} - -div.footerAddon { - font-size : small; -} - -.loginError { - color : red; - margin : 0.5em; -} - -form.searchForm { - margin : 5px; -} - -div.postStarOps { - float : right; -} diff --git a/mobile/classic/mobile.js b/mobile/classic/mobile.js deleted file mode 100644 index 599e00725..000000000 --- a/mobile/classic/mobile.js +++ /dev/null @@ -1,94 +0,0 @@ -function toggleSelectRow(cb, id) { - try { - - var row = document.getElementById("HROW-" + id); - var checked = cb.checked; - if (row) { - var unread = row.className.match("Unread"); - var new_classname = row.className; - - new_classname = new_classname.replace("Selected", ""); - new_classname = new_classname.replace("Unread", ""); - - if (unread) new_classname = new_classname + "Unread"; - if (checked) new_classname = new_classname + "Selected"; - - row.className = new_classname; - } - } catch (e) { - exception_error("toggleSelectRow", e); - } -} - -function selectHeadlines(mode) { - try { - - var cboxes = document.getElementsByTagName("INPUT"); - - for (var i = 0; i < cboxes.length; i++) { - if (cboxes[i].id && cboxes[i].id.match("HSCB-")) { - var row_id = cboxes[i].id.replace("HSCB-", "") - var row = document.getElementById("HROW-" + row_id); - - if (row) { - - if (mode == 1) { - cboxes[i].checked = true; - toggleSelectRow(cboxes[i], row_id); - } - - if (mode == 2) { - - var unread = row.className.match("Unread"); - - if (unread) { - cboxes[i].checked = true; - } else { - cboxes[i].checked = false; - } - } - - if (mode == 3) { - cboxes[i].checked = false; - } - - if (mode == 4) { - cboxes[i].checked = !cboxes[i].checked; - } - - toggleSelectRow(cboxes[i], row_id); - - } - - } - - } - - } catch (e) { - exception_error("selectHeadlines", e); - } -} - -function exception_error(location, e, silent) { - var msg; - - if (e.fileName) { - var base_fname = e.fileName.substring(e.fileName.lastIndexOf("/") + 1); - - msg = "Exception: " + e.name + ", " + e.message + - "\nFunction: " + location + "()" + - "\nLocation: " + base_fname + ":" + e.lineNumber; - - } else if (e.description) { - msg = "Exception: " + e.description + "\nFunction: " + location + "()"; - } else { - msg = "Exception: " + e + "\nFunction: " + location + "()"; - } - - debug("EXCEPTION: " + msg + ""); - - if (!silent) { - alert(msg); - } -} - diff --git a/mobile/feed.php b/mobile/feed.php index bd5804cca..f7b7443d3 100644 --- a/mobile/feed.php +++ b/mobile/feed.php @@ -6,13 +6,7 @@ define('MOBILE_VERSION', true); require_once "../config.php"; - require_once "functions.php"; - require_once "../functions.php"; - - require_once "../sessions.php"; - - require_once "../version.php"; - require_once "../db-prefs.php"; + require_once "mobile-functions.php"; $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); diff --git a/mobile/functions.php b/mobile/functions.php deleted file mode 100644 index 1063fca18..000000000 --- a/mobile/functions.php +++ /dev/null @@ -1,538 +0,0 @@ - 0; - } - - function render_flat_feed_list($link, $offset) { - $owner_uid = $_SESSION["uid"]; - $limit = 0; - - if (!$offset) $offset = 0; - - if (mobile_get_pref($link, "SORT_FEEDS_UNREAD")) { - $order_by = "unread DESC, title"; - } else { - $order_by = "title"; - } - - if ($limit > 0) { - $limit_qpart = "LIMIT $limit OFFSET $offset"; - } else { - $limit_qpart = ""; - } - - $result = db_query($link, "SELECT id, - title, - (SELECT COUNT(id) FROM ttrss_entries,ttrss_user_entries - WHERE feed_id = ttrss_feeds.id AND unread = true - AND ttrss_user_entries.ref_id = ttrss_entries.id - AND owner_uid = '$owner_uid') AS unread - FROM ttrss_feeds - WHERE - ttrss_feeds.owner_uid = '$owner_uid' - ORDER BY $order_by $limit_qpart"); - - if (!$offset) print '
        '; - - - // print "
      • ".__('Actions...')."
      • "; - - $num_feeds = 0; - - while ($line = db_fetch_assoc($result)) { - $id = $line["id"]; - $unread = $line["unread"]; - - // $unread = rand(0, 100); - - if ($unread > 0) { - $line["title"] = $line["title"] . " ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if (mobile_feed_has_icon($id)) { - $icon_url = "../".ICONS_URL."/$id.ico"; - } else { - $icon_url = "../images/blank_icon.gif"; - } - - if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { - print "
      • " . - "". - $line["title"] . "
      • "; - } - - ++$num_feeds; - } - -/* $next_offset = $offset + $num_feeds; - - print "
      • Show more feeds...
      • "; */ - - if (!$offset) print "
      "; - - } - - function render_category($link, $cat_id, $offset) { - $owner_uid = $_SESSION["uid"]; - - if ($cat_id >= 0) { - - if ($cat_id != 0) { - $cat_query = "cat_id = '$cat_id'"; - } else { - $cat_query = "cat_id IS NULL"; - } - - if (mobile_get_pref($link, "SORT_FEEDS_UNREAD")) { - $order_by = "unread DESC, title"; - } else { - $order_by = "title"; - } - - $result = db_query($link, "SELECT id, - title, - (SELECT COUNT(id) FROM ttrss_entries,ttrss_user_entries - WHERE feed_id = ttrss_feeds.id AND unread = true - AND ttrss_user_entries.ref_id = ttrss_entries.id - AND owner_uid = '$owner_uid') as unread - FROM ttrss_feeds - WHERE - ttrss_feeds.owner_uid = '$owner_uid' AND - $cat_query - ORDER BY $order_by"); - - $title = getCategoryTitle($link, $cat_id); - - print "
        "; - - // print "
      • ".__('Actions...')."
      • "; - - while ($line = db_fetch_assoc($result)) { - $id = $line["id"]; - $unread = $line["unread"]; - - // $unread = rand(0, 100); - - if ($unread > 0) { - $line["title"] = $line["title"] . " ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if (mobile_feed_has_icon($id)) { - $icon_url = "../".ICONS_URL."/$id.ico"; - } else { - $icon_url = "../images/blank_icon.gif"; - } - - if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { - print "
      • " . - "". - $line["title"] . "
      • "; - } - } - - print "
      "; - } else if ($cat_id == -1) { - - $title = __('Special'); - - print "
        "; - - foreach (array(-4, -3, -1, -2, 0) as $id) { - $title = getFeedTitle($link, $id); - $unread = getFeedUnread($link, $id, false); - $icon = getFeedIcon($id); - - if ($unread > 0) { - $title = $title . " ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { - print "
      • - - $title
      • "; - } - } - - print "
      "; - } else if ($cat_id == -2) { - - $title = __('Labels'); - - print "
        "; - - $result = db_query($link, "SELECT id, caption FROM ttrss_labels2 - WHERE owner_uid = '$owner_uid'"); - - $label_data = array(); - - while ($line = db_fetch_assoc($result)) { - - $id = -$line["id"] - 11; - - $unread = getFeedUnread($link, $id); - $title = $line["caption"]; - - if ($unread > 0) { - $title = $title . " ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { - print "
      • - $title
      • "; - } - } - print "
      "; - } - } - - function render_categories_list($link) { - $owner_uid = $_SESSION["uid"]; - - $cat_browse = mobile_get_pref($link, "BROWSE_CATS"); - - print '
        '; - -// print "
      • Search...
      • "; - - foreach (array(-1, -2) as $id) { - $title = getCategoryTitle($link, $id); - $unread = getFeedUnread($link, $id, true); - if ($unread > 0) { - $title = $title . " ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if ($cat_browse) - print "
      • $title
      • "; - else - print "
      • $title
      • "; - } - - $result = db_query($link, "SELECT - ttrss_feed_categories.id, - ttrss_feed_categories.title, - COUNT(ttrss_feeds.id) AS num_feeds - FROM ttrss_feed_categories, ttrss_feeds - WHERE ttrss_feed_categories.owner_uid = $owner_uid - AND ttrss_feed_categories.id = cat_id - GROUP BY ttrss_feed_categories.id, - ttrss_feed_categories.title - ORDER BY ttrss_feed_categories.title"); - - while ($line = db_fetch_assoc($result)) { - - if ($line["num_feeds"] > 0) { - - $unread = getFeedUnread($link, $line["id"], true); - $id = $line["id"]; - - if ($unread > 0) { - $line["title"] = $line["title"] . " ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { - - if ($cat_browse) - print "
      • " . - $line["title"] . "
      • "; - else - print "
      • ". - $line["title"] . "
      • "; - } - } - } - - - $result = db_query($link, "SELECT COUNT(*) AS nf FROM ttrss_feeds WHERE - cat_id IS NULL and owner_uid = '$owner_uid'"); - - $num_feeds = db_fetch_result($result, 0, "nf"); - - if ($num_feeds > 0) { - $unread = getFeedUnread($link, 0, true); - $title = "Uncategorized"; - - if ($unread > 0) { - $title = "$title ($unread)"; - $class = ''; - } else { - $class = 'oldItem'; - } - - if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { - if ($cat_browse) - print "
      • $title
      • "; - else - print "
      • $title
      • "; - - } - } - - print "
      "; - } - - function render_headlines_list($link, $feed_id, $cat_id, $offset, $search, - $is_cat = false) { - - $feed_id = $feed_id; - $limit = 15; - $filter = ''; - - if (!mobile_get_pref($link, "HIDE_READ")) - $view_mode = "all_articles"; - else - $view_mode = 'adaptive'; - - if ($search) { - $search_mode = 'this_feed'; - $match_on = 'both'; - } else { - $search_mode = ''; - $match_on = ''; - } - - $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit, - $view_mode, $is_cat, $search, $search_mode, $match_on, - "score DESC, date_entered DESC", $offset); - - $result = $qfh_ret[0]; - $feed_title = $qfh_ret[1]; - - if (!$offset) { - - print "
      - - - - -
      -

      Search

      - Cancel - Search - - - -
      -
      "; - - if ($cat_id) { - $cat_title = getCategoryTitle($link, $cat_id); - - print "
        "; - } else { - print "
          "; - } - - print "
        • Search...
        • "; - } - - $num_headlines = 0; - - while ($line = db_fetch_assoc($result)) { - $id = $line["id"]; - $real_feed_id = $line["feed_id"]; - - if (sql_bool_to_bool($line["unread"])) { - $class = ''; - } else { - $class = 'oldItem'; - } - - if (mobile_feed_has_icon($real_feed_id)) { - $icon_url = "../".ICONS_URL."/$real_feed_id.ico"; - } else { - $icon_url = "../images/blank_icon.gif"; - } - - print "
        • - "; - print $line["title"]; - print "
        • "; - - ++$num_headlines; - - } - - if ($num_headlines == 0 && $search) { - $articles_url = "feed.php?id=$feed_id&cat=$cat_id&skip=$next_offset"; - - print "
        • " . __("Nothing found (click to reload feed).") . "
        • "; - - } - -// print "Next $limit articles..."; - - $next_offset = $offset + $num_headlines; - $num_unread = getFeedUnread($link, $feed_id, $is_cat); - - /* FIXME needs normal implementation */ - - if ($num_headlines > 0 && ($num_unread == 0 || $num_unread > $next_offset)) { - - if ($is_cat) { - $articles_url = "feed.php?id=$feed_id&skip=$next_offset". - "&search=$search&is_cat=true"; - } else { - $articles_url = "feed.php?id=$feed_id&cat=$cat_id&skip=$next_offset". - "&search=$search"; - } - - print "
        • Get more articles...
        • "; - } - - if (!$offset) print "
        "; - - } - - function render_article($link, $id, $feed_id, $cat_id, $is_cat) { - - $query = "SELECT title,link,content,feed_id,comments,int_id, - marked,unread,published, - ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, - author - FROM ttrss_entries,ttrss_user_entries - WHERE id = '$id' AND ref_id = id AND owner_uid = " . - $_SESSION["uid"] ; - - $result = db_query($link, $query); - - if (db_num_rows($result) != 0) { - - $line = db_fetch_assoc($result); - - $tmp_result = db_query($link, "UPDATE ttrss_user_entries - SET unread = false,last_read = NOW() - WHERE ref_id = '$id' - AND owner_uid = " . $_SESSION["uid"]); - - $updated_fmt = make_local_datetime($link, $line['updated'], false); - - $title = $line["title"]; - $article_link = $line["link"]; - - if (!$is_cat) - $feed_title = getFeedTitle($link, $feed_id); - else - $feed_title = getCategoryTitle($link, $feed_id); - - print "
        "; - - if ($line['feed_id'] != $feed_id) { - $real_feed_title = getFeedTitle($link, $line['feed_id']); - $real_feed_id = $line['feed_id']; - $feed_link = "($real_feed_title)"; - } -// print "
        "; - - print "
        ($updated_fmt)
        "; - - print "

        $title $feed_link

        "; - - print "
        "; - -/* print "
        "; - print ""; - print "
        "; */ - - $is_starred = (sql_bool_to_bool($line["marked"])) ? "true" : "false"; - $is_published = (sql_bool_to_bool($line["published"])) ? "true" : "false"; - - //print "
        "; - //print ""; - //print ""; - //print "
        "; - -// print "
        "; - - $content = sanitize_rss($link, $line["content"]); - $content = preg_replace("/href=/i", "target=\"_blank\" href=", $content); - - if (!mobile_get_pref($link, "SHOW_IMAGES")) { - $content = preg_replace('/]+>/is', '', $content); - } - - print "

        $content

        "; - - print ""; - - print "
        "; - - print "
        - -
        ONOFF
        -
        "; - - print "
        - -
        ONOFF
        -
        "; - - print "
        - -
        ONOFF
        -
        "; - - - print "
        "; - - print "
        "; - - } - } -?> diff --git a/mobile/home.php b/mobile/home.php index 2830f5530..71194da0a 100644 --- a/mobile/home.php +++ b/mobile/home.php @@ -6,13 +6,7 @@ define('MOBILE_VERSION', true); require_once "../config.php"; - require_once "functions.php"; - require_once "../functions.php"; - - require_once "../sessions.php"; - - require_once "../version.php"; - require_once "../db-prefs.php"; + require_once "mobile-functions.php"; $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); @@ -24,7 +18,7 @@ $offset = (int) db_escape_string($_REQUEST["skip"]); if ($use_cats) { - render_categories_list($link); + render_categories_list($link); } else { render_flat_feed_list($link, $offset); } diff --git a/mobile/index.php b/mobile/index.php index b0b374497..edbf586e0 100644 --- a/mobile/index.php +++ b/mobile/index.php @@ -6,13 +6,7 @@ define('MOBILE_VERSION', true); require_once "../config.php"; - require_once "functions.php"; - require_once "../functions.php"; - - require_once "../sessions.php"; - - require_once "../version.php"; - require_once "../db-prefs.php"; + require_once "mobile-functions.php"; $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); @@ -73,12 +67,12 @@ Preferences
  • - 0; + } + + function render_flat_feed_list($link, $offset) { + $owner_uid = $_SESSION["uid"]; + $limit = 0; + + if (!$offset) $offset = 0; + + if (mobile_get_pref($link, "SORT_FEEDS_UNREAD")) { + $order_by = "unread DESC, title"; + } else { + $order_by = "title"; + } + + if ($limit > 0) { + $limit_qpart = "LIMIT $limit OFFSET $offset"; + } else { + $limit_qpart = ""; + } + + $result = db_query($link, "SELECT id, + title, + (SELECT COUNT(id) FROM ttrss_entries,ttrss_user_entries + WHERE feed_id = ttrss_feeds.id AND unread = true + AND ttrss_user_entries.ref_id = ttrss_entries.id + AND owner_uid = '$owner_uid') AS unread + FROM ttrss_feeds + WHERE + ttrss_feeds.owner_uid = '$owner_uid' + ORDER BY $order_by $limit_qpart"); + + if (!$offset) print '
      '; + + + // print "
    • ".__('Actions...')."
    • "; + + $num_feeds = 0; + + while ($line = db_fetch_assoc($result)) { + $id = $line["id"]; + $unread = $line["unread"]; + + // $unread = rand(0, 100); + + if ($unread > 0) { + $line["title"] = $line["title"] . " ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if (mobile_feed_has_icon($id)) { + $icon_url = "../".ICONS_URL."/$id.ico"; + } else { + $icon_url = "../images/blank_icon.gif"; + } + + if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { + print "
    • " . + "". + $line["title"] . "
    • "; + } + + ++$num_feeds; + } + +/* $next_offset = $offset + $num_feeds; + + print "
    • Show more feeds...
    • "; */ + + if (!$offset) print "
    "; + + } + + function render_category($link, $cat_id, $offset) { + $owner_uid = $_SESSION["uid"]; + + if ($cat_id >= 0) { + + if ($cat_id != 0) { + $cat_query = "cat_id = '$cat_id'"; + } else { + $cat_query = "cat_id IS NULL"; + } + + if (mobile_get_pref($link, "SORT_FEEDS_UNREAD")) { + $order_by = "unread DESC, title"; + } else { + $order_by = "title"; + } + + $result = db_query($link, "SELECT id, + title, + (SELECT COUNT(id) FROM ttrss_entries,ttrss_user_entries + WHERE feed_id = ttrss_feeds.id AND unread = true + AND ttrss_user_entries.ref_id = ttrss_entries.id + AND owner_uid = '$owner_uid') as unread + FROM ttrss_feeds + WHERE + ttrss_feeds.owner_uid = '$owner_uid' AND + $cat_query + ORDER BY $order_by"); + + $title = getCategoryTitle($link, $cat_id); + + print "
      "; + + // print "
    • ".__('Actions...')."
    • "; + + while ($line = db_fetch_assoc($result)) { + $id = $line["id"]; + $unread = $line["unread"]; + + // $unread = rand(0, 100); + + if ($unread > 0) { + $line["title"] = $line["title"] . " ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if (mobile_feed_has_icon($id)) { + $icon_url = "../".ICONS_URL."/$id.ico"; + } else { + $icon_url = "../images/blank_icon.gif"; + } + + if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { + print "
    • " . + "". + $line["title"] . "
    • "; + } + } + + print "
    "; + } else if ($cat_id == -1) { + + $title = __('Special'); + + print "
      "; + + foreach (array(-4, -3, -1, -2, 0) as $id) { + $title = getFeedTitle($link, $id); + $unread = getFeedUnread($link, $id, false); + $icon = getFeedIcon($id); + + if ($unread > 0) { + $title = $title . " ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { + print "
    • + + $title
    • "; + } + } + + print "
    "; + } else if ($cat_id == -2) { + + $title = __('Labels'); + + print "
      "; + + $result = db_query($link, "SELECT id, caption FROM ttrss_labels2 + WHERE owner_uid = '$owner_uid'"); + + $label_data = array(); + + while ($line = db_fetch_assoc($result)) { + + $id = -$line["id"] - 11; + + $unread = getFeedUnread($link, $id); + $title = $line["caption"]; + + if ($unread > 0) { + $title = $title . " ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { + print "
    • + $title
    • "; + } + } + print "
    "; + } + } + + function render_categories_list($link) { + $owner_uid = $_SESSION["uid"]; + + $cat_browse = mobile_get_pref($link, "BROWSE_CATS"); + + print '
      '; + +// print "
    • Search...
    • "; + + foreach (array(-1, -2) as $id) { + $title = getCategoryTitle($link, $id); + $unread = getFeedUnread($link, $id, true); + if ($unread > 0) { + $title = $title . " ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if ($cat_browse) + print "
    • $title
    • "; + else + print "
    • $title
    • "; + } + + $result = db_query($link, "SELECT + ttrss_feed_categories.id, + ttrss_feed_categories.title, + COUNT(ttrss_feeds.id) AS num_feeds + FROM ttrss_feed_categories, ttrss_feeds + WHERE ttrss_feed_categories.owner_uid = $owner_uid + AND ttrss_feed_categories.id = cat_id + GROUP BY ttrss_feed_categories.id, + ttrss_feed_categories.title + ORDER BY ttrss_feed_categories.title"); + + while ($line = db_fetch_assoc($result)) { + + if ($line["num_feeds"] > 0) { + + $unread = getFeedUnread($link, $line["id"], true); + $id = $line["id"]; + + if ($unread > 0) { + $line["title"] = $line["title"] . " ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { + + if ($cat_browse) + print "
    • " . + $line["title"] . "
    • "; + else + print "
    • ". + $line["title"] . "
    • "; + } + } + } + + + $result = db_query($link, "SELECT COUNT(*) AS nf FROM ttrss_feeds WHERE + cat_id IS NULL and owner_uid = '$owner_uid'"); + + $num_feeds = db_fetch_result($result, 0, "nf"); + + if ($num_feeds > 0) { + $unread = getFeedUnread($link, 0, true); + $title = "Uncategorized"; + + if ($unread > 0) { + $title = "$title ($unread)"; + $class = ''; + } else { + $class = 'oldItem'; + } + + if ($unread > 0 || !mobile_get_pref($link, "HIDE_READ")) { + if ($cat_browse) + print "
    • $title
    • "; + else + print "
    • $title
    • "; + + } + } + + print "
    "; + } + + function render_headlines_list($link, $feed_id, $cat_id, $offset, $search, + $is_cat = false) { + + $feed_id = $feed_id; + $limit = 15; + $filter = ''; + + if (!mobile_get_pref($link, "HIDE_READ")) + $view_mode = "all_articles"; + else + $view_mode = 'adaptive'; + + if ($search) { + $search_mode = 'this_feed'; + $match_on = 'both'; + } else { + $search_mode = ''; + $match_on = ''; + } + + $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit, + $view_mode, $is_cat, $search, $search_mode, $match_on, + "score DESC, date_entered DESC", $offset); + + $result = $qfh_ret[0]; + $feed_title = $qfh_ret[1]; + + if (!$offset) { + + print "
    + + + + +
    +

    Search

    + Cancel + Search + + + +
    +
    "; + + if ($cat_id) { + $cat_title = getCategoryTitle($link, $cat_id); + + print "
      "; + } else { + print "
        "; + } + + print "
      • Search...
      • "; + } + + $num_headlines = 0; + + while ($line = db_fetch_assoc($result)) { + $id = $line["id"]; + $real_feed_id = $line["feed_id"]; + + if (sql_bool_to_bool($line["unread"])) { + $class = ''; + } else { + $class = 'oldItem'; + } + + if (mobile_feed_has_icon($real_feed_id)) { + $icon_url = "../".ICONS_URL."/$real_feed_id.ico"; + } else { + $icon_url = "../images/blank_icon.gif"; + } + + print "
      • + "; + print $line["title"]; + print "
      • "; + + ++$num_headlines; + + } + + if ($num_headlines == 0 && $search) { + $articles_url = "feed.php?id=$feed_id&cat=$cat_id&skip=$next_offset"; + + print "
      • " . __("Nothing found (click to reload feed).") . "
      • "; + + } + +// print "Next $limit articles..."; + + $next_offset = $offset + $num_headlines; + $num_unread = getFeedUnread($link, $feed_id, $is_cat); + + /* FIXME needs normal implementation */ + + if ($num_headlines > 0 && ($num_unread == 0 || $num_unread > $next_offset)) { + + if ($is_cat) { + $articles_url = "feed.php?id=$feed_id&skip=$next_offset". + "&search=$search&is_cat=true"; + } else { + $articles_url = "feed.php?id=$feed_id&cat=$cat_id&skip=$next_offset". + "&search=$search"; + } + + print "
      • Get more articles...
      • "; + } + + if (!$offset) print "
      "; + + } + + function render_article($link, $id, $feed_id, $cat_id, $is_cat) { + + $query = "SELECT title,link,content,feed_id,comments,int_id, + marked,unread,published, + ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, + author + FROM ttrss_entries,ttrss_user_entries + WHERE id = '$id' AND ref_id = id AND owner_uid = " . + $_SESSION["uid"] ; + + $result = db_query($link, $query); + + if (db_num_rows($result) != 0) { + + $line = db_fetch_assoc($result); + + $tmp_result = db_query($link, "UPDATE ttrss_user_entries + SET unread = false,last_read = NOW() + WHERE ref_id = '$id' + AND owner_uid = " . $_SESSION["uid"]); + + $updated_fmt = make_local_datetime($link, $line['updated'], false); + + $title = $line["title"]; + $article_link = $line["link"]; + + if (!$is_cat) + $feed_title = getFeedTitle($link, $feed_id); + else + $feed_title = getCategoryTitle($link, $feed_id); + + print "
      "; + + if ($line['feed_id'] != $feed_id) { + $real_feed_title = getFeedTitle($link, $line['feed_id']); + $real_feed_id = $line['feed_id']; + $feed_link = "($real_feed_title)"; + } +// print "
      "; + + print "
      ($updated_fmt)
      "; + + print "

      $title $feed_link

      "; + + print "
      "; + +/* print "
      "; + print ""; + print "
      "; */ + + $is_starred = (sql_bool_to_bool($line["marked"])) ? "true" : "false"; + $is_published = (sql_bool_to_bool($line["published"])) ? "true" : "false"; + + //print "
      "; + //print ""; + //print ""; + //print "
      "; + +// print "
      "; + + $content = sanitize_rss($link, $line["content"]); + $content = preg_replace("/href=/i", "target=\"_blank\" href=", $content); + + if (!mobile_get_pref($link, "SHOW_IMAGES")) { + $content = preg_replace('/]+>/is', '', $content); + } + + print "

      $content

      "; + + print ""; + + print "
      "; + + print "
      + +
      ONOFF
      +
      "; + + print "
      + +
      ONOFF
      +
      "; + + print "
      + +
      ONOFF
      +
      "; + + + print "
      "; + + print "
      "; + + } + } +?> diff --git a/mobile/prefs.php b/mobile/prefs.php index a5a9403e9..59c2d04ae 100644 --- a/mobile/prefs.php +++ b/mobile/prefs.php @@ -6,13 +6,7 @@ define('MOBILE_VERSION', true); require_once "../config.php"; - require_once "functions.php"; - require_once "../functions.php"; - - require_once "../sessions.php"; - - require_once "../version.php"; - require_once "../db-prefs.php"; + require_once "mobile-functions.php"; $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); diff --git a/opml.php b/opml.php index 991f93e0c..32432a9a7 100644 --- a/opml.php +++ b/opml.php @@ -1,4 +1,6 @@ 0) { - - var ok = confirm(__("Remove selected labels?")); - - if (ok) { - notify_progress("Removing selected labels..."); - - var query = "?op=pref-labels&subop=remove&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - labellist_callback2(transport); - } }); - - } - } else { - alert(__("No labels are selected.")); - } - - return false; -} - -function removeSelectedUsers() { - - try { - - var sel_rows = getSelectedUsers(); - - if (sel_rows.length > 0) { - - var ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed.")); - - if (ok) { - notify_progress("Removing selected users..."); - - var query = "?op=pref-users&subop=remove&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - userlist_callback2(transport); - } }); - - } - - } else { - alert(__("No users are selected.")); - } - - } catch (e) { - exception_error("removeSelectedUsers", e); - } - - return false; -} - -function removeSelectedFilters() { - - try { - - var sel_rows = getSelectedFilters(); - - if (sel_rows.length > 0) { - - var ok = confirm(__("Remove selected filters?")); - - if (ok) { - notify_progress("Removing selected filters..."); - - var query = "?op=pref-filters&subop=remove&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - updateFilterList(); - } }); - } - } else { - alert(__("No filters are selected.")); - } - - } catch (e) { - exception_error("removeSelectedFilters", e); - } - - return false; -} - - -function removeSelectedFeeds() { - - try { - - var sel_rows = getSelectedFeeds(); - - if (sel_rows.length > 0) { - - var ok = confirm(__("Unsubscribe from selected feeds?")); - - if (ok) { - - notify_progress("Unsubscribing from selected feeds...", true); - - var query = "?op=pref-feeds&subop=remove&ids="+ - param_escape(sel_rows.toString()); - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - updateFeedList(); - } }); - } - - } else { - alert(__("No feeds are selected.")); - } - - } catch (e) { - exception_error("removeSelectedFeeds", e); - } - - return false; -} - -function clearSelectedFeeds() { - - var sel_rows = getSelectedFeeds(); - - if (sel_rows.length > 1) { - alert(__("Please select only one feed.")); - return; - } - - if (sel_rows.length > 0) { - - var ok = confirm(__("Erase all non-starred articles in selected feed?")); - - if (ok) { - notify_progress("Clearing selected feed..."); - clearFeedArticles(sel_rows[0]); - } - - } else { - - alert(__("No feeds are selected.")); - - } - - return false; -} - -function purgeSelectedFeeds() { - - var sel_rows = getSelectedFeeds(); - - if (sel_rows.length > 0) { - - var pr = prompt(__("How many days of articles to keep (0 - use default)?"), "0"); - - if (pr != undefined) { - notify_progress("Purging selected feed..."); - - var query = "?op=rpc&subop=purge&ids="+ - param_escape(sel_rows.toString()) + "&days=" + pr; - - console.log(query); - - new Ajax.Request("prefs.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - } }); - } - - } else { - - alert(__("No feeds are selected.")); - - } - - return false; -} - -function userEditCancel() { - closeInfoBox(); - return false; -} - -function userEditSave() { - - try { - - var login = document.forms["user_edit_form"].login.value; - - if (login.length == 0) { - alert(__("Login field cannot be blank.")); - return; - } - - notify_progress("Saving user..."); - - closeInfoBox(); - - var query = Form.serialize("user_edit_form"); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - userlist_callback2(transport); - } }); - - } catch (e) { - exception_error("userEditSave", e); - } - - return false; - -} - - -function editSelectedUser() { - var rows = getSelectedUsers(); - - if (rows.length == 0) { - alert(__("No users are selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select only one user.")); - return; - } - - notify(""); - - editUser(rows[0]); -} - -function resetSelectedUserPass() { - - try { - - var rows = getSelectedUsers(); - - if (rows.length == 0) { - alert(__("No users are selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select only one user.")); - return; - } - - var ok = confirm(__("Reset password of selected user?")); - - if (ok) { - notify_progress("Resetting password for selected user..."); - - var id = rows[0]; - - var query = "?op=pref-users&subop=resetPass&id=" + - param_escape(id); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - userlist_callback2(transport); - } }); - - } - - } catch (e) { - exception_error("resetSelectedUserPass", e); - } -} - -function selectedUserDetails() { - - try { - - var rows = getSelectedUsers(); - - if (rows.length == 0) { - alert(__("No users are selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select only one user.")); - return; - } - - notify_progress("Loading, please wait..."); - - var id = rows[0]; - - var query = "?op=pref-users&subop=user-details&id=" + id; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - infobox_callback2(transport); - } }); - } catch (e) { - exception_error("selectedUserDetails", e); - } -} - - -function editSelectedFilter() { - var rows = getSelectedFilters(); - - if (rows.length == 0) { - alert(__("No filters are selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select only one filter.")); - return; - } - - notify(""); - - editFilter(rows[0]); - -} - - -function editSelectedFeed() { - var rows = getSelectedFeeds(); - - if (rows.length == 0) { - alert(__("No feeds are selected.")); - return; - } - - if (rows.length > 1) { - return editSelectedFeeds(); - } - - notify(""); - - editFeed(rows[0], {}); - -} - -function editSelectedFeeds() { - - try { - var rows = getSelectedFeeds(); - - if (rows.length == 0) { - alert(__("No feeds are selected.")); - return; - } - - notify_progress("Loading, please wait..."); - - var query = "backend.php?op=pref-feeds&subop=editfeeds&ids=" + - param_escape(rows.toString()); - - console.log(query); - - if (dijit.byId("feedEditDlg")) - dijit.byId("feedEditDlg").destroyRecursive(); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - - notify(""); - - var dialog = new dijit.Dialog({ - id: "feedEditDlg", - title: __("Edit Multiple Feeds"), - style: "width: 600px", - getChildByName: function (name) { - var rv = null; - this.getChildren().each( - function(child) { - if (child.name == name) { - rv = child; - return; - } - }); - return rv; - }, - toggleField: function (checkbox, elem, label) { - this.getChildByName(elem).attr('disabled', !checkbox.checked); - - if ($(label)) - if (checkbox.checked) - $(label).removeClassName('insensitive'); - else - $(label).addClassName('insensitive'); - - }, - execute: function() { - if (this.validate() && confirm(__("Save changes to selected feeds?"))) { - var query = dojo.objectToQuery(this.attr('value')); - - /* Form.serialize ignores unchecked checkboxes */ - - if (!query.match("&rtl_content=") && - this.getChildByName('rtl_content').attr('disabled') == false) { - query = query + "&rtl_content=false"; - } - - if (!query.match("&private=") && - this.getChildByName('private').attr('disabled') == false) { - query = query + "&private=false"; - } - - try { - if (!query.match("&cache_images=") && - this.getChildByName('cache_images').attr('disabled') == false) { - query = query + "&cache_images=false"; - } - } catch (e) { } - - if (!query.match("&include_in_digest=") && - this.getChildByName('include_in_digest').attr('disabled') == false) { - query = query + "&include_in_digest=false"; - } - - if (!query.match("&always_display_enclosures=") && - this.getChildByName('always_display_enclosures').attr('disabled') == false) { - query = query + "&always_display_enclosures=false"; - } - - if (!query.match("&mark_unread_on_update=") && - this.getChildByName('mark_unread_on_update').attr('disabled') == false) { - query = query + "&mark_unread_on_update=false"; - } - - if (!query.match("&update_on_checksum_change=") && - this.getChildByName('update_on_checksum_change').attr('disabled') == false) { - query = query + "&update_on_checksum_change=false"; - } - - console.log(query); - - notify_progress("Saving data...", true); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - dialog.hide(); - updateFeedList(); - }}); - } - }, - content: transport.responseText}); - - dialog.show(); - - } }); - - } catch (e) { - exception_error("editSelectedFeeds", e); - } -} - -function piggie(enable) { - if (enable) { - console.log("I LOVEDED IT!"); - var piggie = $("piggie"); - - Element.show(piggie); - Position.Center(piggie); - Effect.Puff(piggie); - - } -} - -function opmlImportComplete(iframe) { - try { - if (!iframe.contentDocument.body.innerHTML) return false; - - notify(''); - - if (dijit.byId('opmlImportDlg')) - dijit.byId('opmlImportDlg').destroyRecursive(); - - var content = iframe.contentDocument.body.innerHTML; - - dialog = new dijit.Dialog({ - id: "opmlImportDlg", - title: __("OPML Import"), - style: "width: 600px", - onCancel: function() { - updateFeedList(); - }, - content: content}); - - dialog.show(); - - } catch (e) { - exception_error("opmlImportComplete", e); - } -} - -function opmlImport() { - - var opml_file = $("opml_file"); - - if (opml_file.value.length == 0) { - alert(__("Please choose an OPML file first.")); - return false; - } else { - notify_progress("Importing, please wait...", true); - return true; - } -} - -function updateFilterList() { - new Ajax.Request("backend.php", { - parameters: "?op=pref-filters", - onComplete: function(transport) { - filterlist_callback2(transport); - } }); -} - -function updateLabelList() { - new Ajax.Request("backend.php", { - parameters: "?op=pref-labels", - onComplete: function(transport) { - labellist_callback2(transport); - } }); -} - -function updatePrefsList() { - new Ajax.Request("backend.php", { - parameters: "?op=pref-prefs", - onComplete: function(transport) { - prefslist_callback2(transport); - } }); -} - -function selectTab(id, noupdate, subop) { - try { - if (!noupdate) { - notify_progress("Loading, please wait..."); - - if (id == "feedConfig") { - updateFeedList(); - } else if (id == "filterConfig") { - updateFilterList(); - } else if (id == "labelConfig") { - updateLabelList(); - } else if (id == "genConfig") { - updatePrefsList(); - } else if (id == "userConfig") { - updateUsersList(); - } - - var tab = dijit.byId(id + "Tab"); - dijit.byId("pref-tabs").selectChild(tab); - - } - - } catch (e) { - exception_error("selectTab", e); - } -} - -function init_second_stage() { - try { - - document.onkeydown = pref_hotkey_handler; - loading_set_progress(50); - notify(""); - - dojo.addOnLoad(function() { - var tab = getURLParam('tab'); - - if (tab) { - tab = dijit.byId(tab + "Tab"); - if (tab) dijit.byId("pref-tabs").selectChild(tab); - } - - var subop = getURLParam('subop'); - - if (subop == 'editFeed') { - var param = getURLParam('subopparam'); - - window.setTimeout('editFeed(' + param + ')', 100); - } - }); - - setTimeout("hotkey_prefix_timeout()", 5*1000); - - } catch (e) { - exception_error("init_second_stage", e); - } -} - -function init() { - - try { - dojo.registerModulePath("lib", ".."); - dojo.registerModulePath("fox", "../.."); - - dojo.require("lib.CheckBoxTree"); - dojo.require("fox.PrefFeedTree"); - dojo.require("fox.PrefFilterTree"); - dojo.require("fox.PrefLabelTree"); - - dojo.parser.parse(); - - dojo.addOnLoad(function() { - loading_set_progress(50); - - new Ajax.Request("backend.php", { - parameters: {op: "rpc", subop: "sanityCheck"}, - onComplete: function(transport) { - backend_sanity_check_callback(transport); - } }); - }); - - } catch (e) { - exception_error("init", e); - } -} - -function validatePrefsReset() { - try { - var ok = confirm(__("Reset to defaults?")); - - if (ok) { - - query = "?op=pref-prefs&subop=reset-config"; - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - var msg = transport.responseText; - if (msg.match("PREFS_THEME_CHANGED")) { - window.location.reload(); - } else { - notify_info(msg); - selectTab(); - } - } }); - - } - - } catch (e) { - exception_error("validatePrefsReset", e); - } - - return false; - -} - - -function pref_hotkey_handler(e) { - try { - if (e.target.nodeName == "INPUT") return; - - var keycode = false; - var shift_key = false; - - var cmdline = $('cmdline'); - - try { - shift_key = e.shiftKey; - } catch (e) { - - } - - if (window.event) { - keycode = window.event.keyCode; - } else if (e) { - keycode = e.which; - } - - var keychar = String.fromCharCode(keycode); - - if (keycode == 27) { // escape - if (Element.visible("hotkey_help_overlay")) { - Element.hide("hotkey_help_overlay"); - } - hotkey_prefix = false; - closeInfoBox(); - } - - if (keycode == 16) return; // ignore lone shift - if (keycode == 17) return; // ignore lone ctrl - - if ((keycode == 67 || keycode == 71) && !hotkey_prefix) { - hotkey_prefix = keycode; - - var date = new Date(); - var ts = Math.round(date.getTime() / 1000); - - hotkey_prefix_pressed = ts; - - cmdline.innerHTML = keychar; - Element.show(cmdline); - - console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar); - return; - } - - if (Element.visible("hotkey_help_overlay")) { - Element.hide("hotkey_help_overlay"); - } - - if (keycode == 13 || keycode == 27) { - seq = ""; - } else { - seq = seq + "" + keycode; - } - - /* Global hotkeys */ - - Element.hide(cmdline); - - if (!hotkey_prefix) { - - if ((keycode == 191 || keychar == '?') && shift_key) { // ? - if (!Element.visible("hotkey_help_overlay")) { - //Element.show("hotkey_help_overlay"); - Effect.Appear("hotkey_help_overlay", {duration : 0.3, to: 0.9}); - } else { - Element.hide("hotkey_help_overlay"); - } - return false; - } - - if (keycode == 191 || keychar == '/') { // / - var search_boxes = new Array("label_search", - "feed_search", "filter_search", "user_search", "feed_browser_search"); - - for (var i = 0; i < search_boxes.length; i++) { - var elem = $(search_boxes[i]); - if (elem) { - $(search_boxes[i]).focus(); - return false; - } - } - } - } - - /* Prefix c */ - - if (hotkey_prefix == 67) { // c - hotkey_prefix = false; - - if (keycode == 70) { // f - quickAddFilter(); - return false; - } - - if (keycode == 83) { // s - quickAddFeed(); - return false; - } - - if (keycode == 85) { // u - // no-op - } - - if (keycode == 67) { // c - editFeedCats(); - return false; - } - - if (keycode == 84 && shift_key) { // T - feedBrowser(); - return false; - } - - } - - /* Prefix g */ - - if (hotkey_prefix == 71) { // g - - hotkey_prefix = false; - - if (keycode == 49 && $("genConfigTab")) { // 1 - selectTab("genConfig"); - return false; - } - - if (keycode == 50 && $("feedConfigTab")) { // 2 - selectTab("feedConfig"); - return false; - } - - if (keycode == 51 && $("filterConfigTab")) { // 4 - selectTab("filterConfig"); - return false; - } - - if (keycode == 52 && $("labelConfigTab")) { // 5 - selectTab("labelConfig"); - return false; - } - - if (keycode == 53 && $("userConfigTab")) { // 6 - selectTab("userConfig"); - return false; - } - - if (keycode == 88) { // x - return gotoMain(); - } - - } - - if ($("piggie")) { - if (seq.match("8073717369")) { - seq = ""; - piggie(true); - } else { - piggie(false); - } - } - - if (hotkey_prefix) { - console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar); - } else { - console.log("KP: CODE=" + keycode + " CHAR=" + keychar); - } - - } catch (e) { - exception_error("pref_hotkey_handler", e); - } -} - -function editFeedCats() { - try { - var query = "backend.php?op=pref-feeds&subop=editCats"; - - if (dijit.byId("feedCatEditDlg")) - dijit.byId("feedCatEditDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "feedCatEditDlg", - title: __("Feed Categories"), - style: "width: 600px", - getSelectedCategories: function() { - return getSelectedTableRowIds("prefFeedCatList"); - }, - removeSelected: function() { - var sel_rows = this.getSelectedCategories(); - - if (sel_rows.length > 0) { - var ok = confirm(__("Remove selected categories?")); - - if (ok) { - notify_progress("Removing selected categories...", true); - - var query = "?op=pref-feeds&subop=editCats&action=remove&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - dialog.attr('content', transport.responseText); - updateFeedList(); - } }); - - } - - } else { - alert(__("No categories are selected.")); - } - }, - addCategory: function() { - if (this.validate()) { - notify_progress("Creating category..."); - - var query = "?op=pref-feeds&subop=editCats&action=add&cat=" + - param_escape(this.attr('value').newcat); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - dialog.attr('content', transport.responseText); - updateFeedList(); - } }); - } - }, - execute: function() { - if (this.validate()) { - } - }, - href: query}); - - dialog.show(); - - } catch (e) { - exception_error("editFeedCats", e); - } -} - -function showInactiveFeeds() { - try { - var query = "backend.php?op=dlg&id=inactiveFeeds"; - - if (dijit.byId("inactiveFeedsDlg")) - dijit.byId("inactiveFeedsDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "inactiveFeedsDlg", - title: __("Feeds without recent updates"), - style: "width: 600px", - getSelectedFeeds: function() { - return getSelectedTableRowIds("prefInactiveFeedList"); - }, - removeSelected: function() { - var sel_rows = this.getSelectedFeeds(); - - console.log(sel_rows); - - if (sel_rows.length > 0) { - var ok = confirm(__("Remove selected feeds?")); - - if (ok) { - notify_progress("Removing selected feeds...", true); - - var query = "?op=pref-feeds&subop=remove&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - dialog.hide(); - updateFeedList(); - } }); - } - - } else { - alert(__("No feeds are selected.")); - } - }, - execute: function() { - if (this.validate()) { - } - }, - href: query}); - - dialog.show(); - - } catch (e) { - exception_error("showInactiveFeeds", e); - } - -} - -function opmlRegenKey() { - - try { - var ok = confirm(__("Replace current OPML publishing address with a new one?")); - - if (ok) { - - notify_progress("Trying to change address...", true); - - var query = "?op=rpc&subop=regenOPMLKey"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - var reply = JSON.parse(transport.responseText); - - var new_link = reply.link; - - var e = $('pub_opml_url'); - - if (new_link) { - e.href = new_link; - e.innerHTML = new_link; - - new Effect.Highlight(e); - - notify(''); - - } else { - notify_error("Could not change feed URL."); - } - } }); - } - } catch (e) { - exception_error("opmlRegenKey", e); - } - return false; -} - -function feedActionChange() { - try { - var chooser = $("feedActionChooser"); - var opid = chooser[chooser.selectedIndex].value; - - chooser.selectedIndex = 0; - feedActionGo(opid); - } catch (e) { - exception_error("feedActionChange", e); - } -} - -function feedActionGo(op) { - try { - if (op == "facEdit") { - - var rows = getSelectedFeeds(); - - if (rows.length > 1) { - editSelectedFeeds(); - } else { - editSelectedFeed(); - } - } - - if (op == "facClear") { - clearSelectedFeeds(); - } - - if (op == "facPurge") { - purgeSelectedFeeds(); - } - - if (op == "facEditCats") { - editFeedCats(); - } - - if (op == "facRescore") { - rescoreSelectedFeeds(); - } - - if (op == "facUnsubscribe") { - removeSelectedFeeds(); - } - - } catch (e) { - exception_error("feedActionGo", e); - - } -} - -function clearFeedArticles(feed_id) { - - notify_progress("Clearing feed..."); - - var query = "?op=pref-feeds&quiet=1&subop=clear&id=" + feed_id; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - } }); - - return false; -} - -function rescoreSelectedFeeds() { - - var sel_rows = getSelectedFeeds(); - - if (sel_rows.length > 0) { - - //var ok = confirm(__("Rescore last 100 articles in selected feeds?")); - var ok = confirm(__("Rescore articles in selected feeds?")); - - if (ok) { - notify_progress("Rescoring selected feeds...", true); - - var query = "?op=pref-feeds&subop=rescore&quiet=1&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify_callback2(transport); - } }); - - } - } else { - alert(__("No feeds are selected.")); - } - - return false; -} - -function rescore_all_feeds() { - var ok = confirm(__("Rescore all articles? This operation may take a lot of time.")); - - if (ok) { - notify_progress("Rescoring feeds...", true); - - var query = "?op=pref-feeds&subop=rescoreAll&quiet=1"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify_callback2(transport); - } }); - } -} - -function labelColorReset() { - try { - var labels = getSelectedLabels(); - - if (labels.length > 0) { - var ok = confirm(__("Reset selected labels to default colors?")); - - if (ok) { - var query = "?op=pref-labels&subop=color-reset&ids="+ - param_escape(labels.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - labellist_callback2(transport); - } }); - } - - } else { - alert(__("No labels are selected.")); - } - - } catch (e) { - exception_error("labelColorReset", e); - } -} - - -function inPreferences() { - return true; -} - -function editProfiles() { - try { - - if (dijit.byId("profileEditDlg")) - dijit.byId("profileEditDlg").destroyRecursive(); - - var query = "backend.php?op=dlg&id=editPrefProfiles"; - - dialog = new dijit.Dialog({ - id: "profileEditDlg", - title: __("Settings Profiles"), - style: "width: 600px", - getSelectedProfiles: function() { - return getSelectedTableRowIds("prefFeedProfileList"); - }, - removeSelected: function() { - var sel_rows = this.getSelectedProfiles(); - - if (sel_rows.length > 0) { - var ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed.")); - - if (ok) { - notify_progress("Removing selected profiles...", true); - - var query = "?op=rpc&subop=remprofiles&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - editProfiles(); - } }); - - } - - } else { - alert(__("No profiles are selected.")); - } - }, - activateProfile: function() { - var sel_rows = this.getSelectedProfiles(); - - if (sel_rows.length == 1) { - - var ok = confirm(__("Activate selected profile?")); - - if (ok) { - notify_progress("Loading, please wait..."); - - var query = "?op=rpc&subop=setprofile&id="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - window.location.reload(); - } }); - } - - } else { - alert(__("Please choose a profile to activate.")); - } - }, - addProfile: function() { - if (this.validate()) { - notify_progress("Creating profile...", true); - - var query = "?op=rpc&subop=addprofile&title=" + - param_escape(dialog.attr('value').newprofile); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - editProfiles(); - } }); - - } - }, - execute: function() { - if (this.validate()) { - } - }, - href: query}); - - dialog.show(); - } catch (e) { - exception_error("editProfiles", e); - } -} - -function activatePrefProfile() { - - var sel_rows = getSelectedFeedCats(); - - if (sel_rows.length == 1) { - - var ok = confirm(__("Activate selected profile?")); - - if (ok) { - notify_progress("Loading, please wait..."); - - var query = "?op=rpc&subop=setprofile&id="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - window.location.reload(); - } }); - } - - } else { - alert(__("Please choose a profile to activate.")); - } - - return false; -} - -function clearFeedAccessKeys() { - - var ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?")); - - if (ok) { - notify_progress("Clearing URLs..."); - - var query = "?op=rpc&subop=clearKeys"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify_info("Generated URLs cleared."); - } }); - } - - return false; -} - -function clearArticleAccessKeys() { - - var ok = confirm(__("This will invalidate all previously shared article URLs. Continue?")); - - if (ok) { - notify_progress("Clearing URLs..."); - - var query = "?op=rpc&subop=clearArticleKeys"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify_info("Shared URLs cleared."); - } }); - } - - return false; -} -function resetFeedOrder() { - try { - notify_progress("Loading, please wait..."); - - new Ajax.Request("backend.php", { - parameters: "?op=pref-feeds&subop=feedsortreset", - onComplete: function(transport) { - updateFeedList(); - } }); - - - } catch (e) { - exception_error("resetFeedOrder"); - } -} - -function resetCatOrder() { - try { - notify_progress("Loading, please wait..."); - - new Ajax.Request("backend.php", { - parameters: "?op=pref-feeds&subop=catsortreset", - onComplete: function(transport) { - updateFeedList(); - } }); - - - } catch (e) { - exception_error("resetCatOrder"); - } -} - -function editCat(id, item, event) { - try { - var new_name = prompt(__('Rename category to:'), item.name); - - if (new_name && new_name != item.name) { - - notify_progress("Loading, please wait..."); - - new Ajax.Request("backend.php", { - parameters: { - op: 'pref-feeds', - subop: 'renamecat', - id: id, - title: new_name, - }, - onComplete: function(transport) { - updateFeedList(); - } }); - } - - } catch (e) { - exception_error("editCat", e); - } -} - -function editLabel(id, event) { - try { - var query = "backend.php?op=pref-labels&subop=edit&id=" + - param_escape(id); - - if (dijit.byId("labelEditDlg")) - dijit.byId("labelEditDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "labelEditDlg", - title: __("Label Editor"), - style: "width: 600px", - setLabelColor: function(id, fg, bg) { - - var kind = ''; - var color = ''; - - if (fg && bg) { - kind = 'both'; - } else if (fg) { - kind = 'fg'; - color = fg; - } else if (bg) { - kind = 'bg'; - color = bg; - } - - var query = "?op=pref-labels&subop=color-set&kind="+kind+ - "&ids=" + param_escape(id) + "&fg=" + param_escape(fg) + - "&bg=" + param_escape(bg) + "&color=" + param_escape(color); - - // console.log(query); - - var e = $("LICID-" + id); - - if (e) { - if (fg) e.style.color = fg; - if (bg) e.style.backgroundColor = bg; - } - - new Ajax.Request("backend.php", { parameters: query }); - - updateFilterList(); - }, - execute: function() { - if (this.validate()) { - var caption = this.attr('value').caption; - var fg_color = this.attr('value').fg_color; - var bg_color = this.attr('value').bg_color; - var query = dojo.objectToQuery(this.attr('value')); - - dijit.byId('labelTree').setNameById(id, caption); - this.setLabelColor(id, fg_color, bg_color); - this.hide(); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - updateFilterList(); - } }); - } - }, - href: query}); - - dialog.show(); - - } catch (e) { - exception_error("editLabel", e); - } -} - -function clearTwitterCredentials() { - try { - var ok = confirm(__("This will clear your stored authentication information for Twitter. Continue?")); - - if (ok) { - notify_progress("Clearing credentials..."); - - var query = "?op=pref-feeds&subop=remtwitterinfo"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify_info("Twitter credentials have been cleared."); - updateFeedList(); - } }); - } - - } catch (e) { - exception_error("clearTwitterCredentials", e); - } -} - -function customizeCSS() { - try { - var query = "backend.php?op=dlg&id=customizeCSS"; - - if (dijit.byId("cssEditDlg")) - dijit.byId("cssEditDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "cssEditDlg", - title: __("Customize stylesheet"), - style: "width: 600px", - execute: function() { - notify_progress('Saving data...', true); - new Ajax.Request("backend.php", { - parameters: dojo.objectToQuery(this.attr('value')), - onComplete: function(transport) { - notify(''); - window.location.reload(); - } }); - - }, - href: query}); - - dialog.show(); - - } catch (e) { - exception_error("customizeCSS", e); - } -} - -function insertSSLserial(value) { - try { - dijit.byId("SSL_CERT_SERIAL").attr('value', value); - } catch (e) { - exception_error("insertSSLcerial", e); - } -} - -function getSelectedInstances() { - return getSelectedTableRowIds("prefInstanceList"); -} - -function addInstance() { - try { - var query = "backend.php?op=dlg&id=addInstance"; - - if (dijit.byId("instanceAddDlg")) - dijit.byId("instanceAddDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "instanceAddDlg", - title: __("Link Instance"), - style: "width: 600px", - regenKey: function() { - new Ajax.Request("backend.php", { - parameters: "?op=rpc&subop=genHash", - onComplete: function(transport) { - var reply = JSON.parse(transport.responseText); - if (reply) - dijit.byId('instance_add_key').attr('value', reply.hash); - - } }); - }, - execute: function() { - if (this.validate()) { - console.warn(dojo.objectToQuery(this.attr('value'))); - - notify_progress('Saving data...', true); - new Ajax.Request("backend.php", { - parameters: dojo.objectToQuery(this.attr('value')), - onComplete: function(transport) { - dialog.hide(); - notify(''); - updateInstanceList(); - } }); - } - }, - href: query, - }); - - dialog.show(); - - } catch (e) { - exception_error("addInstance", e); - } -} - -function editInstance(id, event) { - try { - if (!event || !event.ctrlKey) { - - selectTableRows('prefInstanceList', 'none'); - selectTableRowById('LIRR-'+id, 'LICHK-'+id, true); - - var query = "backend.php?op=pref-instances&subop=edit&id=" + - param_escape(id); - - if (dijit.byId("instanceEditDlg")) - dijit.byId("instanceEditDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "instanceEditDlg", - title: __("Edit Instance"), - style: "width: 600px", - regenKey: function() { - new Ajax.Request("backend.php", { - parameters: "?op=rpc&subop=genHash", - onComplete: function(transport) { - var reply = JSON.parse(transport.responseText); - if (reply) - dijit.byId('instance_edit_key').attr('value', reply.hash); - - } }); - }, - execute: function() { - if (this.validate()) { -// console.warn(dojo.objectToQuery(this.attr('value'))); - - notify_progress('Saving data...', true); - new Ajax.Request("backend.php", { - parameters: dojo.objectToQuery(this.attr('value')), - onComplete: function(transport) { - dialog.hide(); - notify(''); - updateInstanceList(); - } }); - } - }, - href: query, - }); - - dialog.show(); - - } else if (event.ctrlKey) { - var cb = $('LICHK-' + id); - cb.checked = !cb.checked; - toggleSelectRow(cb); - } - - - } catch (e) { - exception_error("editInstance", e); - } -} - -function removeSelectedInstances() { - try { - var sel_rows = getSelectedInstances(); - - if (sel_rows.length > 0) { - - var ok = confirm(__("Remove selected instances?")); - - if (ok) { - notify_progress("Removing selected instances..."); - - var query = "?op=pref-instances&subop=remove&ids="+ - param_escape(sel_rows.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - updateInstanceList(); - } }); - } - - } else { - alert(__("No instances are selected.")); - } - - } catch (e) { - exception_error("removeInstance", e); - } -} - -function editSelectedInstance() { - var rows = getSelectedInstances(); - - if (rows.length == 0) { - alert(__("No instances are selected.")); - return; - } - - if (rows.length > 1) { - alert(__("Please select only one instance.")); - return; - } - - notify(""); - - editInstance(rows[0]); -} - diff --git a/prefs.php b/prefs.php index 98e4a2fd9..3787a79b6 100644 --- a/prefs.php +++ b/prefs.php @@ -1,4 +1,6 @@ "> - - - - + + + diff --git a/register.php b/register.php index cc453c0f2..3694a5e75 100644 --- a/register.php +++ b/register.php @@ -4,6 +4,8 @@ // 1) templates/register_notice.txt - displayed above the registration form // 2) register_expire_do.php - contains user expiration queries when necessary + set_include_path(get_include_path() . PATH_SEPARATOR . "include"); + require_once 'lib/phpmailer/class.phpmailer.php'; $action = $_REQUEST["action"]; @@ -93,7 +95,7 @@ Create new account - + @@ -232,7 +234,7 @@ -
      +
      "; ?> @@ -245,7 +247,7 @@ if (!$login || !$email || !$test) { print_error(__("Your registration information is incomplete.")); - print "

      + print "

      "; return; @@ -260,7 +262,7 @@ if ($is_registered) { print_error(__('Sorry, this username is already taken.')); - print "

      + print "

      "; } else { @@ -278,7 +280,7 @@ if (db_num_rows($result) != 1) { print_error(__('Registration failed.')); - print "

      + print "

      "; } else { @@ -363,7 +365,7 @@ print_notice(__("Account created successfully.")); - print "

      + print "

      "; @@ -373,7 +375,7 @@ } else { print_error('Plese check the form again, you have failed the robot test.'); - print "

      + print "

      "; @@ -385,7 +387,7 @@ -
      +
      "; ?> diff --git a/sanity_check.php b/sanity_check.php deleted file mode 100644 index 3e5c09803..000000000 --- a/sanity_check.php +++ /dev/null @@ -1,175 +0,0 @@ -Fatal Error: You forgot to copy - config.php-dist to config.php and edit it.\n"; - exit; - } - - require_once "config.php"; - require_once "sanity_config.php"; - - if (CONFIG_VERSION != EXPECTED_CONFIG_VERSION) { - $err_msg = "config: your config file version is incorrect. See config.php-dist.\n"; - } - - $purifier_cache_dir = CACHE_DIR . "/htmlpurifier"; - - if (!is_writable($purifier_cache_dir)) { - $err_msg = "config: HTMLPurifier cache directory should be writable by anyone (chmod -R 777 $purifier_cache_dir)"; - } - - if (GENERATED_CONFIG_CHECK != EXPECTED_CONFIG_VERSION) { - $err_msg = "config: your sanity_config.php is outdated, please recreate it using ./utils/regen_config_checks.sh"; - } - - foreach ($requred_defines as $d) { - if (!defined($d)) { - $err_msg = "config: required constant $d is not defined. Please check config.php"; - } - } - - if (defined('RSS_BACKEND_TYPE')) { - print "Fatal error: RSS_BACKEND_TYPE is deprecated. Please remove this - option from config.php\n"; - exit; - } - - if (file_exists("xml-export.php") || file_exists("xml-import.php")) { - print "Fatal Error: XML Import/Export tools (xml-export.php - and xml-import.php) could be used maliciously. Please remove them - from your TT-RSS instance.\n"; - exit; - } - - if (SINGLE_USER_MODE && DAEMON_UPDATE_LOGIN_LIMIT > 0) { - print "Fatal Error: Please set DAEMON_UPDATE_LOGIN_LIMIT - to 0 in single user mode.\n"; - exit; - } - - if (!defined('SESSION_EXPIRE_TIME')) { - $err_msg = "config: SESSION_EXPIRE_TIME is undefined"; - } - - if (SESSION_EXPIRE_TIME < 60) { - $err_msg = "config: SESSION_EXPIRE_TIME is too low (less than 60)"; - } - - if (SESSION_EXPIRE_TIME < SESSION_COOKIE_LIFETIME) { - $err_msg = "config: SESSION_EXPIRE_TIME should be greater or equal to" . - "SESSION_COOKIE_LIFETIME"; - } - -/* if (defined('DISABLE_SESSIONS')) { - $err_msg = "config: you have enabled DISABLE_SESSIONS. Please disable this option."; -} */ - - if (DATABASE_BACKED_SESSIONS && SINGLE_USER_MODE) { - $err_msg = "config: DATABASE_BACKED_SESSIONS is incompatible with SINGLE_USER_MODE"; - } - - if (DATABASE_BACKED_SESSIONS && DB_TYPE == "mysql") { - $err_msg = "config: DATABASE_BACKED_SESSIONS are currently broken with MySQL"; - } - - if (SINGLE_USER_MODE) { - $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); - - if ($link) { - $result = db_query($link, "SELECT id FROM ttrss_users WHERE id = 1"); - - if (db_num_rows($result) != 1) { - $err_msg = "config: SINGLE_USER_MODE is enabled but default admin account (UID=1) is not found."; - } - } - } - - if (defined('MAIL_FROM')) { - $err_msg = "config: MAIL_FROM has been split into DIGEST_FROM_NAME and DIGEST_FROM_ADDRESS"; - } - - if (!defined('COUNTERS_MAX_AGE')) { - $err_msg = "config: option COUNTERS_MAX_AGE expected, but not defined"; - } - - if (defined('DAEMON_REFRESH_ONLY')) { - $err_msg = "config: option DAEMON_REFRESH_ONLY is obsolete. Please remove this option and read about other ways to update feeds on the wiki."; - - } - - if (defined('ENABLE_SIMPLEPIE')) { - $err_msg = "config: ENABLE_SIMPLEPIE is obsolete and replaced with DEFAULT_UPDATE_METHOD. Please adjust your config.php."; - } - - if (!defined('DEFAULT_UPDATE_METHOD') || (DEFAULT_UPDATE_METHOD != 0 && - DEFAULT_UPDATE_METHOD != 1)) { - $err_msg = "config: DEFAULT_UPDATE_METHOD should be either 0 or 1."; - } - - if (SELF_URL_PATH == "http://yourserver/tt-rss/") { - $err_msg = "config: please set SELF_URL_PATH to the correct value."; - } - - if (!is_writable(ICONS_DIR)) { - $err_msg = "config: your ICONS_DIR (" . ICONS_DIR . ") is not writable.\n"; - } - - if (ini_get("open_basedir")) { - $err_msg = "php.ini: open_basedir is not supported."; - } - - if (!function_exists("curl_init") && !ini_get("allow_url_fopen")) { - $err_msg = "php.ini: either allow_url_fopen or CURL needs to be enabled."; - } - - if (!function_exists("json_encode")) { - $err_msg = "PHP: json functions not found."; - } - - if (DB_TYPE == "mysql" && !function_exists("mysql_connect")) { - $err_msg = "PHP: MySQL functions not found."; - } - - if (DB_TYPE == "pgsql" && !function_exists("pg_connect")) { - $err_msg = "PHP: PostgreSQL functions not found."; - } - - if (!function_exists("mb_strlen")) { - $err_msg = "PHP: mbstring functions not found."; - } - - if (!function_exists("ctype_lower")) { - $err_msg = "PHP: ctype functions not found (required for HTMLPurifier)."; - } - - if (ini_get("safe_mode")) { - $err_msg = "php.ini: Safe mode is not supported. If you wish to continue, remove this test from sanity_check.php and proceeed at your own risk. Please note that your bug reports will not be accepted or reviewed."; - } - - if ((PUBSUBHUBBUB_HUB || PUBSUBHUBBUB_ENABLED) && !function_exists("curl_init")) { - $err_msg = "CURL is required for PubSubHubbub support."; - } - - if (!class_exists("DOMDocument")) { - $err_msg = "PHP: DOMDocument extension not found."; - } - - if (SELF_URL_PATH == "http://local.host/tt-rss") { - $err_msg = "config: please set SELF_URL_PATH to the correct value"; - } - - if (!ISCONFIGURED) { - $err_msg = "config: please read config.php completely."; - } - - if ($err_msg) { - print "Fatal Error: $err_msg\n"; - exit; - } - -?> diff --git a/sanity_config.php b/sanity_config.php deleted file mode 100644 index 51c9d52be..000000000 --- a/sanity_config.php +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/sessions.php b/sessions.php deleted file mode 100644 index 8588f5807..000000000 --- a/sessions.php +++ /dev/null @@ -1,108 +0,0 @@ - diff --git a/tt-rss.js b/tt-rss.js deleted file mode 100644 index 36e0d8cc4..000000000 --- a/tt-rss.js +++ /dev/null @@ -1,1164 +0,0 @@ -var total_unread = 0; -var global_unread = -1; -var firsttime_update = true; -var _active_feed_id = 0; -var _active_feed_is_cat = false; -var hotkey_prefix = false; -var hotkey_prefix_pressed = false; -var init_params = {}; -var _force_scheduled_update = false; -var last_scheduled_update = false; - -var _rpc_seq = 0; - -function next_seq() { - _rpc_seq += 1; - return _rpc_seq; -} - -function get_seq() { - return _rpc_seq; -} - -function activeFeedIsCat() { - return _active_feed_is_cat; -} - -function getActiveFeedId() { - try { - //console.log("gAFID: " + _active_feed_id); - return _active_feed_id; - } catch (e) { - exception_error("getActiveFeedId", e); - } -} - -function setActiveFeedId(id, is_cat) { - try { - _active_feed_id = id; - - if (is_cat != undefined) { - _active_feed_is_cat = is_cat; - } - - selectFeed(id, is_cat); - - } catch (e) { - exception_error("setActiveFeedId", e); - } -} - - -function updateFeedList() { - try { - -// $("feeds-holder").innerHTML = "
      " + -// __("Loading, please wait...") + "
      "; - - Element.show("feedlistLoading"); - - if (dijit.byId("feedTree")) { - dijit.byId("feedTree").destroyRecursive(); - } - - var store = new dojo.data.ItemFileWriteStore({ - url: "backend.php?op=feeds"}); - - var treeModel = new fox.FeedStoreModel({ - store: store, - query: { - "type": "feed" - }, - rootId: "root", - rootLabel: "Feeds", - childrenAttrs: ["items"] - }); - - var tree = new fox.FeedTree({ - persist: false, - model: treeModel, - onOpen: function (item, node) { - var id = String(item.id); - var cat_id = id.substr(id.indexOf(":")+1); - - new Ajax.Request("backend.php", - { parameters: "backend.php?op=feeds&subop=collapse&cid=" + - param_escape(cat_id) + "&mode=0" } ); - }, - onClose: function (item, node) { - var id = String(item.id); - var cat_id = id.substr(id.indexOf(":")+1); - - new Ajax.Request("backend.php", - { parameters: "backend.php?op=feeds&subop=collapse&cid=" + - param_escape(cat_id) + "&mode=1" } ); - - }, - onClick: function (item, node) { - var id = String(item.id); - var is_cat = id.match("^CAT:"); - var feed = id.substr(id.indexOf(":")+1); - viewfeed(feed, '', is_cat); - return false; - }, - openOnClick: false, - showRoot: false, - id: "feedTree", - }, "feedTree"); - -/* var menu = new dijit.Menu({id: 'feedMenu'}); - - menu.addChild(new dijit.MenuItem({ - label: "Simple menu item" - })); - -// menu.bindDomNode(tree.domNode); */ - - var tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) { - console.log(dijit.getEnclosingWidget(event.target)); - dojo.disconnect(tmph); - }); - - $("feeds-holder").appendChild(tree.domNode); - - var tmph = dojo.connect(tree, 'onLoad', function() { - dojo.disconnect(tmph); - Element.hide("feedlistLoading"); - - tree.collapseHiddenCats(); - - feedlist_init(); - -// var node = dijit.byId("feedTree")._itemNodesMap['FEED:-2'][0].domNode -// menu.bindDomNode(node); - - loading_set_progress(25); - }); - - tree.startup(); - - } catch (e) { - exception_error("updateFeedList", e); - } -} - -function catchupAllFeeds() { - - var str = __("Mark all articles as read?"); - - if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { - - var query_str = "backend.php?op=feeds&subop=catchupAll"; - - notify_progress("Marking all feeds as read..."); - - //console.log("catchupAllFeeds Q=" + query_str); - - new Ajax.Request("backend.php", { - parameters: query_str, - onComplete: function(transport) { - feedlist_callback2(transport); - } }); - - global_unread = 0; - updateTitle(""); - } -} - -function viewCurrentFeed(subop) { - - if (getActiveFeedId() != undefined) { - viewfeed(getActiveFeedId(), subop, activeFeedIsCat()); - } - return false; // block unneeded form submits -} - -function timeout() { - if (getInitParam("bw_limit") == "1") return; - - try { - var date = new Date(); - var ts = Math.round(date.getTime() / 1000); - - if (ts - last_scheduled_update > 10 || _force_scheduled_update) { - - //console.log("timeout()"); - - window.clearTimeout(counter_timeout_id); - - var query_str = "?op=rpc&subop=getAllCounters&seq=" + next_seq(); - - var omode; - - if (firsttime_update && !navigator.userAgent.match("Opera")) { - firsttime_update = false; - omode = "T"; - } else { - omode = "flc"; - } - - query_str = query_str + "&omode=" + omode; - - if (!_force_scheduled_update) - query_str = query_str + "&last_article_id=" + getInitParam("last_article_id"); - - //console.log("[timeout]" + query_str); - - new Ajax.Request("backend.php", { - parameters: query_str, - onComplete: function(transport) { - handle_rpc_json(transport, !_force_scheduled_update); - _force_scheduled_update = false; - } }); - - last_scheduled_update = ts; - } - - } catch (e) { - exception_error("timeout", e); - } - - setTimeout("timeout()", 3000); -} - -function search() { - var query = "backend.php?op=dlg&id=search¶m=" + - param_escape(getActiveFeedId() + ":" + activeFeedIsCat()); - - if (dijit.byId("searchDlg")) - dijit.byId("searchDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "searchDlg", - title: __("Search"), - style: "width: 600px", - execute: function() { - if (this.validate()) { - _search_query = dojo.objectToQuery(this.attr('value')); - this.hide(); - viewCurrentFeed(); - } - }, - href: query}); - - dialog.show(); -} - -function updateTitle() { - var tmp = "Tiny Tiny RSS"; - - if (global_unread > 0) { - tmp = tmp + " (" + global_unread + ")"; - } - - if (window.fluid) { - if (global_unread > 0) { - window.fluid.dockBadge = global_unread; - } else { - window.fluid.dockBadge = ""; - } - } - - document.title = tmp; -} - -function genericSanityCheck() { - setCookie("ttrss_test", "TEST"); - - if (getCookie("ttrss_test") != "TEST") { - return fatalError(2); - } - - return true; -} - -function init() { - try { - dojo.registerModulePath("fox", "../.."); - - dojo.require("fox.FeedTree"); - - if (typeof themeBeforeLayout == 'function') { - themeBeforeLayout(); - } - - dojo.parser.parse(); - - dojo.addOnLoad(function() { - updateFeedList(); - closeArticlePanel(); - - if (typeof themeAfterLayout == 'function') { - themeAfterLayout(); - } - - }); - - if (!genericSanityCheck()) - return false; - - loading_set_progress(20); - - var hasAudio = !!((myAudioTag = document.createElement('audio')).canPlayType); - - new Ajax.Request("backend.php", { - parameters: {op: "rpc", subop: "sanityCheck", hasAudio: hasAudio}, - onComplete: function(transport) { - backend_sanity_check_callback(transport); - } }); - - } catch (e) { - exception_error("init", e); - } -} - -function init_second_stage() { - - try { - - delCookie("ttrss_test"); - - var toolbar = document.forms["main_toolbar_form"]; - - dijit.getEnclosingWidget(toolbar.view_mode).attr('value', - getInitParam("default_view_mode")); - - dijit.getEnclosingWidget(toolbar.order_by).attr('value', - getInitParam("default_view_order_by")); - - feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1; - - loading_set_progress(30); - - // can't use cache_clear() here because viewfeed might not have initialized yet - if ('sessionStorage' in window && window['sessionStorage'] !== null) - sessionStorage.clear(); - - console.log("second stage ok"); - - } catch (e) { - exception_error("init_second_stage", e); - } -} - -function quickMenuGo(opid) { - try { - if (opid == "qmcPrefs") { - gotoPreferences(); - } - - if (opid == "qmcTagCloud") { - displayDlg("printTagCloud"); - } - - if (opid == "qmcTagSelect") { - displayDlg("printTagSelect"); - } - - if (opid == "qmcSearch") { - search(); - return; - } - - if (opid == "qmcAddFeed") { - quickAddFeed(); - return; - } - - if (opid == "qmcDigest") { - window.location.href = "digest.php"; - return; - } - - if (opid == "qmcEditFeed") { - if (activeFeedIsCat()) - alert(__("You can't edit this kind of feed.")); - else - editFeed(getActiveFeedId()); - return; - } - - if (opid == "qmcRemoveFeed") { - var actid = getActiveFeedId(); - - if (activeFeedIsCat()) { - alert(__("You can't unsubscribe from the category.")); - return; - } - - if (!actid) { - alert(__("Please select some feed first.")); - return; - } - - var fn = getFeedName(actid); - - var pr = __("Unsubscribe from %s?").replace("%s", fn); - - if (confirm(pr)) { - unsubscribeFeed(actid); - } - - return; - } - - if (opid == "qmcCatchupAll") { - catchupAllFeeds(); - return; - } - - if (opid == "qmcShowOnlyUnread") { - toggleDispRead(); - return; - } - - if (opid == "qmcAddFilter") { - quickAddFilter(); - return; - } - - if (opid == "qmcAddLabel") { - addLabel(); - return; - } - - if (opid == "qmcRescoreFeed") { - rescoreCurrentFeed(); - return; - } - - if (opid == "qmcHKhelp") { - //Element.show("hotkey_help_overlay"); - Effect.Appear("hotkey_help_overlay", {duration : 0.3}); - } - - if (opid == "qmcAbout") { - dialog = new dijit.Dialog({ - title: __("About..."), - style: "width: 400px", - href: "backend.php?op=dlg&id=about", - }); - - dialog.show(); - } - - } catch (e) { - exception_error("quickMenuGo", e); - } -} - -function toggleDispRead() { - try { - - var hide = !(getInitParam("hide_read_feeds") == "1"); - - hideOrShowFeeds(hide); - - var query = "?op=rpc&subop=setpref&key=HIDE_READ_FEEDS&value=" + - param_escape(hide); - - setInitParam("hide_read_feeds", hide); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - } }); - - } catch (e) { - exception_error("toggleDispRead", e); - } -} - -function parse_runtime_info(data) { - - //console.log("parsing runtime info..."); - - for (k in data) { - var v = data[k]; - -// console.log("RI: " + k + " => " + v); - - if (k == "new_version_available") { - var icon = $("newVersionIcon"); - if (icon) { - if (v == "1") { - icon.style.display = "inline"; - } else { - icon.style.display = "none"; - } - } - return; - } - - if (k == "daemon_is_running" && v != 1) { - notify_error("Update daemon is not running.", true); - return; - } - - if (k == "daemon_stamp_ok" && v != 1) { - notify_error("Update daemon is not updating feeds.", true); - return; - } - - if (k == "max_feed_id" || k == "num_feeds") { - if (init_params[k] != v) { - console.log("feed count changed, need to reload feedlist."); - updateFeedList(); - } - } - - init_params[k] = v; - notify(''); - } -} - -function catchupCurrentFeed() { - - var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); - - var str = __("Mark all articles in %s as read?").replace("%s", fn); - - if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { - return viewCurrentFeed('MarkAllRead'); - } -} - -function catchupFeedInGroup(id) { - - try { - - var title = getFeedName(id); - - var str = __("Mark all articles in %s as read?").replace("%s", title); - - if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { - return viewCurrentFeed('MarkAllReadGR:' + id); - } - - } catch (e) { - exception_error("catchupFeedInGroup", e); - } -} - -function collapse_feedlist() { - try { - - if (!Element.visible('feeds-holder')) { - Element.show('feeds-holder'); - Element.show('feeds-holder_splitter'); - $("collapse_feeds_btn").innerHTML = "<<"; - } else { - Element.hide('feeds-holder'); - Element.hide('feeds-holder_splitter'); - $("collapse_feeds_btn").innerHTML = ">>"; - } - - dijit.byId("main").resize(); - - query = "?op=rpc&subop=setpref&key=_COLLAPSED_FEEDLIST&value=true"; - new Ajax.Request("backend.php", { parameters: query }); - - } catch (e) { - exception_error("collapse_feedlist", e); - } -} - -function viewModeChanged() { - return viewCurrentFeed(''); -} - -function viewLimitChanged() { - return viewCurrentFeed(''); -} - -/* function adjustArticleScore(id, score) { - try { - - var pr = prompt(__("Assign score to article:"), score); - - if (pr != undefined) { - var query = "?op=rpc&subop=setScore&id=" + id + "&score=" + pr; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - viewCurrentFeed(); - } }); - - } - } catch (e) { - exception_error("adjustArticleScore", e); - } -} */ - -function rescoreCurrentFeed() { - - var actid = getActiveFeedId(); - - if (activeFeedIsCat() || actid < 0) { - alert(__("You can't rescore this kind of feed.")); - return; - } - - if (!actid) { - alert(__("Please select some feed first.")); - return; - } - - var fn = getFeedName(actid); - var pr = __("Rescore articles in %s?").replace("%s", fn); - - if (confirm(pr)) { - notify_progress("Rescoring articles..."); - - var query = "?op=pref-feeds&subop=rescore&quiet=1&ids=" + actid; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - viewCurrentFeed(); - } }); - } -} - -function hotkey_handler(e) { - try { - - if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return; - - var keycode = false; - var shift_key = false; - - var cmdline = $('cmdline'); - - try { - shift_key = e.shiftKey; - } catch (e) { - - } - - if (window.event) { - keycode = window.event.keyCode; - } else if (e) { - keycode = e.which; - } - - var keychar = String.fromCharCode(keycode); - - if (keycode == 27) { // escape - if (Element.visible("hotkey_help_overlay")) { - Element.hide("hotkey_help_overlay"); - } - hotkey_prefix = false; - } - - if (keycode == 16) return; // ignore lone shift - if (keycode == 17) return; // ignore lone ctrl - - if ((keycode == 70 || keycode == 67 || keycode == 71 || keycode == 65) - && !hotkey_prefix) { - - var date = new Date(); - var ts = Math.round(date.getTime() / 1000); - - hotkey_prefix = keycode; - hotkey_prefix_pressed = ts; - - cmdline.innerHTML = keychar; - Element.show(cmdline); - - console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar + " TS=" + ts); - return true; - } - - if (Element.visible("hotkey_help_overlay")) { - Element.hide("hotkey_help_overlay"); - } - - /* Global hotkeys */ - - Element.hide(cmdline); - - if (!hotkey_prefix) { - - if (keycode == 27) { // escape - closeArticlePanel(); - return; - } - - if (keycode == 69) { // e - var id = getActiveArticleId(); - emailArticle(id); - } - - if ((keycode == 191 || keychar == '?') && shift_key) { // ? - if (!Element.visible("hotkey_help_overlay")) { - Effect.Appear("hotkey_help_overlay", {duration : 0.3, to : 0.9}); - } else { - Element.hide("hotkey_help_overlay"); - } - return false; - } - - if (keycode == 191 || keychar == '/') { // / - search(); - return false; - } - - if (keycode == 74 && !shift_key) { // j - var rv = dijit.byId("feedTree").getPreviousFeed( - getActiveFeedId(), activeFeedIsCat()); - - if (rv) viewfeed(rv[0], '', rv[1]); - - return; - } - - if (keycode == 75) { // k - var rv = dijit.byId("feedTree").getNextFeed( - getActiveFeedId(), activeFeedIsCat()); - - if (rv) viewfeed(rv[0], '', rv[1]); - - return; - } - - if (shift_key && keycode == 40) { // shift-down - catchupRelativeToArticle(1); - return; - } - - if (shift_key && keycode == 38) { // shift-up - catchupRelativeToArticle(0); - return; - } - - if (shift_key && keycode == 78) { // N - scrollArticle(50); - return; - } - - if (shift_key && keycode == 80) { // P - scrollArticle(-50); - return; - } - - if (keycode == 68 && shift_key) { // shift-D - dismissSelectedArticles(); - return; - } - - if (keycode == 88 && shift_key) { // shift-X - dismissReadArticles(); - return; - } - - if (keycode == 78 || keycode == 40) { // n, down - if (typeof moveToPost != 'undefined') { - moveToPost('next'); - return false; - } - } - - if (keycode == 80 || keycode == 38) { // p, up - if (typeof moveToPost != 'undefined') { - moveToPost('prev'); - return false; - } - } - - if (keycode == 83 && shift_key) { // S - selectionTogglePublished(undefined, false, true); - return; - } - - if (keycode == 83) { // s - selectionToggleMarked(undefined, false, true); - return; - } - - if (keycode == 85) { // u - selectionToggleUnread(undefined, false, true); - return; - } - - if (keycode == 84 && shift_key) { // T - var id = getActiveArticleId(); - if (id) { - editArticleTags(id, getActiveFeedId(), isCdmMode()); - return; - } - } - - if (keycode == 9) { // tab - var id = getArticleUnderPointer(); - if (id) { - var cb = $("RCHK-" + id); - - if (cb) { - cb.checked = !cb.checked; - toggleSelectRowById(cb, "RROW-" + id); - return false; - } - } - } - - if (keycode == 79) { // o - if (getActiveArticleId()) { - openArticleInNewWindow(getActiveArticleId()); - return; - } - } - - if (keycode == 81 && shift_key) { // Q - if (typeof catchupAllFeeds != 'undefined') { - catchupAllFeeds(); - return; - } - } - - if (keycode == 88 && !shift_key) { // x - if (activeFeedIsCat()) { - dijit.byId("feedTree").collapseCat(getActiveFeedId()); - return; - } - } - } - - /* Prefix a */ - - if (hotkey_prefix == 65) { // a - hotkey_prefix = false; - - if (keycode == 65) { // a - selectArticles('all'); - return; - } - - if (keycode == 85) { // u - selectArticles('unread'); - return; - } - - if (keycode == 73) { // i - selectArticles('invert'); - return; - } - - if (keycode == 78) { // n - selectArticles('none'); - return; - } - - } - - /* Prefix f */ - - if (hotkey_prefix == 70) { // f - - hotkey_prefix = false; - - if (keycode == 81) { // q - if (getActiveFeedId()) { - catchupCurrentFeed(); - return; - } - } - - if (keycode == 82) { // r - if (getActiveFeedId()) { - viewfeed(getActiveFeedId(), '', activeFeedIsCat()); - return; - } - } - - if (keycode == 65) { // a - toggleDispRead(); - return false; - } - - if (keycode == 85) { // u - if (getActiveFeedId()) { - viewfeed(getActiveFeedId(), ''); - return false; - } - } - - if (keycode == 69) { // e - - if (activeFeedIsCat()) - alert(__("You can't edit this kind of feed.")); - else - editFeed(getActiveFeedId()); - return; - - return false; - } - - if (keycode == 83) { // s - quickAddFeed(); - return false; - } - - if (keycode == 67 && shift_key) { // C - if (typeof catchupAllFeeds != 'undefined') { - catchupAllFeeds(); - return false; - } - } - - if (keycode == 67) { // c - if (getActiveFeedId()) { - catchupCurrentFeed(); - return false; - } - } - - if (keycode == 88) { // x - reverseHeadlineOrder(); - return; - } - } - - /* Prefix c */ - - if (hotkey_prefix == 67) { // c - hotkey_prefix = false; - - if (keycode == 70) { // f - quickAddFilter(); - return false; - } - - if (keycode == 76) { // l - addLabel(); - return false; - } - - if (keycode == 83) { // s - if (typeof collapse_feedlist != 'undefined') { - collapse_feedlist(); - return false; - } - } - - if (keycode == 77) { // m - // TODO: sortable feedlist - return; - } - - if (keycode == 78) { // n - catchupRelativeToArticle(1); - return; - } - - if (keycode == 80) { // p - catchupRelativeToArticle(0); - return; - } - - - } - - /* Prefix g */ - - if (hotkey_prefix == 71) { // g - - hotkey_prefix = false; - - - if (keycode == 65) { // a - viewfeed(-4); - return false; - } - - if (keycode == 83) { // s - viewfeed(-1); - return false; - } - - if (keycode == 80 && shift_key) { // P - gotoPreferences(); - return false; - } - - if (keycode == 80) { // p - viewfeed(-2); - return false; - } - - if (keycode == 70) { // f - viewfeed(-3); - return false; - } - - if (keycode == 84) { // t - displayDlg("printTagCloud"); - return false; - } - } - - /* Cmd */ - - if (hotkey_prefix == 224 || hotkey_prefix == 91) { // f - hotkey_prefix = false; - return; - } - - if (hotkey_prefix) { - console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar); - } else { - console.log("KP: CODE=" + keycode + " CHAR=" + keychar); - } - - - } catch (e) { - exception_error("hotkey_handler", e); - } -} - -function inPreferences() { - return false; -} - -function reverseHeadlineOrder() { - try { - - var query_str = "?op=rpc&subop=togglepref&key=REVERSE_HEADLINES"; - - new Ajax.Request("backend.php", { - parameters: query_str, - onComplete: function(transport) { - viewCurrentFeed(); - } }); - - } catch (e) { - exception_error("reverseHeadlineOrder", e); - } -} - -function scheduleFeedUpdate(id, is_cat) { - try { - if (!id) { - id = getActiveFeedId(); - is_cat = activeFeedIsCat(); - } - - if (!id) { - alert(__("Please select some feed first.")); - return; - } - - var query = "?op=rpc&subop=scheduleFeedUpdate&id=" + - param_escape(id) + - "&is_cat=" + param_escape(is_cat); - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - - var reply = JSON.parse(transport.responseText); - var message = reply['message']; - - if (message) { - notify_info(message); - return; - } - - } }); - - - } catch (e) { - exception_error("scheduleFeedUpdate", e); - } -} - -function newVersionDlg() { - try { - var query = "backend.php?op=dlg&id=newVersion"; - - if (dijit.byId("newVersionDlg")) - dijit.byId("newVersionDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "newVersionDlg", - title: __("New version available!"), - style: "width: 600px", - href: query, - }); - - dialog.show(); - - } catch (e) { - exception_error("newVersionDlg", e); - } -} - -function handle_rpc_json(transport, scheduled_call) { - try { - var reply = JSON.parse(transport.responseText); - - if (reply) { - - var error = reply['error']; - - if (error) { - var code = error['code']; - var msg = error['msg']; - - console.warn("[handle_rpc_json] received fatal error " + code + "/" + msg); - - if (code != 0) { - fatalError(code, msg); - return false; - } - } - - var seq = reply['seq']; - - if (seq) { - if (get_seq() != seq) { - console.log("[handle_rpc_json] sequence mismatch: " + seq + - " (want: " + get_seq() + ")"); - return true; - } - } - - var message = reply['message']; - - if (message) { - if (message == "UPDATE_COUNTERS") { - console.log("need to refresh counters..."); - setInitParam("last_article_id", -1); - _force_scheduled_update = true; - } - } - - var counters = reply['counters']; - - if (counters) - parse_counters(counters, scheduled_call); - - var runtime_info = reply['runtime-info'];; - - if (runtime_info) - parse_runtime_info(runtime_info); - - hideOrShowFeeds(getInitParam("hide_read_feeds") == 1); - - } else { - notify_error("Error communicating with server."); - } - - } catch (e) { - notify_error("Error communicating with server."); - console.log(e); - //exception_error("handle_rpc_json", e, transport); - } - - return true; -} - diff --git a/tt-rss.php b/tt-rss.php deleted file mode 100644 index 02756146d..000000000 --- a/tt-rss.php +++ /dev/null @@ -1,209 +0,0 @@ - - - - - Tiny Tiny RSS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -
      -
      -
      -
      - -
      -
      - - - - - -
       
      - - - - -
      - -
      -
      - -
      -
      -
      - -
      -
      -
      "> - -
      -
      - -
      - - - - - - - - - - - -
      - -
      -
      - -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      - -
      - -
      -
      - -
      -
      -
      -
      -
      - - -
      - - -
      -
      -
      -
      -
      - - - - - diff --git a/twitter.php b/twitter.php index 9d0c8e509..2c325140b 100644 --- a/twitter.php +++ b/twitter.php @@ -1,4 +1,6 @@ CONSUMER_KEY, 'consumer_secret' => CONSUMER_SECRET, - )); + )); if ($op == 'clear') { unset($_SESSION['oauth']); @@ -43,7 +45,7 @@ $code = $tmhOAuth->request('POST', $tmhOAuth->url('oauth/access_token', ''), array( 'oauth_verifier' => $_REQUEST['oauth_verifier'])); - + if ($code == 200) { $access_token = json_encode($tmhOAuth->extract_params($tmhOAuth->response['response'])); @@ -62,7 +64,7 @@ if ($op == 'register') { - $code = $tmhOAuth->request('POST', + $code = $tmhOAuth->request('POST', $tmhOAuth->url('oauth/request_token', ''), array( 'oauth_callback' => $callback)); @@ -73,8 +75,8 @@ $force = isset($_REQUEST['force']) ? '&force_login=1' : ''; $forcewrite = isset($_REQUEST['force_write']) ? '&oauth_access_type=write' : ''; $forceread = isset($_REQUEST['force_read']) ? '&oauth_access_type=read' : ''; - - $location = $tmhOAuth->url("oauth/{$method}", '') . + + $location = $tmhOAuth->url("oauth/{$method}", '') . "?oauth_token={$_SESSION['oauth']['oauth_token']}{$force}{$forcewrite}{$forceread}"; header("Location: $location"); diff --git a/update.php b/update.php index 7b17c60f8..e6063a9e8 100755 --- a/update.php +++ b/update.php @@ -1,5 +1,7 @@ #!/usr/bin/php diff --git a/viewfeed.js b/viewfeed.js deleted file mode 100644 index 9cb902315..000000000 --- a/viewfeed.js +++ /dev/null @@ -1,2245 +0,0 @@ -var active_post_id = false; - -var article_cache = new Array(); - -var vgroup_last_feed = false; -var post_under_pointer = false; - -var last_requested_article = false; - -var catchup_id_batch = []; -var catchup_timeout_id = false; -var feed_precache_timeout_id = false; -var precache_idle_timeout_id = false; - -var cids_requested = []; - -var has_storage = 'sessionStorage' in window && window['sessionStorage'] !== null; - -function headlines_callback2(transport, offset, background, infscroll_req) { - try { - handle_rpc_json(transport); - - loading_set_progress(25); - - console.log("headlines_callback2 [offset=" + offset + "] B:" + background + " I:" + infscroll_req); - - var is_cat = false; - var feed_id = false; - - var reply = false; - - try { - reply = JSON.parse(transport.responseText); - } catch (e) { - console.error(e); - } - - if (reply) { - - is_cat = reply['headlines']['is_cat']; - feed_id = reply['headlines']['id']; - - if (background) { - var content = reply['headlines']['content']; - - if (getInitParam("cdm_auto_catchup") == 1) { - content = content + "
      "; - } - - cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], content); - return; - } - - setActiveFeedId(feed_id, is_cat); - - try { - if (offset == 0 && infscroll_req == false) { - $("headlines-frame").scrollTop = 0; - } - } catch (e) { }; - - var headlines_count = reply['headlines-info']['count']; - - vgroup_last_feed = reply['headlines-info']['vgroup_last_feed']; - - if (parseInt(headlines_count) < getInitParam("default_article_limit")) { - _infscroll_disable = 1; - } else { - _infscroll_disable = 0; - } - - var counters = reply['counters']; - var articles = reply['articles']; - //var runtime_info = reply['runtime-info']; - - if (offset == 0 && infscroll_req == false) { - dijit.byId("headlines-frame").attr('content', - reply['headlines']['content']); - - dijit.byId("headlines-toolbar").attr('content', - reply['headlines']['toolbar']); - - - if (getInitParam("cdm_auto_catchup") == 1) { - var hsp = $("headlines-spacer"); - if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); - dijit.byId('headlines-frame').domNode.appendChild(hsp); - } - - initHeadlinesMenu(); - - } else { - - if (headlines_count > 0 && feed_id == getActiveFeedId() && is_cat == activeFeedIsCat()) { - console.log("adding some more headlines..."); - - var c = dijit.byId("headlines-frame"); - var ids = getSelectedArticleIds2(); - - $("headlines-tmp").innerHTML = reply['headlines']['content']; - - var hsp = $("headlines-spacer"); - - if (hsp) - c.domNode.removeChild(hsp); - - $$("#headlines-tmp > div").each(function(row) { - if ($$("#headlines-frame DIV[id="+row.id+"]").length == 0) { - row.style.display = 'none'; - c.domNode.appendChild(row); - } else { - row.parentNode.removeChild(row); - } - }); - - if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"}); - - fixHeadlinesOrder(getLoadedArticleIds()); - - if (getInitParam("cdm_auto_catchup") == 1) { - c.domNode.appendChild(hsp); - } - - console.log("restore selected ids: " + ids); - - for (var i = 0; i < ids.length; i++) { - markHeadline(ids[i]); - } - - initHeadlinesMenu(); - - $$("#headlines-frame > div[id*=RROW]").each( - function(child) { - if (!Element.visible(child)) - new Effect.Appear(child, { duration : 0.5 }); - }); - - } else { - console.log("no new headlines received"); - - var hsp = $("headlines-spacer"); - - if (hsp) hsp.innerHTML = ""; - } - } - - if (headlines_count > 0) - cache_headlines(feed_id, is_cat, reply['headlines']['toolbar'], $("headlines-frame").innerHTML); - - if (articles) { - for (var i = 0; i < articles.length; i++) { - var a_id = articles[i]['id']; - cache_set("article:" + a_id, articles[i]['content']); - } - } else { - console.log("no cached articles received"); - } - - // do not precache stuff after fresh feed - if (feed_id != -3) - precache_headlines(); - - if (counters) - parse_counters(counters); - else - request_counters(); - - } else { - console.error("Invalid object received: " + transport.responseText); - dijit.byId("headlines-frame").attr('content', "
      " + - __('Could not update headlines (invalid object received - see error console for details)') + - "
      "); - } - - _infscroll_request_sent = 0; - - notify(""); - - } catch (e) { - exception_error("headlines_callback2", e, transport); - } -} - -function render_article(article) { - try { - dijit.byId("headlines-wrap-inner").addChild( - dijit.byId("content-insert")); - - var c = dijit.byId("content-insert"); - - try { - c.domNode.scrollTop = 0; - } catch (e) { }; - - c.attr('content', article); - - correctHeadlinesOffset(getActiveArticleId()); - - try { - c.focus(); - } catch (e) { }; - - } catch (e) { - exception_error("render_article", e); - } -} - -function showArticleInHeadlines(id) { - - try { - - selectArticles("none"); - - var crow = $("RROW-" + id); - - if (!crow) return; - - var article_is_unread = crow.hasClassName("Unread"); - - crow.removeClassName("Unread"); - - selectArticles('none'); - - var upd_img_pic = $("FUPDPIC-" + id); - - var view_mode = false; - - try { - view_mode = document.forms['main_toolbar_form'].view_mode; - view_mode = view_mode[view_mode.selectedIndex].value; - } catch (e) { - // - } - - if (upd_img_pic && (upd_img_pic.src.match("updated.png") || - upd_img_pic.src.match("fresh_sign.png"))) { - - upd_img_pic.src = "images/blank_icon.gif"; - - cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); - - } else if (article_is_unread && view_mode == "all_articles") { - cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); - } - - markHeadline(id); - - if (article_is_unread) - _force_scheduled_update = true; - - } catch (e) { - exception_error("showArticleInHeadlines", e); - } -} - -function article_callback2(transport, id) { - try { - console.log("article_callback2 " + id); - - handle_rpc_json(transport); - - var reply = false; - - try { - reply = JSON.parse(transport.responseText); - } catch (e) { - console.error(e); - } - - if (reply) { - - var upic = $('FUPDPIC-' + id); - - if (upic) upic.src = 'images/blank_icon.gif'; - - reply.each(function(article) { - if (active_post_id == article['id']) { - render_article(article['content']); - } - cids_requested.remove(article['id']); - - cache_set("article:" + article['id'], article['content']); - }); - -// if (id != last_requested_article) { -// console.log("requested article id is out of sequence, aborting"); -// return; -// } - - } else { - console.error("Invalid object received: " + transport.responseText); - - render_article("
      " + - __('Could not display article (invalid object received - see error console for details)') + "
      "); - } - - request_counters(); - - try { - if (!_infscroll_disable && - $$("#headlines-frame > div[id*=RROW]").last().hasClassName("Selected")) { - - loadMoreHeadlines(); - } - } catch (e) { - console.warn(e); - } - - notify(""); - } catch (e) { - exception_error("article_callback2", e, transport); - } -} - -function view(id) { - try { - console.log("loading article: " + id); - - var cached_article = cache_get("article:" + id); - - console.log("cache check result: " + (cached_article != false)); - - hideAuxDlg(); - - var query = "?op=view&id=" + param_escape(id); - - var neighbor_ids = getRelativePostIds(id); - - /* only request uncached articles */ - - var cids_to_request = []; - - for (var i = 0; i < neighbor_ids.length; i++) { - if (cids_requested.indexOf(neighbor_ids[i]) == -1) - if (!cache_get("article:" + neighbor_ids[i])) { - cids_to_request.push(neighbor_ids[i]); - cids_requested.push(neighbor_ids[i]); - } - } - - console.log("additional ids: " + cids_to_request.toString()); - - query = query + "&cids=" + cids_to_request.toString(); - - var crow = $("RROW-" + id); - var article_is_unread = crow.hasClassName("Unread"); - - active_post_id = id; - showArticleInHeadlines(id); - - precache_headlines(); - - if (!cached_article) { - - var upic = $('FUPDPIC-' + id); - - if (upic) { - upic.src = getInitParam("sign_progress"); - } - - } else if (cached_article && article_is_unread) { - - query = query + "&mode=prefetch"; - - render_article(cached_article); - - } else if (cached_article) { - - query = query + "&mode=prefetch_old"; - render_article(cached_article); - - // if we don't need to request any relative ids, we might as well skip - // the server roundtrip altogether - if (cids_to_request.length == 0) { - - try { - if (!_infscroll_disable && - $$("#headlines-frame > div[id*=RROW]").last().hasClassName("Selected")) { - - loadMoreHeadlines(); - } - } catch (e) { - console.warn(e); - } - - return; - } - } - - last_requested_article = id; - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - article_callback2(transport, id); - } }); - - return false; - - } catch (e) { - exception_error("view", e); - } -} - -function toggleMark(id, client_only) { - try { - var query = "?op=rpc&id=" + id + "&subop=mark"; - - var img = $("FMPIC-" + id); - - if (!img) return; - - if (img.src.match("mark_unset")) { - img.src = img.src.replace("mark_unset", "mark_set"); - img.alt = __("Unstar article"); - query = query + "&mark=1"; - - } else { - img.src = img.src.replace("mark_set", "mark_unset"); - img.alt = __("Star article"); - query = query + "&mark=0"; - } - - cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); - - if (!client_only) { - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - } - - } catch (e) { - exception_error("toggleMark", e); - } -} - -function togglePub(id, client_only, no_effects, note) { - try { - var query = "?op=rpc&id=" + id + "&subop=publ"; - - if (note != undefined) { - query = query + "¬e=" + param_escape(note); - } else { - query = query + "¬e=undefined"; - } - - var img = $("FPPIC-" + id); - - if (!img) return; - - if (img.src.match("pub_unset") || note != undefined) { - img.src = img.src.replace("pub_unset", "pub_set"); - img.alt = __("Unpublish article"); - query = query + "&pub=1"; - - } else { - img.src = img.src.replace("pub_set", "pub_unset"); - img.alt = __("Publish article"); - - query = query + "&pub=0"; - } - - cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); - - if (!client_only) { - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - } - - } catch (e) { - exception_error("togglePub", e); - } -} - -function moveToPost(mode) { - - try { - - var rows = getVisibleArticleIds(); - - var prev_id = false; - var next_id = false; - - if (!$('RROW-' + active_post_id)) { - active_post_id = false; - } - - if (active_post_id == false) { - next_id = getFirstVisibleHeadlineId(); - prev_id = getLastVisibleHeadlineId(); - } else { - for (var i = 0; i < rows.length; i++) { - if (rows[i] == active_post_id) { - prev_id = rows[i-1]; - next_id = rows[i+1]; - } - } - } - - if (mode == "next") { - if (next_id) { - if (isCdmMode()) { - - cdmExpandArticle(next_id); - cdmScrollToArticleId(next_id); - - } else { - correctHeadlinesOffset(next_id); - view(next_id, getActiveFeedId()); - } - } - } - - if (mode == "prev") { - if (prev_id) { - if (isCdmMode()) { - cdmExpandArticle(prev_id); - cdmScrollToArticleId(prev_id); - } else { - correctHeadlinesOffset(prev_id); - view(prev_id, getActiveFeedId()); - } - } - } - - } catch (e) { - exception_error("moveToPost", e); - } -} - -function toggleSelected(id, force_on) { - try { - - var cb = $("RCHK-" + id); - var row = $("RROW-" + id); - - if (row) { - if (row.hasClassName('Selected') && !force_on) { - row.removeClassName('Selected'); - if (cb) cb.checked = false; - } else { - row.addClassName('Selected'); - if (cb) cb.checked = true; - } - } - } catch (e) { - exception_error("toggleSelected", e); - } -} - -function toggleUnread_afh(effect) { - try { - - var elem = effect.element; - elem.style.backgroundColor = ""; - - } catch (e) { - exception_error("toggleUnread_afh", e); - } -} - -function toggleUnread(id, cmode, effect) { - try { - - var row = $("RROW-" + id); - if (row) { - if (cmode == undefined || cmode == 2) { - if (row.hasClassName("Unread")) { - row.removeClassName("Unread"); - - if (effect) { - new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5", - afterFinish: toggleUnread_afh, - queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } ); - } - - } else { - row.addClassName("Unread"); - } - - } else if (cmode == 0) { - - row.removeClassName("Unread"); - - if (effect) { - new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5", - afterFinish: toggleUnread_afh, - queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } ); - } - - } else if (cmode == 1) { - row.addClassName("Unread"); - } - - if (cmode == undefined) cmode = 2; - - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=" + param_escape(cmode) + "&ids=" + param_escape(id); - -// notify_progress("Loading, please wait..."); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - - } - - } catch (e) { - exception_error("toggleUnread", e); - } -} - -function selectionRemoveLabel(id, ids) { - try { - - if (!ids) ids = getSelectedArticleIds2(); - - if (ids.length == 0) { - alert(__("No articles are selected.")); - return; - } - - var query = "?op=rpc&subop=removeFromLabel&ids=" + - param_escape(ids.toString()) + "&lid=" + param_escape(id); - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - show_labels_in_headlines(transport); - } }); - - } catch (e) { - exception_error("selectionAssignLabel", e); - - } -} - -function selectionAssignLabel(id, ids) { - try { - - if (!ids) ids = getSelectedArticleIds2(); - - if (ids.length == 0) { - alert(__("No articles are selected.")); - return; - } - - var query = "?op=rpc&subop=assignToLabel&ids=" + - param_escape(ids.toString()) + "&lid=" + param_escape(id); - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - show_labels_in_headlines(transport); - } }); - - } catch (e) { - exception_error("selectionAssignLabel", e); - - } -} - -function selectionToggleUnread(set_state, callback, no_error) { - try { - var rows = getSelectedArticleIds2(); - - if (rows.length == 0 && !no_error) { - alert(__("No articles are selected.")); - return; - } - - for (var i = 0; i < rows.length; i++) { - var row = $("RROW-" + rows[i]); - if (row) { - if (set_state == undefined) { - if (row.hasClassName("Unread")) { - row.removeClassName("Unread"); - } else { - row.addClassName("Unread"); - } - } - - if (set_state == false) { - row.removeClassName("Unread"); - } - - if (set_state == true) { - row.addClassName("Unread"); - } - } - } - - if (rows.length > 0) { - - var cmode = ""; - - if (set_state == undefined) { - cmode = "2"; - } else if (set_state == true) { - cmode = "1"; - } else if (set_state == false) { - cmode = "0"; - } - - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=" + cmode + "&ids=" + param_escape(rows.toString()); - - notify_progress("Loading, please wait..."); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - if (callback) callback(transport); - } }); - - } - - } catch (e) { - exception_error("selectionToggleUnread", e); - } -} - -function selectionToggleMarked() { - try { - - var rows = getSelectedArticleIds2(); - - if (rows.length == 0) { - alert(__("No articles are selected.")); - return; - } - - for (var i = 0; i < rows.length; i++) { - toggleMark(rows[i], true, true); - } - - if (rows.length > 0) { - - var query = "?op=rpc&subop=markSelected&ids=" + - param_escape(rows.toString()) + "&cmode=2"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - - } - - } catch (e) { - exception_error("selectionToggleMarked", e); - } -} - -function selectionTogglePublished() { - try { - - var rows = getSelectedArticleIds2(); - - if (rows.length == 0) { - alert(__("No articles are selected.")); - return; - } - - for (var i = 0; i < rows.length; i++) { - togglePub(rows[i], true, true); - } - - if (rows.length > 0) { - - var query = "?op=rpc&subop=publishSelected&ids=" + - param_escape(rows.toString()) + "&cmode=2"; - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - - } - - } catch (e) { - exception_error("selectionToggleMarked", e); - } -} - -function getSelectedArticleIds2() { - - var rv = []; - - $$("#headlines-frame > div[id*=RROW][class*=Selected]").each( - function(child) { - rv.push(child.id.replace("RROW-", "")); - }); - - return rv; -} - -function getLoadedArticleIds() { - var rv = []; - - var children = $$("#headlines-frame > div[id*=RROW-]"); - - children.each(function(child) { - rv.push(child.id.replace("RROW-", "")); - }); - - return rv; - -} - -// mode = all,none,unread,invert -function selectArticles(mode) { - try { - - var children = $$("#headlines-frame > div[id*=RROW]"); - - children.each(function(child) { - var id = child.id.replace("RROW-", ""); - var cb = $("RCHK-" + id); - - if (mode == "all") { - child.addClassName("Selected"); - cb.checked = true; - } else if (mode == "unread") { - if (child.hasClassName("Unread")) { - child.addClassName("Selected"); - cb.checked = true; - } else { - child.removeClassName("Selected"); - cb.checked = false; - } - } else if (mode == "invert") { - if (child.hasClassName("Selected")) { - child.removeClassName("Selected"); - cb.checked = false; - } else { - child.addClassName("Selected"); - cb.checked = true; - } - - } else { - child.removeClassName("Selected"); - cb.checked = false; - } - }); - - } catch (e) { - exception_error("selectArticles", e); - } -} - -function catchupPage() { - - var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); - - var str = __("Mark all visible articles in %s as read?"); - - str = str.replace("%s", fn); - - if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { - return; - } - - selectArticles('all'); - selectionToggleUnread(false, 'viewCurrentFeed()', true); - selectArticles('none'); -} - -function deleteSelection() { - - try { - - var rows = getSelectedArticleIds2(); - - if (rows.length == 0) { - alert(__("No articles are selected.")); - return; - } - - var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); - var str; - - if (getActiveFeedId() != 0) { - str = __("Delete %d selected articles in %s?"); - } else { - str = __("Delete %d selected articles?"); - } - - str = str.replace("%d", rows.length); - str = str.replace("%s", fn); - - if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { - return; - } - - query = "?op=rpc&subop=delete&ids=" + param_escape(rows); - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - viewCurrentFeed(); - } }); - - } catch (e) { - exception_error("deleteSelection", e); - } -} - -function archiveSelection() { - - try { - - var rows = getSelectedArticleIds2(); - - if (rows.length == 0) { - alert(__("No articles are selected.")); - return; - } - - var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); - var str; - var op; - - if (getActiveFeedId() != 0) { - str = __("Archive %d selected articles in %s?"); - op = "archive"; - } else { - str = __("Move %d archived articles back?"); - op = "unarchive"; - } - - str = str.replace("%d", rows.length); - str = str.replace("%s", fn); - - if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { - return; - } - - query = "?op=rpc&subop="+op+"&ids=" + param_escape(rows); - - console.log(query); - - for (var i = 0; i < rows.length; i++) { - cache_delete("article:" + rows[i]); - } - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - viewCurrentFeed(); - } }); - - } catch (e) { - exception_error("archiveSelection", e); - } -} - -function catchupSelection() { - - try { - - var rows = getSelectedArticleIds2(); - - if (rows.length == 0) { - alert(__("No articles are selected.")); - return; - } - - var fn = getFeedName(getActiveFeedId(), activeFeedIsCat()); - - var str = __("Mark %d selected articles in %s as read?"); - - str = str.replace("%d", rows.length); - str = str.replace("%s", fn); - - if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) { - return; - } - - selectionToggleUnread(false, 'viewCurrentFeed()', true); - - } catch (e) { - exception_error("catchupSelection", e); - } -} - -function editArticleTags(id) { - var query = "backend.php?op=dlg&id=editArticleTags¶m=" + param_escape(id); - - if (dijit.byId("editTagsDlg")) - dijit.byId("editTagsDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "editTagsDlg", - title: __("Edit article Tags"), - style: "width: 600px", - execute: function() { - if (this.validate()) { - var query = dojo.objectToQuery(this.attr('value')); - - notify_progress("Saving article tags...", true); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - dialog.hide(); - - var data = JSON.parse(transport.responseText); - - if (data) { - var tags_str = article.tags; - var id = tags_str.id; - - var tags = $("ATSTR-" + id); - var tooltip = dijit.byId("ATSTRTIP-" + id); - - if (tags) tags.innerHTML = tags_str.content; - if (tooltip) tooltip.attr('label', tags_str.content_full); - - cache_delete("article:" + id); - } - - }}); - } - }, - href: query, - }); - - var tmph = dojo.connect(dialog, 'onLoad', function() { - dojo.disconnect(tmph); - - new Ajax.Autocompleter('tags_str', 'tags_choices', - "backend.php?op=rpc&subop=completeTags", - { tokens: ',', paramName: "search" }); - }); - - dialog.show(); - -} - -function cdmScrollToArticleId(id) { - try { - var ctr = $("headlines-frame"); - var e = $("RROW-" + id); - - if (!e || !ctr) return; - - ctr.scrollTop = e.offsetTop; - - } catch (e) { - exception_error("cdmScrollToArticleId", e); - } -} - -function getActiveArticleId() { - return active_post_id; -} - -function postMouseIn(id) { - post_under_pointer = id; -} - -function postMouseOut(id) { - post_under_pointer = false; -} - -function headlines_scroll_handler(e) { - try { - var hsp = $("headlines-spacer"); - - if (!_infscroll_disable) { - if (hsp && (e.scrollTop + e.offsetHeight > hsp.offsetTop) || - e.scrollTop + e.offsetHeight > e.scrollHeight - 100) { - - if (hsp) - hsp.innerHTML = " " + - __("Loading, please wait..."); - - loadMoreHeadlines(); - return; - - } - } else { - if (hsp) hsp.innerHTML = ""; - } - - if (getInitParam("cdm_auto_catchup") == 1) { - - $$("#headlines-frame > div[id*=RROW][class*=Unread]").each( - function(child) { - if ($("headlines-frame").scrollTop > - (child.offsetTop + child.offsetHeight/2)) { - - var id = child.id.replace("RROW-", ""); - - if (catchup_id_batch.indexOf(id) == -1) - catchup_id_batch.push(id); - - //console.log("auto_catchup_batch: " + catchup_id_batch.toString()); - } - }); - - if (catchup_id_batch.length > 0) { - window.clearTimeout(catchup_timeout_id); - - if (!_infscroll_request_sent) { - catchup_timeout_id = window.setTimeout('catchupBatchedArticles()', - 2000); - } - } - } - - } catch (e) { - console.warn("headlines_scroll_handler: " + e); - } -} - -function catchupBatchedArticles() { - try { - if (catchup_id_batch.length > 0 && !_infscroll_request_sent) { - - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=0&ids=" + param_escape(catchup_id_batch.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - - catchup_id_batch.each(function(id) { - var elem = $("RROW-" + id); - if (elem) elem.removeClassName("Unread"); - }); - - catchup_id_batch = []; - } }); - } - - } catch (e) { - exception_error("catchupBatchedArticles", e); - } -} - -function catchupRelativeToArticle(below, id) { - - try { - - if (!id) id = getActiveArticleId(); - - if (!id) { - alert(__("No article is selected.")); - return; - } - - var visible_ids = getVisibleArticleIds(); - - var ids_to_mark = new Array(); - - if (!below) { - for (var i = 0; i < visible_ids.length; i++) { - if (visible_ids[i] != id) { - var e = $("RROW-" + visible_ids[i]); - - if (e && e.hasClassName("Unread")) { - ids_to_mark.push(visible_ids[i]); - } - } else { - break; - } - } - } else { - for (var i = visible_ids.length-1; i >= 0; i--) { - if (visible_ids[i] != id) { - var e = $("RROW-" + visible_ids[i]); - - if (e && e.hasClassName("Unread")) { - ids_to_mark.push(visible_ids[i]); - } - } else { - break; - } - } - } - - if (ids_to_mark.length == 0) { - alert(__("No articles found to mark")); - } else { - var msg = __("Mark %d article(s) as read?").replace("%d", ids_to_mark.length); - - if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg)) { - - for (var i = 0; i < ids_to_mark.length; i++) { - var e = $("RROW-" + ids_to_mark[i]); - e.removeClassName("Unread"); - } - - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=0" + "&ids=" + param_escape(ids_to_mark.toString()); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - - } - } - - } catch (e) { - exception_error("catchupRelativeToArticle", e); - } -} - -function cdmExpandArticle(id) { - try { - - hideAuxDlg(); - - var elem = $("CICD-" + active_post_id); - - var upd_img_pic = $("FUPDPIC-" + id); - - if (upd_img_pic && (upd_img_pic.src.match("updated.png") || - upd_img_pic.src.match("fresh_sign.png"))) { - - upd_img_pic.src = "images/blank_icon.gif"; - } - - if (id == active_post_id && Element.visible(elem)) - return true; - - selectArticles("none"); - - var old_offset = $("RROW-" + id).offsetTop; - - if (active_post_id && elem && !getInitParam("cdm_expanded")) { - Element.hide(elem); - Element.show("CEXC-" + active_post_id); - } - - active_post_id = id; - - elem = $("CICD-" + id); - - if (!Element.visible(elem)) { - Element.show(elem); - Element.hide("CEXC-" + id); - - if ($("CWRAP-" + id).innerHTML == "") { - - $("FUPDPIC-" + id).src = "images/indicator_tiny.gif"; - - $("CWRAP-" + id).innerHTML = "
      " + - __("Loading, please wait...") + "
      "; - - var query = "?op=rpc&subop=cdmGetArticle&id=" + param_escape(id); - - var neighbor_ids = getRelativePostIds(id); - - /* only request uncached articles */ - var cids_to_request = []; - - for (var i = 0; i < neighbor_ids.length; i++) { - if (cids_requested.indexOf(neighbor_ids[i]) == -1) - if ($("CWRAP-" + neighbor_ids[i]).innerHTML == "") { - cids_to_request.push(neighbor_ids[i]); - cids_requested.push(neighbor_ids[i]); - } - } - - console.log("additional ids: " + cids_to_request.toString()); - - query = query + "&cids=" + cids_to_request.toString(); - - console.log(query); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - - $("FUPDPIC-" + id).src = 'images/blank_icon.gif'; - - handle_rpc_json(transport); - - var reply = JSON.parse(transport.responseText); - - reply.each(function(article) { - $("CWRAP-" + article['id']).innerHTML = article['content']; - cids_requested.remove(article['id']); - }); - }}); - - } - } - - var new_offset = $("RROW-" + id).offsetTop; - - $("headlines-frame").scrollTop += (new_offset-old_offset); - - if ($("RROW-" + id).offsetTop != old_offset) - $("headlines-frame").scrollTop = new_offset; - - toggleUnread(id, 0, true); - toggleSelected(id); - - } catch (e) { - exception_error("cdmExpandArticle", e); - } - - return false; -} - -function fixHeadlinesOrder(ids) { - try { - for (var i = 0; i < ids.length; i++) { - var e = $("RROW-" + ids[i]); - - if (e) { - if (i % 2 == 0) { - e.removeClassName("even"); - e.addClassName("odd"); - } else { - e.removeClassName("odd"); - e.addClassName("even"); - } - } - } - } catch (e) { - exception_error("fixHeadlinesOrder", e); - } -} - -function getArticleUnderPointer() { - return post_under_pointer; -} - -function zoomToArticle(event, id) { - try { - var cached_article = cache_get("article: " + id); - - if (dijit.byId("ATAB-" + id)) - if (!event || !event.shiftKey) - return dijit.byId("content-tabs").selectChild(dijit.byId("ATAB-" + id)); - - if (dijit.byId("ATSTRTIP-" + id)) - dijit.byId("ATSTRTIP-" + id).destroyRecursive(); - - if (cached_article) { - //closeArticlePanel(); - - var article_pane = new dijit.layout.ContentPane({ - title: __("Loading...") , content: cached_article, - style: 'padding : 0px;', - id: 'ATAB-' + id, - closable: true }); - - dijit.byId("content-tabs").addChild(article_pane); - - if (!event || !event.shiftKey) - dijit.byId("content-tabs").selectChild(article_pane); - - if ($("PTITLE-" + id)) - article_pane.attr('title', $("PTITLE-" + id).innerHTML); - - } else { - - var query = "?op=rpc&subop=getArticles&ids=" + param_escape(id); - - notify_progress("Loading, please wait...", true); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - - var reply = JSON.parse(transport.responseText); - - if (reply) { - //closeArticlePanel(); - - var content = reply[0]['content']; - - var article_pane = new dijit.layout.ContentPane({ - title: "article-" + id , content: content, - style: 'padding : 0px;', - id: 'ATAB-' + id, - closable: true }); - - dijit.byId("content-tabs").addChild(article_pane); - - if (!event || !event.shiftKey) - dijit.byId("content-tabs").selectChild(article_pane); - - if ($("PTITLE-" + id)) - article_pane.attr('title', $("PTITLE-" + id).innerHTML); - } - - } }); - } - - } catch (e) { - exception_error("zoomToArticle", e); - } -} - -function scrollArticle(offset) { - try { - if (!isCdmMode()) { - var ci = $("content-insert"); - if (ci) { - ci.scrollTop += offset; - } - } else { - var hi = $("headlines-frame"); - if (hi) { - hi.scrollTop += offset; - } - - } - } catch (e) { - exception_error("scrollArticle", e); - } -} - -function show_labels_in_headlines(transport) { - try { - var data = JSON.parse(transport.responseText); - - if (data) { - data['info-for-headlines'].each(function(elem) { - var ctr = $("HLLCTR-" + elem.id); - - if (ctr) ctr.innerHTML = elem.labels; - }); - - cache_headlines(getActiveFeedId(), activeFeedIsCat(), null, $("headlines-frame").innerHTML); - - } - } catch (e) { - exception_error("show_labels_in_headlines", e); - } -} - -/* function toggleHeadlineActions() { - try { - var e = $("headlineActionsBody"); - var p = $("headlineActionsDrop"); - - if (!Element.visible(e)) { - Element.show(e); - } else { - Element.hide(e); - } - - e.scrollTop = 0; - e.style.left = (p.offsetLeft + 1) + "px"; - e.style.top = (p.offsetTop + p.offsetHeight + 2) + "px"; - - } catch (e) { - exception_error("toggleHeadlineActions", e); - } -} */ - -/* function publishWithNote(id, def_note) { - try { - if (!def_note) def_note = ''; - - var note = prompt(__("Please enter a note for this article:"), def_note); - - if (note != undefined) { - togglePub(id, false, false, note); - } - - } catch (e) { - exception_error("publishWithNote", e); - } -} */ - -function emailArticle(id) { - try { - if (!id) { - var ids = getSelectedArticleIds2(); - - if (ids.length == 0) { - alert(__("No articles are selected.")); - return; - } - - id = ids.toString(); - } - - if (dijit.byId("emailArticleDlg")) - dijit.byId("emailArticleDlg").destroyRecursive(); - - var query = "backend.php?op=dlg&id=emailArticle¶m=" + param_escape(id); - - dialog = new dijit.Dialog({ - id: "emailArticleDlg", - title: __("Forward article by email"), - style: "width: 600px", - execute: function() { - if (this.validate()) { - - new Ajax.Request("backend.php", { - parameters: dojo.objectToQuery(this.attr('value')), - onComplete: function(transport) { - - var reply = JSON.parse(transport.responseText); - - var error = reply['error']; - - if (error) { - alert(__('Error sending email:') + ' ' + error); - } else { - notify_info('Your message has been sent.'); - dialog.hide(); - } - - } }); - } - }, - href: query}); - - var tmph = dojo.connect(dialog, 'onLoad', function() { - dojo.disconnect(tmph); - - new Ajax.Autocompleter('emailArticleDlg_destination', 'emailArticleDlg_dst_choices', - "backend.php?op=rpc&subop=completeEmails", - { tokens: '', paramName: "search" }); - }); - - dialog.show(); - - /* displayDlg('emailArticle', id, - function () { - document.forms['article_email_form'].destination.focus(); - - new Ajax.Autocompleter('destination', 'destination_choices', - "backend.php?op=rpc&subop=completeEmails", - { tokens: '', paramName: "search" }); - - }); */ - - } catch (e) { - exception_error("emailArticle", e); - } -} - -function dismissArticle(id) { - try { - var elem = $("RROW-" + id); - - toggleUnread(id, 0, true); - - new Effect.Fade(elem, {duration : 0.5}); - - active_post_id = false; - - } catch (e) { - exception_error("dismissArticle", e); - } -} - -function dismissSelectedArticles() { - try { - - var ids = getVisibleArticleIds(); - var tmp = []; - var sel = []; - - for (var i = 0; i < ids.length; i++) { - var elem = $("RROW-" + ids[i]); - - if (elem.className && elem.hasClassName("Selected") && - ids[i] != active_post_id) { - new Effect.Fade(elem, {duration : 0.5}); - sel.push(ids[i]); - } else { - tmp.push(ids[i]); - } - } - - if (sel.length > 0) - selectionToggleUnread(false); - - fixHeadlinesOrder(tmp); - - } catch (e) { - exception_error("dismissSelectedArticles", e); - } -} - -function dismissReadArticles() { - try { - - var ids = getVisibleArticleIds(); - var tmp = []; - - for (var i = 0; i < ids.length; i++) { - var elem = $("RROW-" + ids[i]); - - if (elem.className && !elem.hasClassName("Unread") && - !elem.hasClassName("Selected")) { - - new Effect.Fade(elem, {duration : 0.5}); - } else { - tmp.push(ids[i]); - } - } - - fixHeadlinesOrder(tmp); - - } catch (e) { - exception_error("dismissSelectedArticles", e); - } -} - -function getVisibleArticleIds() { - var ids = []; - - try { - - getLoadedArticleIds().each(function(id) { - var elem = $("RROW-" + id); - if (elem && Element.visible(elem)) - ids.push(id); - }); - - } catch (e) { - exception_error("getVisibleArticleIds", e); - } - - return ids; -} - -function cdmClicked(event, id) { - try { - //var shift_key = event.shiftKey; - - hideAuxDlg(); - - if (!event.ctrlKey) { - - if (!getInitParam("cdm_expanded")) { - return cdmExpandArticle(id); - } else { - - selectArticles("none"); - toggleSelected(id); - - var elem = $("RROW-" + id); - - if (elem) - elem.removeClassName("Unread"); - - var upd_img_pic = $("FUPDPIC-" + id); - - if (upd_img_pic && (upd_img_pic.src.match("updated.png") || - upd_img_pic.src.match("fresh_sign.png"))) { - - upd_img_pic.src = "images/blank_icon.gif"; - } - - active_post_id = id; - - var query = "?op=rpc&subop=catchupSelected" + - "&cmode=0&ids=" + param_escape(id); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - handle_rpc_json(transport); - } }); - - return true; - } - - } else { - toggleSelected(id, true); - toggleUnread(id, 0, false); - zoomToArticle(event, id); - } - - } catch (e) { - exception_error("cdmClicked"); - } - - return false; -} - -function postClicked(event, id) { - try { - - if (!event.ctrlKey) { - return true; - } else { - postOpenInNewTab(event, id); - return false; - } - - } catch (e) { - exception_error("postClicked"); - } -} - -function hlOpenInNewTab(event, id) { - toggleUnread(id, 0, false); - zoomToArticle(event, id); -} - -function postOpenInNewTab(event, id) { - closeArticlePanel(id); - zoomToArticle(event, id); -} - -function hlClicked(event, id) { - try { - if (event.which == 2) { - view(id); - return true; - } else if (event.altKey) { - openArticleInNewWindow(id); - } else if (!event.ctrlKey) { - view(id); - return false; - } else { - toggleSelected(id); - toggleUnread(id, 0, false); - zoomToArticle(event, id); - return false; - } - - } catch (e) { - exception_error("hlClicked"); - } -} - -function getFirstVisibleHeadlineId() { - var rows = getVisibleArticleIds(); - return rows[0]; - -} - -function getLastVisibleHeadlineId() { - var rows = getVisibleArticleIds(); - return rows[rows.length-1]; -} - -function openArticleInNewWindow(id) { - toggleUnread(id, 0, false); - window.open("backend.php?op=la&id=" + id); -} - -function isCdmMode() { - return getInitParam("combined_display_mode"); -} - -function markHeadline(id) { - var row = $("RROW-" + id); - if (row) { - var check = $("RCHK-" + id); - - if (check) { - check.checked = true; - } - - row.addClassName("Selected"); - } -} - -function getRelativePostIds(id, limit) { - - var tmp = []; - - try { - - if (!limit) limit = 6; //3 - - var ids = getVisibleArticleIds(); - - for (var i = 0; i < ids.length; i++) { - if (ids[i] == id) { - for (var k = 1; k <= limit; k++) { - //if (i > k-1) tmp.push(ids[i-k]); - if (i < ids.length-k) tmp.push(ids[i+k]); - } - break; - } - } - - } catch (e) { - exception_error("getRelativePostIds", e); - } - - return tmp; -} - -function correctHeadlinesOffset(id) { - - try { - - var container = $("headlines-frame"); - var row = $("RROW-" + id); - - var viewport = container.offsetHeight; - - var rel_offset_top = row.offsetTop - container.scrollTop; - var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop; - - //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom); - //console.log("Vport: " + viewport); - - if (rel_offset_top <= 0 || rel_offset_top > viewport) { - container.scrollTop = row.offsetTop; - } else if (rel_offset_bottom > viewport) { - - /* doesn't properly work with Opera in some cases because - Opera fucks up element scrolling */ - - container.scrollTop = row.offsetTop + row.offsetHeight - viewport; - } - - } catch (e) { - exception_error("correctHeadlinesOffset", e); - } - -} - -function headlineActionsChange(elem) { - try { - eval(elem.value); - elem.attr('value', 'false'); - } catch (e) { - exception_error("headlineActionsChange", e); - } -} - -function closeArticlePanel() { - - var tabs = dijit.byId("content-tabs"); - var child = tabs.selectedChildWidget; - - if (child && tabs.getIndexOfChild(child) > 0) { - tabs.removeChild(child); - child.destroy(); - } else { - if (dijit.byId("content-insert")) - dijit.byId("headlines-wrap-inner").removeChild( - dijit.byId("content-insert")); - } -} - -function initHeadlinesMenu() { - try { - if (dijit.byId("headlinesMenu")) - dijit.byId("headlinesMenu").destroyRecursive(); - - var ids = []; - - if (!isCdmMode()) { - nodes = $$("#headlines-frame > div[id*=RROW]"); - } else { - nodes = $$("#headlines-frame span[id*=RTITLE]"); - } - - nodes.each(function(node) { - ids.push(node.id); - }); - - var menu = new dijit.Menu({ - id: "headlinesMenu", - targetNodeIds: ids, - }); - - var tmph = dojo.connect(menu, '_openMyself', function (event) { - var callerNode = event.target, match = null, tries = 0; - - while (match == null && callerNode && tries <= 3) { - match = callerNode.id.match("^[A-Z]+[-]([0-9]+)$"); - callerNode = callerNode.parentNode; - ++tries; - } - - if (match) this.callerRowId = parseInt(match[1]); - - }); - -/* if (!isCdmMode()) - menu.addChild(new dijit.MenuItem({ - label: __("View article"), - onClick: function(event) { - view(this.getParent().callerRowId); - }})); */ - - menu.addChild(new dijit.MenuItem({ - label: __("Open original article"), - onClick: function(event) { - openArticleInNewWindow(this.getParent().callerRowId); - }})); - - menu.addChild(new dijit.MenuItem({ - label: __("View in a tt-rss tab"), - onClick: function(event) { - hlOpenInNewTab(event, this.getParent().callerRowId); - }})); - - menu.addChild(new dijit.MenuSeparator()); - - menu.addChild(new dijit.MenuItem({ - label: __("Mark above as read"), - onClick: function(event) { - catchupRelativeToArticle(0, this.getParent().callerRowId); - }})); - - menu.addChild(new dijit.MenuItem({ - label: __("Mark below as read"), - onClick: function(event) { - catchupRelativeToArticle(1, this.getParent().callerRowId); - }})); - - - var labels = dijit.byId("feedTree").model.getItemsInCategory(-2); - - if (labels) { - - menu.addChild(new dijit.MenuSeparator()); - - var labelAddMenu = new dijit.Menu({ownerMenu: menu}); - var labelDelMenu = new dijit.Menu({ownerMenu: menu}); - - labels.each(function(label) { - var id = label.id[0]; - var bare_id = id.substr(id.indexOf(":")+1); - var name = label.name[0]; - - bare_id = -11-bare_id; - - labelAddMenu.addChild(new dijit.MenuItem({ - label: name, - labelId: bare_id, - onClick: function(event) { - selectionAssignLabel(this.labelId, - [this.getParent().ownerMenu.callerRowId]); - }})); - - labelDelMenu.addChild(new dijit.MenuItem({ - label: name, - labelId: bare_id, - onClick: function(event) { - selectionRemoveLabel(this.labelId, - [this.getParent().ownerMenu.callerRowId]); - }})); - - }); - - menu.addChild(new dijit.PopupMenuItem({ - label: __("Assign label"), - popup: labelAddMenu, - })); - - menu.addChild(new dijit.PopupMenuItem({ - label: __("Remove label"), - popup: labelDelMenu, - })); - - } - - menu.startup(); - - } catch (e) { - exception_error("initHeadlinesMenu", e); - } -} - -function tweetArticle(id) { - try { - var query = "?op=rpc&subop=getTweetInfo&id=" + param_escape(id); - - console.log(query); - - var d = new Date(); - var ts = d.getTime(); - - var w = window.open('backend.php?op=loading', 'ttrss_tweet', - "status=0,toolbar=0,location=0,width=500,height=400,scrollbars=1,menubar=0"); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - var ti = JSON.parse(transport.responseText); - - var share_url = "http://twitter.com/share?_=" + ts + - "&text=" + param_escape(ti.title) + - "&url=" + param_escape(ti.link); - - w.location.href = share_url; - - } }); - - - } catch (e) { - exception_error("tweetArticle", e); - } -} - -function editArticleNote(id) { - try { - - var query = "backend.php?op=dlg&id=editArticleNote¶m=" + param_escape(id); - - if (dijit.byId("editNoteDlg")) - dijit.byId("editNoteDlg").destroyRecursive(); - - dialog = new dijit.Dialog({ - id: "editNoteDlg", - title: __("Edit article note"), - style: "width: 600px", - execute: function() { - if (this.validate()) { - var query = dojo.objectToQuery(this.attr('value')); - - notify_progress("Saving article note...", true); - - new Ajax.Request("backend.php", { - parameters: query, - onComplete: function(transport) { - notify(''); - dialog.hide(); - - var reply = JSON.parse(transport.responseText); - - cache_delete("article:" + id); - - var elem = $("POSTNOTE-" + id); - - if (elem) { - Element.hide(elem); - elem.innerHTML = reply.note; - - if (reply.raw_length != 0) - new Effect.Appear(elem); - } - - }}); - } - }, - href: query, - }); - - dialog.show(); - - } catch (e) { - exception_error("editArticleNote", e); - } -} - -function player(elem) { - var aid = elem.getAttribute("audio-id"); - var status = elem.getAttribute("status"); - - var audio = $(aid); - - if (audio) { - if (status == 0) { - audio.play(); - status = 1; - elem.innerHTML = __("Playing..."); - elem.title = __("Click to pause"); - elem.addClassName("playing"); - } else { - audio.pause(); - status = 0; - elem.innerHTML = __("Play"); - elem.title = __("Click to play"); - elem.removeClassName("playing"); - } - - elem.setAttribute("status", status); - } else { - alert("Your browser doesn't seem to support HTML5 audio."); - } -} - -function cache_set(id, obj) { - //console.log("cache_set: " + id); - if (has_storage) - try { - sessionStorage[id] = obj; - } catch (e) { - sessionStorage.clear(); - } -} - -function cache_get(id) { - if (has_storage) - return sessionStorage[id]; -} - -function cache_clear() { - if (has_storage) - sessionStorage.clear(); -} - -function cache_delete(id) { - if (has_storage) - sessionStorage.removeItem(id); -} - -function cache_headlines(feed, is_cat, toolbar_obj, content_obj) { - if (toolbar_obj && content_obj) { - cache_set("feed:" + feed + ":" + is_cat, - JSON.stringify({toolbar: toolbar_obj, content: content_obj})); - } else { - try { - obj = cache_get("feed:" + feed + ":" + is_cat); - - if (obj) { - obj = JSON.parse(obj); - - if (toolbar_obj) obj.toolbar = toolbar_obj; - if (content_obj) obj.content = content_obj; - - cache_set("feed:" + feed + ":" + is_cat, JSON.stringify(obj)); - } - - } catch (e) { - console.warn("cache_headlines failed: " + e); - } - } -} - -function render_local_headlines(feed, is_cat, obj) { - try { - - dijit.byId("headlines-toolbar").attr('content', - obj.toolbar); - - dijit.byId("headlines-frame").attr('content', - obj.content); - - dojo.parser.parse('headlines-toolbar'); - - $("headlines-frame").scrollTop = 0; - selectArticles('none'); - setActiveFeedId(feed, is_cat); - initHeadlinesMenu(); - - precache_headlines(); - - } catch (e) { - exception_error("render_local_headlines", e); - } -} - -function precache_headlines_idle() { - try { - if (!feed_precache_timeout_id) { - var feeds = dijit.byId("feedTree").getVisibleUnreadFeeds(); - var uncached = []; - - feeds.each(function(item) { - if (parseInt(item[0]) > 0 && !cache_get("feed:" + item[0] + ":" + item[1])) - uncached.push(item); - }); - - if (uncached.length > 0) { - var rf = uncached[Math.floor(Math.random()*uncached.length)]; - viewfeed(rf[0], '', rf[1], 0, true); - } - } - precache_idle_timeout_id = setTimeout("precache_headlines_idle()", 1000*30); - - } catch (e) { - exception_error("precache_headlines_idle", e); - } -} - -function precache_headlines() { - try { - - if (!feed_precache_timeout_id) { - feed_precache_timeout_id = window.setTimeout(function() { - var nuf = getNextUnreadFeed(getActiveFeedId(), activeFeedIsCat()); - var nf = dijit.byId("feedTree").getNextFeed(getActiveFeedId(), activeFeedIsCat()); - - if (nuf && !cache_get("feed:" + nuf + ":" + activeFeedIsCat())) - viewfeed(nuf, '', activeFeedIsCat(), 0, true); - - if (nf != nuf && nf && !cache_get("feed:" + nf[0] + ":" + nf[1])) - viewfeed(nf[0], '', nf[1], 0, true); - - window.setTimeout(function() { - feed_precache_timeout_id = false; - }, 3000); - }, 1000); - } - - } catch (e) { - exception_error("precache_headlines", e); - } -} - -function shareArticle(id) { - try { - if (dijit.byId("shareArticleDlg")) - dijit.byId("shareArticleDlg").destroyRecursive(); - - var query = "backend.php?op=dlg&id=shareArticle¶m=" + param_escape(id); - - dialog = new dijit.Dialog({ - id: "shareArticleDlg", - title: __("Share article by URL"), - style: "width: 600px", - href: query}); - - dialog.show(); - - } catch (e) { - exception_error("emailArticle", e); - } -} - - -- cgit v1.2.3