]> git.wh0rd.org Git - tt-rss.git/commitdiff
overall directory tree cleanup
authorAndrew Dolgov <fox@fakecake.org>
Sun, 11 Dec 2011 19:59:25 +0000 (23:59 +0400)
committerAndrew Dolgov <fox@fakecake.org>
Sun, 11 Dec 2011 19:59:25 +0000 (23:59 +0400)
70 files changed:
FeedTree.js [deleted file]
PrefFeedTree.js [deleted file]
PrefFilterTree.js [deleted file]
PrefLabelTree.js [deleted file]
backend.php
db-prefs.php [deleted file]
db-updater.php
db.php [deleted file]
deprecated.js [deleted file]
digest.js [deleted file]
digest.php
errors.php
feedlist.js [deleted file]
functions.js [deleted file]
functions.php [deleted file]
image.php
include/db-prefs.php [new file with mode: 0644]
include/db.php [new file with mode: 0644]
include/functions.php [new file with mode: 0644]
include/localized_schema.php [new file with mode: 0644]
include/login_form.php [new file with mode: 0644]
include/sanity_check.php [new file with mode: 0644]
include/sanity_config.php [new file with mode: 0644]
include/sessions.php [new file with mode: 0644]
include/version.php [new file with mode: 0644]
index.php
js/FeedTree.js [new file with mode: 0644]
js/PrefFeedTree.js [new file with mode: 0644]
js/PrefFilterTree.js [new file with mode: 0644]
js/PrefLabelTree.js [new file with mode: 0644]
js/deprecated.js [new file with mode: 0644]
js/digest.js [new file with mode: 0644]
js/feedlist.js [new file with mode: 0644]
js/functions.js [new file with mode: 0644]
js/prefs.js [new file with mode: 0644]
js/tt-rss.js [new file with mode: 0644]
js/viewfeed.js [new file with mode: 0644]
localized_js.php
localized_schema.php [deleted file]
login_form.php [deleted file]
messages.pot [deleted file]
mobile/article.php
mobile/cat.php
mobile/classic/functions.php [deleted file]
mobile/classic/index.php [deleted file]
mobile/classic/login_form.php [deleted file]
mobile/classic/logout.php [deleted file]
mobile/classic/mobile.css [deleted file]
mobile/classic/mobile.js [deleted file]
mobile/feed.php
mobile/functions.php [deleted file]
mobile/home.php
mobile/index.php
mobile/logout.php
mobile/mobile-functions.php [new file with mode: 0644]
mobile/prefs.php
opml.php
prefs.js [deleted file]
prefs.php
register.php
sanity_check.php [deleted file]
sanity_config.php [deleted file]
sessions.php [deleted file]
tt-rss.js [deleted file]
tt-rss.php [deleted file]
twitter.php
update.php
update_daemon2.php
version.php [deleted file]
viewfeed.js [deleted file]

diff --git a/FeedTree.js b/FeedTree.js
deleted file mode 100644 (file)
index b5b7571..0000000
+++ /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 = "&alpha;";
-                       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(/&quot;/g, "\"");
-               name = name.replace(/&amp;/g, "&");
-               name = name.replace(/&mdash;/g, "-");
-               name = name.replace(/&lt;/g, "<");
-               name = name.replace(/&gt;/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 (file)
index 4ea4866..0000000
+++ /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 (file)
index a4cf3da..0000000
+++ /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 = "<span class=\"labelFixedLength\">" + label + 
-                               "</span>" + 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 (file)
index 05a0c15..0000000
+++ /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 = '&alpha;';
-                       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";
-       }, 
-});
-
index 7bbec8c5a484d13e20596673ff0fecfc8ca02e51..247cab38b9df45451e9e8e6a35c4eff31800617c 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        /* remove ill effects of magic quotes */
 
        if (get_magic_quotes_gpc()) {
diff --git a/db-prefs.php b/db-prefs.php
deleted file mode 100644 (file)
index 7ee4920..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-<?php
-       require_once "config.php";
-       require_once "db.php";
-
-       if (!defined('DISABLE_SESSIONS')) {
-               if (!$_SESSION["prefs_cache"])
-                       $_SESSION["prefs_cache"] = array();
-       }
-
-       function get_pref($link, $pref_name, $user_id = false, $die_on_error = false) {
-
-               $pref_name = db_escape_string($pref_name);
-               $prefs_cache = true;
-               $profile = false;
-
-               if (!$user_id) {
-                       $user_id = $_SESSION["uid"];
-                       @$profile = $_SESSION["profile"];
-               } else {
-                       $user_id = sprintf("%d", $user_id);
-                       //$prefs_cache = false;
-               }
-
-               if ($prefs_cache && !defined('DISABLE_SESSIONS')) {
-                       if ($_SESSION["prefs_cache"] && @$_SESSION["prefs_cache"][$pref_name]) {
-                               $tuple = $_SESSION["prefs_cache"][$pref_name];
-                               return convert_pref_type($tuple["value"], $tuple["type"]);
-                       }
-               }
-
-               if ($profile) {
-                       $profile_qpart = "profile = '$profile' AND";
-               } else {
-                       $profile_qpart = "profile IS NULL AND";
-               }
-
-               if (get_schema_version($link) < 63) $profile_qpart = "";
-
-               $result = db_query($link, "SELECT
-                       value,ttrss_prefs_types.type_name as type_name
-                       FROM
-                               ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types
-                       WHERE
-                               $profile_qpart
-                               ttrss_user_prefs.pref_name = '$pref_name' AND
-                               ttrss_prefs_types.id = type_id AND
-                               owner_uid = '$user_id' AND
-                               ttrss_user_prefs.pref_name = ttrss_prefs.pref_name");
-
-               if (db_num_rows($result) > 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;
-                               }
-                       }
-               }
-       }
-?>
index edee3bc83341369fc5cb2af7ab01c6567ddc8c26..9a4fdf604b2eb52e2c9a4ea0faa56b40ed081135 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "functions.php";
        require_once "sessions.php";
        require_once "sanity_check.php";
@@ -87,7 +89,7 @@ function confirmOP() {
 
                } else {
                        print_notice(__("Tiny Tiny RSS database is up to date."));
-                       print "<form method=\"GET\" action=\"tt-rss.php\">
+                       print "<form method=\"GET\" action=\"index.php\">
                                <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                </form>";
                }
diff --git a/db.php b/db.php
deleted file mode 100644 (file)
index 0682b58..0000000
--- a/db.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-require_once "config.php";
-
-function db_connect($host, $user, $pass, $db) {
-       if (DB_TYPE == "pgsql") {
-
-               $string = "dbname=$db user=$user";
-
-               if ($pass) {
-                       $string .= " password=$pass";
-               }
-
-               if ($host) {
-                       $string .= " host=$host";
-               }
-
-               if (defined('DB_PORT')) {
-                       $string = "$string port=" . DB_PORT;
-               }
-
-               $link = pg_connect($string);
-
-               if (!$link) {
-                       die("Connection failed: " . pg_last_error($link));
-               }
-
-               return $link;
-
-       } else if (DB_TYPE == "mysql") {
-               $link = mysql_connect($host, $user, $pass);
-               if ($link) {
-                       $result = mysql_select_db($db, $link);
-                       if (!$result) {
-                               die("Can't select DB: " . mysql_error($link));
-                       }
-                       return $link;
-               } else {
-                       die("Connection failed: " . mysql_error($link));
-               }
-       }
-}
-
-function db_escape_string($s, $strip_tags = true) {
-       if ($strip_tags) $s = strip_tags($s);
-
-       if (DB_TYPE == "pgsql") {
-               return pg_escape_string($s);
-       } else {
-               return mysql_real_escape_string($s);
-       }
-}
-
-function db_query($link, $query, $die_on_error = true) {
-       //if ($_REQUEST["qlog"])
-       //      error_log($_SESSION["uid"] . ":" . $_REQUEST["op"] . "/" . $_REQUEST["subop"] .
-       //              " $query\n", 3, "/tmp/ttrss-query.log");
-
-       if (DB_TYPE == "pgsql") {
-               $result = pg_query($link, $query);
-               if (!$result) {
-                       $query = htmlspecialchars($query); // just in case
-                       if ($die_on_error) {
-                               die("Query <i>$query</i> 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 <i>$query</i> 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 (file)
index 1d04a1a..0000000
+++ /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 (file)
index 7dba6d3..0000000
--- 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 += "<a href=\"#\" onclick=\"viewfeed('" +
-                                                       //              article.tags[i] + "')\">" +
-                                                       //      article.tags[i] + "</a>, ";
-
-                                                       tags_part += article.tags[i] + ", ";
-                                               }
-
-                                               tags_part = tags_part.replace(/, $/, "");
-                                               tags_part = "<span class=\"tags\">" + tags_part + "</span>";
-
-                                       }
-
-                                       if (article.marked)
-                                               mark_part = "<img title='"+ __("Unstar article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_set.png'>";
-                                       else
-                                               mark_part =     "<img title='"+__("Star article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_unset.png'>";
-
-                                       if (article.published)
-                                               publ_part = "<img title='"+__("Unpublish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_set.png'>";
-                                       else
-                                               publ_part =     "<img title='"+__("Publish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_unset.png'>";
-
-                                       var tmp = "<div id=\"toolbar\">" +
-                                               "<a target=\"_blank\" href=\""+article.url+"\">" + __("Original article") + "</a>" +
-                                               "<div style=\"float : right\"><a href=\"#\" onclick=\"close_article()\">" +
-                                               __("Close this panel") + "</a></div></div>" +
-                                               "<div id=\"inner\">" +
-                                               "<div id=\"ops\">" +
-                                               mark_part +
-                                               publ_part +
-                                               "</div>" +
-                                               "<h1>" + article.title + "</h1>" +
-                                               "<div id=\"tags\">" +
-                                               tags_part +
-                                               "</div>" +
-                                               article.content + "</div>";
-
-                                       $("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 = "<img src='" + get_feed_icon(feed) + "'/>";
-
-               var tmp_html = "<li id=\"F-"+feed.id+"\" onclick=\"viewfeed("+feed.id+")\">" +
-                       icon_part + feed.title +
-                       "<div class='unread-ctr'>" + "<span class=\"unread\">" + feed.unread + "</span>" +
-                       "</div>" + "</li>";
-
-               $("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 = "<img class='icon' src='" + get_feed_icon(feed) + "'/>";
-
-
-               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";
-
-               //"<img title='" + __("Share on Twitter") + "' onclick=\"tweet_article("+article.id+", true)\" src='images/art-tweet.png'>" +
-
-               //"<img title='" + __("Mark as read") + "' onclick=\"view("+article.id+", true)\" src='images/digest_checkbox.png'>" +
-
-               var checkbox_part = "<input type=\"checkbox\" class=\"cb\" onclick=\"toggle_select_article(this)\"/>";
-
-               var date = new Date(article.updated * 1000);
-
-               var date_part = date.toString().substring(0,21);
-
-               var tmp_html = "<li id=\"A-"+article.id+"\" "+style+" class=\""+li_class+"\">" +
-                       checkbox_part +
-                       icon_part +
-                       "<a target=\"_blank\" href=\""+article.link+"\""+
-                               "onclick=\"return view("+article.id+")\" class='title'>" +
-                               article.title + "</a>" +
-                       "<div class='body'>" +
-                       "<div onclick=\"view("+article.id+")\" class='excerpt'>" +
-                               article.excerpt + "</div>" +
-                       "<div class='info'>";
-
-/*             tmp_html += "<a href=\#\" onclick=\"viewfeed("+feed.id+")\">" +
-                                       feed.title + "</a> " + " @ "; */
-
-               tmp_html += date_part + "</div>" +
-                       "</div></li>";
-
-               $("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 += "<li id='F-MORE-PROMPT'>" +
-                               "<img src='images/blank_icon.gif'>" +
-                               "<a href=\"#\" onclick=\"expand_feeds()\">" +
-                               __("%d more...").replace("%d", feeds.length-10) +
-                               "</a>" + "</li>";
-               }
-
-               if (feeds.length == 0) {
-                       $('feeds-content').innerHTML =
-                               "<div class='insensitive' style='text-align : center'>" +
-                                       __("No unread feeds.") + "</div>";
-               }
-
-               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 += "<li id='H-MORE-PROMPT'>" +
-                                               "<div class='body'>" +
-                                               "<a href=\"#\" onclick=\"catchup_visible_articles()\">" +
-                                               __("Mark as read") + "</a> | " +
-                                               "<a href=\"javascript:load_more()\">" +
-                                               __("Load more...") + "</a>" +
-                                               "<img style=\"display : none\" "+
-                                               "id=\"H-LOADING-IMG\" src='images/indicator_tiny.gif'>" +
-                                               "</div></li>";
-                               }
-                       } 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 + "&note=" + param_escape(note);
-               } else {
-                       query = query + "&note=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);
-       }
-}
index f3879ace2c38e6326052a3f12b06d4d851a9718a..74a0c566d69ea82fa38fb152e194622b226dac7b 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "functions.php";
        require_once "sessions.php";
        require_once "sanity_check.php";
@@ -37,9 +39,9 @@
        <script type="text/javascript" src="lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"></script>
        <script type="text/javascript" src="lib/dojo/dojo.js" djConfig="parseOnLoad: true"></script>
        <script type="text/javascript" charset="utf-8" src="localized_js.php?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="functions.js?<?php echo $dt_add ?>"></script>
 
-       <script type="text/javascript" src="digest.js"></script>
+       <script type="text/javascript" charset="utf-8" src="js/functions.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" src="js/digest.js"></script>
 
        <script type="text/javascript">
                Event.observe(window, 'load', function() {
index 12aeffe751943c219d9c65eed4442573f73bf7f0..802947d985c508c1e8288157e3ff3d284d727794 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "functions.php";
 
        $ERRORS[0] = "";
diff --git a/feedlist.js b/feedlist.js
deleted file mode 100644 (file)
index 62c44b4..0000000
+++ /dev/null
@@ -1,505 +0,0 @@
-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 = "<a href='#' onclick='viewCurrentFeed()'>" +
-                       __("New articles available in this feed (click to show)") + "</a>";
-
-               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/functions.js b/functions.js
deleted file mode 100644 (file)
index 15f645c..0000000
+++ /dev/null
@@ -1,1657 +0,0 @@
-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 = "<div class=\"fatalError\">" +
-                       "<pre>" + msg + "</pre>";
-
-               content += "<form name=\"exceptionForm\" id=\"exceptionForm\" target=\"_blank\" "+
-                 "action=\"http://tt-rss.org/report.php\" method=\"POST\">";
-
-               content += "<textarea style=\"display : none\" name=\"message\">" + msg + "</textarea>";
-               content += "<textarea style=\"display : none\" name=\"params\">N/A</textarea>";
-
-               if (ext_info) {
-                       content += "<div><b>Additional information:</b></div>" +
-                       "<textarea name=\"xinfo\" readonly=\"1\">" + ext_info + "</textarea>";
-               }
-
-               content += "<div><b>Stack trace:</b></div>" +
-                       "<textarea name=\"stack\" readonly=\"1\">" + e.stack + "</textarea>";
-
-               content += "</form>";
-
-               content += "</div>";
-
-               content += "<div class='dlgButtons'>";
-
-               content += "<button dojoType=\"dijit.form.Button\""+
-                               "onclick=\"dijit.byId('exceptionDlg').report()\">" +
-                               __('Report to tt-rss.org') + "</button> ";
-               content += "<button dojoType=\"dijit.form.Button\" "+
-                               "onclick=\"dijit.byId('exceptionDlg').hide()\">" +
-                               __('Close') + "</button>";
-               content += "</div>";
-
-               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 = "<img src='"+getInitParam("sign_progress")+"'> " + msg;
-       } else if (n_type == 3) {
-               n.className = "notifyError";
-               msg = "<img src='"+getInitParam("sign_excl")+"'> " + msg;
-       } else if (n_type == 4) {
-               n.className = "notifyInfo";
-               msg = "<img src='"+getInitParam("sign_info")+"'> " + msg;
-       }
-
-//     msg = "<img src='images/live_com_loading.gif'> " + 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 = "tt-rss.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<x.length;j++) {
-        // call isNumeric recursively for each character
-        number=isNumeric(x.substring(j,j+1));
-        if(!number) return number;
-      }
-      return number;
-    }
-    else {
-      // if x is number return true
-      if(numbers.indexOf(x)>=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) + "&param=" + 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 = "tt-rss.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 = "<div><b>Error code:</b> " + code + "</div>" +
-                               "<p>" + msg + "</p>";
-
-                       if (ext_info) {
-                               content = content + "<div><b>Additional information:</b></div>" +
-                                       "<textarea style='width: 100%' readonly=\"1\">" +
-                                       ext_info + "</textarea>";
-                       }
-
-                       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 += "&hellip;";
-       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&reg_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(/\&amp;key=.*$/,
-                                                               "&amp;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/functions.php b/functions.php
deleted file mode 100644 (file)
index 974d02a..0000000
+++ /dev/null
@@ -1,7648 +0,0 @@
-<?php
-
-       date_default_timezone_set('UTC');
-       if (defined('E_DEPRECATED')) {
-               error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
-       } else {
-               error_reporting(E_ALL & ~E_NOTICE);
-       }
-
-       require_once 'config.php';
-
-       if (DB_TYPE == "pgsql") {
-               define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
-       } else {
-               define('SUBSTRING_FOR_DATE', 'SUBSTRING');
-       }
-
-       define('THEME_VERSION_REQUIRED', 1.1);
-
-       /**
-        * Return available translations names.
-        *
-        * @access public
-        * @return array A array of available translations.
-        */
-       function get_translations() {
-               $tr = array(
-                                       "auto"  => "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 <category> 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 <dc:subject> 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 */
-                               // <a href="..." rel="tag">Xorg</a>, //
-
-                               $entry_tags = null;
-
-                               preg_match_all("/<a.*?rel=['\"]tag['\"].*?\>([^<]+)<\/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 "<!-- post $orig_title needs update : $post_needs_update -->";
-
-                                               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 {
-                                       // <enclosure>
-
-                                       $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);
-                                                       }
-                                               }
-                                       }
-
-                                       // <media:content>
-                                       // 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 "<select name=\"$id\" id=\"$id\" $attributes>";
-               foreach ($values as $v) {
-                       if ($v == $default)
-                               $sel = "selected=\"1\"";
-                        else
-                               $sel = "";
-
-                       print "<option value=\"$v\" $sel>$v</option>";
-               }
-               print "</select>";
-       }
-
-       function print_select_hash($id, $default, $values, $attributes = "") {
-               print "<select name=\"$id\" id='$id' $attributes>";
-               foreach (array_keys($values) as $v) {
-                       if ($v == $default)
-                               $sel = 'selected="selected"';
-                        else
-                               $sel = "";
-
-                       print "<option $sel value=\"$v\">".$values[$v]."</option>";
-               }
-
-               print "</select>";
-       }
-
-       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 "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
-                               type=\"radio\" $sel $attributes name=\"$id\">&nbsp;$v&nbsp;";
-
-               }
-       }
-
-       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"] . "<br>";
-
-                               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 = '&hellip;') {
-               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 "<link rel=\"stylesheet\" type=\"text/css\"
-                               href=\"$t/theme.css?$time \">";
-                       if (file_exists("$t/theme.js")) {
-                               print "<script type=\"text/javascript\" src=\"$t/theme.js?$time\">
-                                       </script>";
-                       }
-               }
-       }
-
-       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 "<select id=\"$id\" name=\"$id\" $attributes>";
-               if ($include_all_feeds) {
-                       print "<option value=\"0\">".__('All feeds')."</option>";
-               }
-
-               $result = db_query($link, "SELECT id,title FROM ttrss_feeds
-                       WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
-
-               if (db_num_rows($result) > 0 && $include_all_feeds) {
-                       print "<option disabled>--------</option>";
-               }
-
-               while ($line = db_fetch_assoc($result)) {
-                       if ($line["id"] == $default_id) {
-                               $is_selected = "selected=\"1\"";
-                       } else {
-                               $is_selected = "";
-                       }
-
-                       $title = truncate_string(htmlspecialchars($line["title"]), 40);
-
-                       printf("<option $is_selected value='%d'>%s</option>",
-                               $line["id"], $title);
-               }
-
-               print "</select>";
-       }
-
-       function print_feed_cat_select($link, $id, $default_id = "",
-               $attributes = "", $include_all_cats = true) {
-
-               print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
-
-               if ($include_all_cats) {
-                       print "<option value=\"0\">".__('Uncategorized')."</option>";
-               }
-
-               $result = db_query($link, "SELECT id,title FROM ttrss_feed_categories
-                       WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
-
-               if (db_num_rows($result) > 0 && $include_all_cats) {
-                       print "<option disabled=\"1\">--------</option>";
-               }
-
-               while ($line = db_fetch_assoc($result)) {
-                       if ($line["id"] == $default_id) {
-                               $is_selected = "selected=\"1\"";
-                       } else {
-                               $is_selected = "";
-                       }
-
-                       if ($line["title"])
-                               printf("<option $is_selected value='%d'>%s</option>",
-                                       $line["id"], htmlspecialchars($line["title"]));
-               }
-
-#              print "<option value=\"ADD_CAT\">" .__("Add category...") . "</option>";
-
-               print "</select>";
-       }
-
-       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 = "<div style=\"$note_style\">Article note: " . $line['note'] . "</div>" .
-                                       $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('/<img[^>]+>/is', '', $res);
-               }
-
-               if (strpos($res, "href=") === false)
-                       $res = rewrite_urls($res);
-
-               $charset_hack = '<head>
-                       <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-               </head>';
-
-               $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 = "<div id=\"subtoolbar_main\">";
-
-               $reply .= __('Select:')."
-                       <a href=\"#\" onclick=\"$sel_all_link\">".__('All')."</a>,
-                       <a href=\"#\" onclick=\"$sel_unread_link\">".__('Unread')."</a>,
-                       <a href=\"#\" onclick=\"$sel_inv_link\">".__('Invert')."</a>,
-                       <a href=\"#\" onclick=\"$sel_none_link\">".__('None')."</a></li>";
-
-               $reply .= " ";
-
-               $reply .= "<select dojoType=\"dijit.form.Select\"
-                       onchange=\"headlineActionsChange(this)\">";
-               $reply .= "<option value=\"false\">".__('Actions...')."</option>";
-
-               $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>";
-
-               $reply .= "<option value=\"$tog_unread_link\">".__('Unread')."</option>
-                       <option value=\"$tog_marked_link\">".__('Starred')."</option>
-                       <option value=\"$tog_published_link\">".__('Published')."</option>";
-
-               $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>";
-
-               $reply .= "<option value=\"$catchup_sel_link\">".__('Mark as read')."</option>";
-
-               if ($feed_id != "0") {
-                       $reply .= "<option value=\"$archive_sel_link\">".__('Archive')."</option>";
-               } else {
-                       $reply .= "<option value=\"$archive_sel_link\">".__('Move back')."</option>";
-                       $reply .= "<option value=\"$delete_sel_link\">".__('Delete')."</option>";
-
-               }
-
-               $reply .= "<option value=\"emailArticle(false)\">".__('Forward by email').
-                       "</option>";
-
-               if ($is_cat) $cat_q = "&is_cat=$is_cat";
-
-               if ($search) {
-                       $search_q = "&q=$search&m=$match_on&smode=$search_mode";
-               } else {
-                       $search_q = "";
-               }
-
-               $rss_link = htmlspecialchars(get_self_url_prefix() .
-                       "/public.php?op=rss&id=$feed_id$cat_q$search_q");
-
-               $reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>";
-
-               $reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>";
-
-               $reply .= "<option value=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>";
-
-               $reply .= "</select>";
-
-               $reply .= "</div>";
-
-               $reply .= "<div id=\"subtoolbar_ftitle\">";
-
-               if ($feed_site_url) {
-                       $target = "target=\"_blank\"";
-                       $reply .= "<a title=\"".__("Visit the website")."\" $target href=\"$feed_site_url\">".
-                               truncate_string($feed_title,30)."</a>";
-
-                       if ($error) {
-                               $reply .= " (<span class=\"error\" title=\"$error\">Error</span>)";
-                       }
-
-               } 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 .= "<span style=\"background : $bg_color; color : $fg_color\" >";
-                                       $reply .= $feed_title;
-                                       $reply .= "</span>";
-                               } else {
-                                       $reply .= $feed_title;
-                               }
-
-                       } else {
-                               $reply .= $feed_title;
-                       }
-               }
-
-               $reply .= "
-                       <a href=\"#\"
-                               title=\"".__("View as RSS feed")."\"
-                               onclick=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">
-                               <img class=\"noborder\" style=\"vertical-align : middle\" src=\"images/feed-icon-12x12.png\"></a>";
-
-               $reply .= "</div>";
-
-               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 "<div class=\"warning\" id=\"$id\">
-                       <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
-       }
-
-       function format_notice($msg, $id = "") {
-               global $link;
-               return "<div class=\"notice\" id=\"$id\">
-                       <img src=\"".theme_image($link, "images/sign_info.png")."\">$msg</div>";
-       }
-
-       function format_error($msg, $id = "") {
-               global $link;
-               return "<div class=\"error\" id=\"$id\">
-                       <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
-       }
-
-       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 .= "<audio id=\"$id\"\">
-                                       <source src=\"$url\"></source>
-                                       </audio>";
-
-                               $entry .= "<span onclick=\"player(this)\"
-                                       title=\"".__("Click to play")."\" status=\"0\"
-                                       class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
-
-                       } else {
-
-                               $entry .= "<object type=\"application/x-shockwave-flash\"
-                                       data=\"lib/button/musicplayer.swf?song_url=$url\"
-                                       width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
-                                       <param name=\"movie\"
-                                               value=\"lib/button/musicplayer.swf?song_url=$url\" />
-                                       </object>";
-                       }
-               }
-
-               $filename = substr($url, strrpos($url, "/")+1);
-
-               $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
-                       $filename . " (" . $ctype . ")" . "</a>";
-
-               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 "<article id='$id'><![CDATA["; };
-
-               $result = db_query($link, "SELECT rtl_content, always_display_enclosures 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"));
-                       $always_display_enclosures = sql_bool_to_bool(db_fetch_result($result, 0, "always_display_enclosures"));
-               } else {
-                       $rtl_content = false;
-                       $always_display_enclosures = false;
-               }
-
-               if ($rtl_content) {
-                       $rtl_tag = "dir=\"RTL\"";
-                       $rtl_class = "RTL";
-               } else {
-                       $rtl_tag = "";
-                       $rtl_class = "";
-               }
-
-               if ($mark_as_read) {
-                       $result = db_query($link, "UPDATE ttrss_user_entries
-                               SET unread = false,last_read = NOW()
-                               WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
-
-                       ccache_update($link, $feed_id, $_SESSION["uid"]);
-               }
-
-               $result = db_query($link, "SELECT title,link,content,feed_id,comments,int_id,
-                       ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
-                       (SELECT icon_url FROM ttrss_feeds WHERE id = feed_id) as icon_url,
-                       (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
-                       num_comments,
-                       tag_cache,
-                       author,
-                       orig_feed_id,
-                       note
-                       FROM ttrss_entries,ttrss_user_entries
-                       WHERE   id = '$id' AND ref_id = id AND owner_uid = " . $_SESSION["uid"]);
-
-               if ($result) {
-
-                       $line = db_fetch_assoc($result);
-
-                       if ($line["icon_url"]) {
-                               $feed_icon = "<img src=\"" . $line["icon_url"] . "\">";
-                       } else {
-                               $feed_icon = "&nbsp;";
-                       }
-
-                       $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 = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
-                       } else {
-                               if ($line["comments"] && $line["link"] != $line["comments"]) {
-                                       $entry_comments = "<a target='_blank' href=\"".$line["comments"]."\">comments</a>";
-                               }
-                       }
-
-                       if ($zoom_mode) {
-                               header("Content-Type: text/html");
-                               $rv['content'] .= "<html><head>
-                                               <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
-                                               <title>Tiny Tiny RSS - ".$line["title"]."</title>
-                                               <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
-                                       </head><body>";
-                       }
-
-                       $rv['content'] .= "<div id=\"PTITLE-$id\" style=\"display : none\">" .
-                               truncate_string(strip_tags($line['title']), 15) . "</div>";
-
-                       $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
-
-                       $rv['content'] .= "<div onclick=\"return postClicked(event, $id)\"
-                               class=\"postHeader\" id=\"POSTHDR-$id\">";
-
-                       $entry_author = $line["author"];
-
-                       if ($entry_author) {
-                               $entry_author = __(" - ") . $entry_author;
-                       }
-
-                       $parsed_updated = make_local_datetime($link, $line["updated"], true,
-                               false, true);
-
-                       $rv['content'] .= "<div class=\"postDate$rtl_class\">$parsed_updated</div>";
-
-                       if ($line["link"]) {
-                               $rv['content'] .= "<div clear='both'><a target='_blank'
-                                       title=\"".htmlspecialchars($line['title'])."\"
-                                       href=\"" .
-                                       $line["link"] . "\">" .
-                                       truncate_string($line["title"], 100) .
-                                       "<span class='author'>$entry_author</span></a></div>";
-                       } else {
-                               $rv['content'] .= "<div clear='both'>" . $line["title"] . "$entry_author</div>";
-                       }
-
-                       $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 = "&nbsp;"; # placeholder
-
-                       $rv['content'] .= "<div style='float : right'>
-                               <img src='".theme_image($link, 'images/tag.png')."'
-                               class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
-
-                       if (!$zoom_mode) {
-                               $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
-                                       <a title=\"".__('Edit tags for this article')."\"
-                                       href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
-
-                               $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
-                                       id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
-                                       position=\"below\">$tags_str_full</div>";
-
-                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-zoom.png')."\"
-                                               class='tagsPic' style=\"cursor : pointer\"
-                                               onclick=\"postOpenInNewTab(event, $id)\"
-                                               alt='Zoom' title='".__('Open article in new tab')."'>";
-
-                               //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
-
-                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-pub-note.png')."\"
-                                               class='tagsPic' style=\"cursor : pointer\"
-                                               onclick=\"editArticleNote($id)\"
-                                               alt='PubNote' title='".__('Edit article note')."'>";
-
-                               if (DIGEST_ENABLE) {
-                                       $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
-                                               class='tagsPic' style=\"cursor : pointer\"
-                                               onclick=\"emailArticle($id)\"
-                                               alt='Zoom' title='".__('Forward by email')."'>";
-                               }
-
-                               if (ENABLE_TWEET_BUTTON) {
-                                       $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
-                                                       class='tagsPic' style=\"cursor : pointer\"
-                                                       onclick=\"tweetArticle($id)\"
-                                                       alt='Zoom' title='".__('Share on Twitter')."'>";
-                               }
-
-                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-share.png')."\"
-                                       class='tagsPic' style=\"cursor : pointer\"
-                                       onclick=\"shareArticle(".$line['int_id'].")\"
-                                       alt='Zoom' title='".__('Share by URL')."'>";
-
-                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/digest_checkbox.png')."\"
-                                               class='tagsPic' style=\"cursor : pointer\"
-                                               onclick=\"closeArticlePanel($id)\"
-                                               alt='Zoom' title='".__('Close this panel')."'>";
-
-                       } else {
-                               $tags_str = strip_tags($tags_str);
-                               $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
-                       }
-                       $rv['content'] .= "</div>";
-                       $rv['content'] .= "<div clear='both'>$entry_comments</div>";
-
-                       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'] .= "<div clear='both'>";
-                                       $rv['content'] .= __("Originally from:");
-
-                                       $rv['content'] .= "&nbsp;";
-
-                                       $tmp_line = db_fetch_assoc($tmp_result);
-
-                                       $rv['content'] .= "<a target='_blank'
-                                               href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
-                                               $tmp_line['title'] . "</a>";
-
-                                       $rv['content'] .= "&nbsp;";
-
-                                       $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
-                                       $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
-
-                                       $rv['content'] .= "</div>";
-                               }
-                       }
-
-                       $rv['content'] .= "</div>";
-
-                       $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
-                               if ($line['note']) {
-                                       $rv['content'] .= format_article_note($id, $line['note']);
-                               }
-                       $rv['content'] .= "</div>";
-
-                       $rv['content'] .= "<div class=\"postIcon\">" .
-                               "<a target=\"_blank\" title=\"".__("Visit the website")."\"$
-                               href=\"".htmlspecialchars($feed_site_url)."\">".
-                               $feed_icon . "</a></div>";
-
-                       $rv['content'] .= "<div class=\"postContent\">";
-
-                       $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'] .= "</div>";
-
-                       $rv['content'] .= "</div>";
-
-               }
-
-               if ($zoom_mode) {
-                       $rv['content'] .= "
-                               <div style=\"text-align : center\">
-                               <button onclick=\"return window.close()\">".
-                                       __("Close this window")."</button></div>";
-                       $rv['content'] .= "</body></html>";
-               }
-
-               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'] = "<div align='center'>".__('Feed not found.')."</div>";
-                       }
-               }
-
-               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 = "<span id=\"HLLCTR-$id\">";
-                               $labels_str .= format_article_labels($labels, $id);
-                               $labels_str .= "</span>";
-
-                               if (count($topmost_article_ids) < 3) {
-                                       array_push($topmost_article_ids, $id);
-                               }
-
-                               if ($line["last_read"] == "" && !sql_bool_to_bool($line["unread"])) {
-
-                                       $update_pic = "<img id='FUPDPIC-$id' src=\"".
-                                               theme_image($link, 'images/updated.png')."\"
-                                               alt=\"Updated\">";
-                               } else {
-                                       $update_pic = "<img id='FUPDPIC-$id' src=\"images/blank_icon.gif\"
-                                               alt=\"Updated\">";
-                               }
-
-                               if (sql_bool_to_bool($line["unread"]) &&
-                                       time() - strtotime($line["updated_noms"]) < $fresh_intl) {
-
-                                       $update_pic = "<img id='FUPDPIC-$id' src=\"".
-                                               theme_image($link, 'images/fresh_sign.png')."\" alt=\"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 = "<img id=\"FMPIC-$id\"
-                                               src=\"".theme_image($link, 'images/mark_set.png')."\"
-                                               class=\"markedPic\" alt=\"Unstar article\"
-                                               onclick='javascript:toggleMark($id)'>";
-                               } else {
-                                       $marked_pic = "<img id=\"FMPIC-$id\"
-                                               src=\"".theme_image($link, 'images/mark_unset.png')."\"
-                                               class=\"markedPic\" alt=\"Star article\"
-                                               onclick='javascript:toggleMark($id)'>";
-                               }
-
-                               if ($line["published"] == "t" || $line["published"] == "1") {
-                                       $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($link,
-                                               'images/pub_set.png')."\"
-                                               class=\"markedPic\"
-                                               alt=\"Unpublish article\" onclick='javascript:togglePub($id)'>";
-                               } else {
-                                       $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($link,
-                                               'images/pub_unset.png')."\"
-                                               class=\"markedPic\"
-                                               alt=\"Publish article\" onclick='javascript:togglePub($id)'>";
-                               }
-
-#                              $content_link = "<a target=\"_blank\" href=\"".$line["link"]."\">" .
-#                                      $line["title"] . "</a>";
-
-#                              $content_link = "<a
-#                                      href=\"" . htmlspecialchars($line["link"]) . "\"
-#                                      onclick=\"view($id,$feed_id);\">" .
-#                                      $line["title"] . "</a>";
-
-#                              $content_link = "<a href=\"javascript:viewContentUrl('".$line["link"]."');\">" .
-#                                      $line["title"] . "</a>";
-
-                               $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 = "<img class='hlScorePic' src=\"images/$score_pic\"
-                                       onclick=\"adjustArticleScore($id, $score)\" title=\"$score $score_title\">"; */
-
-                               $score_pic = "<img class='hlScorePic' src=\"$score_pic\"
-                                       title=\"$score\">";
-
-                               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 = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
-                               } else {
-                                       $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/feed-icon-12x12.png\" alt=\"\">";
-                               }
-
-                               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 = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
-
-                                                       $reply['content'] .= "<div class='cdmFeedTitle'>".
-                                                               "<div style=\"float : right\">$feed_icon_img</div>".
-                                                               "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
-                                                               $line["feed_title"]."</a> $vf_catchup_link</div>";
-
-                                               }
-                                       }
-
-                                       $mouseover_attrs = "onmouseover='postMouseIn($id)'
-                                               onmouseout='postMouseOut($id)'";
-
-                                       $reply['content'] .= "<div class='$class' id='RROW-$id' $mouseover_attrs>";
-
-                                       $reply['content'] .= "<div class='hlUpdPic'>$update_pic</div>";
-
-                                       $reply['content'] .= "<div class='hlLeft'>";
-
-                                       $reply['content'] .= "<input type=\"checkbox\" onclick=\"tSR(this)\"
-                                                       id=\"RCHK-$id\">";
-
-                                       $reply['content'] .= "$marked_pic";
-                                       $reply['content'] .= "$published_pic";
-
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "<div onclick='return hlClicked(event, $id)'
-                                               class=\"hlTitle\"><span class='hlContent$hlc_suffix'>";
-                                       $reply['content'] .= "<a id=\"RTITLE-$id\"
-                                               href=\"" . htmlspecialchars($line["link"]) . "\"
-                                               onclick=\"\">" .
-                                               truncate_string($line["title"], 200);
-
-                                       if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) {
-                                               if ($content_preview) {
-                                                       $reply['content'] .= "<span class=\"contentPreview\"> - $content_preview</span>";
-                                               }
-                                       }
-
-                                       $reply['content'] .= "</a></span>";
-
-                                       $reply['content'] .= $labels_str;
-
-                                       if (!get_pref($link, 'VFEED_GROUP_BY_FEED') &&
-                                               defined('_SHOW_FEED_TITLE_IN_VFEEDS')) {
-                                               if (@$line["feed_title"]) {
-                                                       $reply['content'] .= "<span class=\"hlFeed\">
-                                                               (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
-                                                               $line["feed_title"]."</a>)
-                                                       </span>";
-                                               }
-                                       }
-
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "<span class=\"hlUpdated\">$updated_fmt</span>";
-                                       $reply['content'] .= "<div class=\"hlRight\">";
-
-                                       $reply['content'] .= $score_pic;
-
-                                       if ($line["feed_title"] && !get_pref($link, 'VFEED_GROUP_BY_FEED')) {
-
-                                               $reply['content'] .= "<span onclick=\"viewfeed($feed_id)\"
-                                                       style=\"cursor : pointer\"
-                                                       title=\"".htmlspecialchars($line['feed_title'])."\">
-                                                       $feed_icon_img<span>";
-                                       }
-
-                                       $reply['content'] .= "</div>";
-                                       $reply['content'] .= "</div>";
-
-                               } 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 = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
-
-                                                       $has_feed_icon = feed_has_icon($feed_id);
-
-                                                       if ($has_feed_icon) {
-                                                               $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
-                                                       } else {
-                                                               //$feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\" alt=\"\">";
-                                                       }
-
-                                                       $reply['content'] .= "<div class='cdmFeedTitle'>".
-                                                               "<div style=\"float : right\">$feed_icon_img</div>".
-                                                               "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
-                                                               $line["feed_title"]."</a> $vf_catchup_link</div>";
-                                               }
-                                       }
-
-                                       $expand_cdm = get_pref($link, 'CDM_EXPANDED');
-
-                                       $mouseover_attrs = "onmouseover='postMouseIn($id)'
-                                               onmouseout='postMouseOut($id)'";
-
-                                       $reply['content'] .= "<div class=\"$class\"
-                                               id=\"RROW-$id\" $mouseover_attrs'>";
-
-                                       $reply['content'] .= "<div class=\"cdmHeader\">";
-
-                                       $reply['content'] .= "<div>";
-
-                                       $reply['content'] .= "<input type=\"checkbox\" onclick=\"toggleSelectRowById(this,
-                                                       'RROW-$id')\" id=\"RCHK-$id\"/>";
-
-                                       $reply['content'] .= "$marked_pic";
-                                       $reply['content'] .= "$published_pic";
-
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "<span id=\"RTITLE-$id\"
-                                               onclick=\"return cdmClicked(event, $id);\"
-                                               class=\"titleWrap$hlc_suffix\">
-                                               <a class=\"title\"
-                                               title=\"".htmlspecialchars($line['title'])."\"
-                                               target=\"_blank\" href=\"".
-                                               htmlspecialchars($line["link"])."\">".
-                                               truncate_string($line["title"], 100) .
-                                               " $entry_author</a>";
-
-                                       $reply['content'] .= $labels_str;
-
-                                       if (!get_pref($link, 'VFEED_GROUP_BY_FEED') &&
-                                               defined('_SHOW_FEED_TITLE_IN_VFEEDS')) {
-                                               if (@$line["feed_title"]) {
-                                                       $reply['content'] .= "<span class=\"hlFeed\">
-                                                               (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
-                                                               $line["feed_title"]."</a>)
-                                                       </span>";
-                                               }
-                                       }
-
-                                       if (!$expand_cdm)
-                                               $content_hidden = "style=\"display : none\"";
-                                       else
-                                               $excerpt_hidden = "style=\"display : none\"";
-
-                                       $reply['content'] .= "<span $excerpt_hidden
-                                               id=\"CEXC-$id\" class=\"cdmExcerpt\"> - $content_preview</span>";
-
-                                       $reply['content'] .= "</span>";
-
-                                       $reply['content'] .= "<div>";
-                                       $reply['content'] .= "<span class='updated'>$updated_fmt</span>";
-                                       $reply['content'] .= "$score_pic";
-
-                                       if (!get_pref($link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) {
-                                               $reply['content'] .= "<span style=\"cursor : pointer\"
-                                                       title=\"".htmlspecialchars($line["feed_title"])."\"
-                                                       onclick=\"viewfeed($feed_id)\">$feed_icon_img</span>";
-                                       }
-                                       $reply['content'] .= "<div class=\"updPic\">$update_pic</div>";
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "<div class=\"cdmContent\" $content_hidden
-                                               onclick=\"return cdmClicked(event, $id);\"
-                                               id=\"CICD-$id\">";
-
-                                       $reply['content'] .= "<div class=\"cdmContentInner\">";
-
-                       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'] .= "<div clear='both'>";
-                                                       $reply['content'] .= __("Originally from:");
-
-                                                       $reply['content'] .= "&nbsp;";
-
-                                                       $tmp_line = db_fetch_assoc($tmp_result);
-
-                                                       $reply['content'] .= "<a target='_blank'
-                                                               href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
-                                                               $tmp_line['title'] . "</a>";
-
-                                                       $reply['content'] .= "&nbsp;";
-
-                                                       $reply['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
-                                                       $reply['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
-
-                                                       $reply['content'] .= "</div>";
-                                               }
-                                       }
-
-                                       $feed_site_url = $line["site_url"];
-
-                                       $article_content = sanitize_rss($link, $line["content_preview"],
-                                                       false, false, $feed_site_url);
-
-                                       $reply['content'] .= "<div id=\"POSTNOTE-$id\">";
-                                       if ($line['note']) {
-                                               $reply['content'] .= format_article_note($id, $line['note']);
-                                       }
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "<span id=\"CWRAP-$id\">";
-                                       $reply['content'] .= $expand_cdm ? $article_content : '';
-                                       $reply['content'] .= "</span>";
-
-/*                                     $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'] .= "</div>";
-
-                                       $reply['content'] .= "<div class=\"cdmFooter\">";
-
-                                       $tag_cache = $line["tag_cache"];
-
-                                       $tags_str = format_tags_string(
-                                               get_article_tags($link, $id, $_SESSION["uid"], $tag_cache),
-                                               $id);
-
-                                       $reply['content'] .= "<img src='".theme_image($link,
-                                                       'images/tag.png')."' alt='Tags' title='Tags'>
-                                               <span id=\"ATSTR-$id\">$tags_str</span>
-                                               <a title=\"".__('Edit tags for this article')."\"
-                                               href=\"#\" onclick=\"editArticleTags($id, $feed_id, true)\">(+)</a>";
-
-                                       $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 = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
-                                       } else {
-                                               if ($line["comments"] && $line["link"] != $line["comments"]) {
-                                                       $entry_comments = "<a target='_blank' href=\"".$line["comments"]."\">comments</a>";
-                                               }
-                                       }
-
-                                       if ($entry_comments) $reply['content'] .= "&nbsp;($entry_comments)";
-
-                                       $reply['content'] .= "<div style=\"float : right\">";
-
-                                       $reply['content'] .= "<img src=\"images/art-zoom.png\"
-                                               onclick=\"zoomToArticle(event, $id)\"
-                                               style=\"cursor : pointer\"
-                                               alt='Zoom'
-                                               title='".__('Open article in new tab')."'>";
-
-                                       //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
-
-                                       $reply['content'] .= "<img src=\"images/art-pub-note.png\"
-                                               style=\"cursor : pointer\" style=\"cursor : pointer\"
-                                               onclick=\"editArticleNote($id)\"
-                                               alt='PubNote' title='".__('Edit article note')."'>";
-
-                                       if (DIGEST_ENABLE) {
-                                               $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
-                                                       style=\"cursor : pointer\"
-                                                       onclick=\"emailArticle($id)\"
-                                                       alt='Zoom' title='".__('Forward by email')."'>";
-                                       }
-
-                                       if (ENABLE_TWEET_BUTTON) {
-                                               $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
-                                                       class='tagsPic' style=\"cursor : pointer\"
-                                                       onclick=\"tweetArticle($id)\"
-                                                       alt='Zoom' title='".__('Share on Twitter')."'>";
-                                       }
-
-                                       $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-share.png')."\"
-                                               class='tagsPic' style=\"cursor : pointer\"
-                                               onclick=\"shareArticle(".$line['int_id'].")\"
-                                               alt='Zoom' title='".__('Share by URL')."'>";
-
-                                       $reply['content'] .= "<img src=\"images/digest_checkbox.png\"
-                                               style=\"cursor : pointer\" style=\"cursor : pointer\"
-                                               onclick=\"dismissArticle($id)\"
-                                               alt='Dismiss' title='".__('Dismiss article')."'>";
-
-                                       $reply['content'] .= "</div>";
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "</div>";
-
-                                       $reply['content'] .= "</div>";
-
-                               }
-
-                               ++$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'] .= "<div class='whiteBox'>$message";
-
-                               $reply['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
-
-                               $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'] .= "<br/>";
-                                       $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
-                                               __('Some feeds have update errors (click for details)')."</a>";
-                               }
-                               $reply['content'] .= "</span></p></div>";
-                       }
-               }
-
-               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 "<a href=\"javascript:viewfeed('$key_escaped') \" style=\"font-size: " .
-                               $size . "px\" title=\"$value articles tagged with " .
-                               $key . '">' . $key . '</a> ';
-               }
-       }
-
-       function print_checkpoint($n, $s) {
-               $ts = getmicrotime();
-               echo sprintf("<!-- CP[$n] %.4f seconds -->", $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 = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
-
-                       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 .= ", &hellip;";
-               }
-
-               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("<span class='hlLabelRef'
-                               style='color : %s; background-color : %s'>%s</span>",
-                                       $l[2], $l[3], $l[1]);
-                       }
-
-               return $labels_str;
-
-       }
-
-       function format_article_note($id, $note) {
-
-               $str = "<div class='articleNote'        onclick=\"editArticleNote($id)\">
-                       <div class='noteEdit' onclick=\"editArticleNote($id)\">".
-                       __('(edit note)')."</div>$note</div>";
-
-               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'] = "<div class='whiteBox'>". $error . "</div>";
-
-               $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'] = "<div class='whiteBox'>".__('No feed selected.');
-
-               $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
-
-               $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'] .= "<br/>";
-                       $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
-                               __('Some feeds have update errors (click for details)')."</a>";
-               }
-               $reply['headlines']['content'] .= "</span></p>";
-
-               $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, '<html>') === false
-                       && stripos($content, '<html ') === false
-               ) {
-                       return false;
-               }
-
-               return true;
-       }
-
-       function print_label_select($link, $name, $value, $attributes = "") {
-
-               $result = db_query($link, "SELECT caption FROM ttrss_labels2
-                       WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
-
-               print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
-                       "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
-
-               while ($line = db_fetch_assoc($result)) {
-
-                       $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
-
-                       print "<option value=\"".htmlspecialchars($line["caption"])."\"
-                               $issel>" . htmlspecialchars($line["caption"]) . "</option>";
-
-               }
-
-#              print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
-
-               print "</select>";
-
-
-       }
-
-       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 .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
-#                                      $filename . " (" . $ctype . ")" . "</a>";
-
-                               array_push($entries_html, $entry);
-
-                               $entry = array();
-
-                               $entry["type"] = $ctype;
-                               $entry["filename"] = $filename;
-                               $entry["url"] = $url;
-
-                               array_push($entries, $entry);
-                       }
-
-                       $rv .= "<div class=\"postEnclosures\">";
-
-                       if (!get_pref($link, "STRIP_IMAGES")) {
-                               if ($always_display_enclosures ||
-                                                       !preg_match("/<img/i", $article_content)) {
-
-                                       foreach ($entries as $entry) {
-
-                                               if (preg_match("/image/", $entry["type"]) ||
-                                                               preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
-
-                                                               $rv .= "<p><img
-                                                               alt=\"".htmlspecialchars($entry["filename"])."\"
-                                                               src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
-                                               }
-                                       }
-                               }
-                       }
-
-                       if (count($entries) == 1) {
-                               $rv .= __("Attachment:") . " ";
-                       } else {
-                               $rv .= __("Attachments:") . " ";
-                       }
-
-                       $rv .= join(", ", $entries_html);
-
-                       $rv .= "</div>";
-               }
-
-               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 "<style type=\"text/css\">";
-                       print str_replace("<br/>", "\n", $value);
-                       print "</style>";
-               }
-
-       }
-
-       function rewrite_urls($line) {
-               global $url_regex;
-
-               $urls = null;
-
-               $result = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
-                       "<a target=\"_blank\" href=\"\\1\">\\1</a>", $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 "<select style='width: 100%' name='profile'>";
-
-                               print "<option value='0'>" . __("Default profile") . "</option>";
-
-                               while ($line = db_fetch_assoc($result)) {
-                                       $id = $line["id"];
-                                       $title = $line["title"];
-
-                                       print "<option value='$id'>$title</option>";
-                               }
-
-                               print "</select>";
-
-                               $_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
-
-               }
-       }
-?>
index 45e1358aac09584abcfe4bf4fa0f701701eddd32..210bbc2f7f3342dae900a3bbd438ecc571d20ae2 100644 (file)
--- a/image.php
+++ b/image.php
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "config.php";
        require_once "lib/simplepie/simplepie.inc";
 
diff --git a/include/db-prefs.php b/include/db-prefs.php
new file mode 100644 (file)
index 0000000..7ee4920
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+       require_once "config.php";
+       require_once "db.php";
+
+       if (!defined('DISABLE_SESSIONS')) {
+               if (!$_SESSION["prefs_cache"])
+                       $_SESSION["prefs_cache"] = array();
+       }
+
+       function get_pref($link, $pref_name, $user_id = false, $die_on_error = false) {
+
+               $pref_name = db_escape_string($pref_name);
+               $prefs_cache = true;
+               $profile = false;
+
+               if (!$user_id) {
+                       $user_id = $_SESSION["uid"];
+                       @$profile = $_SESSION["profile"];
+               } else {
+                       $user_id = sprintf("%d", $user_id);
+                       //$prefs_cache = false;
+               }
+
+               if ($prefs_cache && !defined('DISABLE_SESSIONS')) {
+                       if ($_SESSION["prefs_cache"] && @$_SESSION["prefs_cache"][$pref_name]) {
+                               $tuple = $_SESSION["prefs_cache"][$pref_name];
+                               return convert_pref_type($tuple["value"], $tuple["type"]);
+                       }
+               }
+
+               if ($profile) {
+                       $profile_qpart = "profile = '$profile' AND";
+               } else {
+                       $profile_qpart = "profile IS NULL AND";
+               }
+
+               if (get_schema_version($link) < 63) $profile_qpart = "";
+
+               $result = db_query($link, "SELECT
+                       value,ttrss_prefs_types.type_name as type_name
+                       FROM
+                               ttrss_user_prefs,ttrss_prefs,ttrss_prefs_types
+                       WHERE
+                               $profile_qpart
+                               ttrss_user_prefs.pref_name = '$pref_name' AND
+                               ttrss_prefs_types.id = type_id AND
+                               owner_uid = '$user_id' AND
+                               ttrss_user_prefs.pref_name = ttrss_prefs.pref_name");
+
+               if (db_num_rows($result) > 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 (file)
index 0000000..0682b58
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+
+require_once "config.php";
+
+function db_connect($host, $user, $pass, $db) {
+       if (DB_TYPE == "pgsql") {
+
+               $string = "dbname=$db user=$user";
+
+               if ($pass) {
+                       $string .= " password=$pass";
+               }
+
+               if ($host) {
+                       $string .= " host=$host";
+               }
+
+               if (defined('DB_PORT')) {
+                       $string = "$string port=" . DB_PORT;
+               }
+
+               $link = pg_connect($string);
+
+               if (!$link) {
+                       die("Connection failed: " . pg_last_error($link));
+               }
+
+               return $link;
+
+       } else if (DB_TYPE == "mysql") {
+               $link = mysql_connect($host, $user, $pass);
+               if ($link) {
+                       $result = mysql_select_db($db, $link);
+                       if (!$result) {
+                               die("Can't select DB: " . mysql_error($link));
+                       }
+                       return $link;
+               } else {
+                       die("Connection failed: " . mysql_error($link));
+               }
+       }
+}
+
+function db_escape_string($s, $strip_tags = true) {
+       if ($strip_tags) $s = strip_tags($s);
+
+       if (DB_TYPE == "pgsql") {
+               return pg_escape_string($s);
+       } else {
+               return mysql_real_escape_string($s);
+       }
+}
+
+function db_query($link, $query, $die_on_error = true) {
+       //if ($_REQUEST["qlog"])
+       //      error_log($_SESSION["uid"] . ":" . $_REQUEST["op"] . "/" . $_REQUEST["subop"] .
+       //              " $query\n", 3, "/tmp/ttrss-query.log");
+
+       if (DB_TYPE == "pgsql") {
+               $result = pg_query($link, $query);
+               if (!$result) {
+                       $query = htmlspecialchars($query); // just in case
+                       if ($die_on_error) {
+                               die("Query <i>$query</i> 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 <i>$query</i> 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 (file)
index 0000000..8f46c29
--- /dev/null
@@ -0,0 +1,7647 @@
+<?php
+       date_default_timezone_set('UTC');
+       if (defined('E_DEPRECATED')) {
+               error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
+       } else {
+               error_reporting(E_ALL & ~E_NOTICE);
+       }
+
+       require_once 'config.php';
+
+       if (DB_TYPE == "pgsql") {
+               define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
+       } else {
+               define('SUBSTRING_FOR_DATE', 'SUBSTRING');
+       }
+
+       define('THEME_VERSION_REQUIRED', 1.1);
+
+       /**
+        * Return available translations names.
+        *
+        * @access public
+        * @return array A array of available translations.
+        */
+       function get_translations() {
+               $tr = array(
+                                       "auto"  => "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 <category> 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 <dc:subject> 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 */
+                               // <a href="..." rel="tag">Xorg</a>, //
+
+                               $entry_tags = null;
+
+                               preg_match_all("/<a.*?rel=['\"]tag['\"].*?\>([^<]+)<\/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 "<!-- post $orig_title needs update : $post_needs_update -->";
+
+                                               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 {
+                                       // <enclosure>
+
+                                       $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);
+                                                       }
+                                               }
+                                       }
+
+                                       // <media:content>
+                                       // 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 "<select name=\"$id\" id=\"$id\" $attributes>";
+               foreach ($values as $v) {
+                       if ($v == $default)
+                               $sel = "selected=\"1\"";
+                        else
+                               $sel = "";
+
+                       print "<option value=\"$v\" $sel>$v</option>";
+               }
+               print "</select>";
+       }
+
+       function print_select_hash($id, $default, $values, $attributes = "") {
+               print "<select name=\"$id\" id='$id' $attributes>";
+               foreach (array_keys($values) as $v) {
+                       if ($v == $default)
+                               $sel = 'selected="selected"';
+                        else
+                               $sel = "";
+
+                       print "<option $sel value=\"$v\">".$values[$v]."</option>";
+               }
+
+               print "</select>";
+       }
+
+       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 "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
+                               type=\"radio\" $sel $attributes name=\"$id\">&nbsp;$v&nbsp;";
+
+               }
+       }
+
+       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"] . "<br>";
+
+                               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 = '&hellip;') {
+               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 "<link rel=\"stylesheet\" type=\"text/css\"
+                               href=\"$t/theme.css?$time \">";
+                       if (file_exists("$t/theme.js")) {
+                               print "<script type=\"text/javascript\" src=\"$t/theme.js?$time\">
+                                       </script>";
+                       }
+               }
+       }
+
+       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 "<select id=\"$id\" name=\"$id\" $attributes>";
+               if ($include_all_feeds) {
+                       print "<option value=\"0\">".__('All feeds')."</option>";
+               }
+
+               $result = db_query($link, "SELECT id,title FROM ttrss_feeds
+                       WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
+
+               if (db_num_rows($result) > 0 && $include_all_feeds) {
+                       print "<option disabled>--------</option>";
+               }
+
+               while ($line = db_fetch_assoc($result)) {
+                       if ($line["id"] == $default_id) {
+                               $is_selected = "selected=\"1\"";
+                       } else {
+                               $is_selected = "";
+                       }
+
+                       $title = truncate_string(htmlspecialchars($line["title"]), 40);
+
+                       printf("<option $is_selected value='%d'>%s</option>",
+                               $line["id"], $title);
+               }
+
+               print "</select>";
+       }
+
+       function print_feed_cat_select($link, $id, $default_id = "",
+               $attributes = "", $include_all_cats = true) {
+
+               print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
+
+               if ($include_all_cats) {
+                       print "<option value=\"0\">".__('Uncategorized')."</option>";
+               }
+
+               $result = db_query($link, "SELECT id,title FROM ttrss_feed_categories
+                       WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
+
+               if (db_num_rows($result) > 0 && $include_all_cats) {
+                       print "<option disabled=\"1\">--------</option>";
+               }
+
+               while ($line = db_fetch_assoc($result)) {
+                       if ($line["id"] == $default_id) {
+                               $is_selected = "selected=\"1\"";
+                       } else {
+                               $is_selected = "";
+                       }
+
+                       if ($line["title"])
+                               printf("<option $is_selected value='%d'>%s</option>",
+                                       $line["id"], htmlspecialchars($line["title"]));
+               }
+
+#              print "<option value=\"ADD_CAT\">" .__("Add category...") . "</option>";
+
+               print "</select>";
+       }
+
+       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 = "<div style=\"$note_style\">Article note: " . $line['note'] . "</div>" .
+                                       $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('/<img[^>]+>/is', '', $res);
+               }
+
+               if (strpos($res, "href=") === false)
+                       $res = rewrite_urls($res);
+
+               $charset_hack = '<head>
+                       <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+               </head>';
+
+               $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 = "<div id=\"subtoolbar_main\">";
+
+               $reply .= __('Select:')."
+                       <a href=\"#\" onclick=\"$sel_all_link\">".__('All')."</a>,
+                       <a href=\"#\" onclick=\"$sel_unread_link\">".__('Unread')."</a>,
+                       <a href=\"#\" onclick=\"$sel_inv_link\">".__('Invert')."</a>,
+                       <a href=\"#\" onclick=\"$sel_none_link\">".__('None')."</a></li>";
+
+               $reply .= " ";
+
+               $reply .= "<select dojoType=\"dijit.form.Select\"
+                       onchange=\"headlineActionsChange(this)\">";
+               $reply .= "<option value=\"false\">".__('Actions...')."</option>";
+
+               $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>";
+
+               $reply .= "<option value=\"$tog_unread_link\">".__('Unread')."</option>
+                       <option value=\"$tog_marked_link\">".__('Starred')."</option>
+                       <option value=\"$tog_published_link\">".__('Published')."</option>";
+
+               $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>";
+
+               $reply .= "<option value=\"$catchup_sel_link\">".__('Mark as read')."</option>";
+
+               if ($feed_id != "0") {
+                       $reply .= "<option value=\"$archive_sel_link\">".__('Archive')."</option>";
+               } else {
+                       $reply .= "<option value=\"$archive_sel_link\">".__('Move back')."</option>";
+                       $reply .= "<option value=\"$delete_sel_link\">".__('Delete')."</option>";
+
+               }
+
+               $reply .= "<option value=\"emailArticle(false)\">".__('Forward by email').
+                       "</option>";
+
+               if ($is_cat) $cat_q = "&is_cat=$is_cat";
+
+               if ($search) {
+                       $search_q = "&q=$search&m=$match_on&smode=$search_mode";
+               } else {
+                       $search_q = "";
+               }
+
+               $rss_link = htmlspecialchars(get_self_url_prefix() .
+                       "/public.php?op=rss&id=$feed_id$cat_q$search_q");
+
+               $reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>";
+
+               $reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>";
+
+               $reply .= "<option value=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>";
+
+               $reply .= "</select>";
+
+               $reply .= "</div>";
+
+               $reply .= "<div id=\"subtoolbar_ftitle\">";
+
+               if ($feed_site_url) {
+                       $target = "target=\"_blank\"";
+                       $reply .= "<a title=\"".__("Visit the website")."\" $target href=\"$feed_site_url\">".
+                               truncate_string($feed_title,30)."</a>";
+
+                       if ($error) {
+                               $reply .= " (<span class=\"error\" title=\"$error\">Error</span>)";
+                       }
+
+               } 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 .= "<span style=\"background : $bg_color; color : $fg_color\" >";
+                                       $reply .= $feed_title;
+                                       $reply .= "</span>";
+                               } else {
+                                       $reply .= $feed_title;
+                               }
+
+                       } else {
+                               $reply .= $feed_title;
+                       }
+               }
+
+               $reply .= "
+                       <a href=\"#\"
+                               title=\"".__("View as RSS feed")."\"
+                               onclick=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">
+                               <img class=\"noborder\" style=\"vertical-align : middle\" src=\"images/feed-icon-12x12.png\"></a>";
+
+               $reply .= "</div>";
+
+               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 "<div class=\"warning\" id=\"$id\">
+                       <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
+       }
+
+       function format_notice($msg, $id = "") {
+               global $link;
+               return "<div class=\"notice\" id=\"$id\">
+                       <img src=\"".theme_image($link, "images/sign_info.png")."\">$msg</div>";
+       }
+
+       function format_error($msg, $id = "") {
+               global $link;
+               return "<div class=\"error\" id=\"$id\">
+                       <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
+       }
+
+       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 .= "<audio id=\"$id\"\">
+                                       <source src=\"$url\"></source>
+                                       </audio>";
+
+                               $entry .= "<span onclick=\"player(this)\"
+                                       title=\"".__("Click to play")."\" status=\"0\"
+                                       class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
+
+                       } else {
+
+                               $entry .= "<object type=\"application/x-shockwave-flash\"
+                                       data=\"lib/button/musicplayer.swf?song_url=$url\"
+                                       width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
+                                       <param name=\"movie\"
+                                               value=\"lib/button/musicplayer.swf?song_url=$url\" />
+                                       </object>";
+                       }
+               }
+
+               $filename = substr($url, strrpos($url, "/")+1);
+
+               $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
+                       $filename . " (" . $ctype . ")" . "</a>";
+
+               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 "<article id='$id'><![CDATA["; };
+
+               $result = db_query($link, "SELECT rtl_content, always_display_enclosures 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"));
+                       $always_display_enclosures = sql_bool_to_bool(db_fetch_result($result, 0, "always_display_enclosures"));
+               } else {
+                       $rtl_content = false;
+                       $always_display_enclosures = false;
+               }
+
+               if ($rtl_content) {
+                       $rtl_tag = "dir=\"RTL\"";
+                       $rtl_class = "RTL";
+               } else {
+                       $rtl_tag = "";
+                       $rtl_class = "";
+               }
+
+               if ($mark_as_read) {
+                       $result = db_query($link, "UPDATE ttrss_user_entries
+                               SET unread = false,last_read = NOW()
+                               WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
+
+                       ccache_update($link, $feed_id, $_SESSION["uid"]);
+               }
+
+               $result = db_query($link, "SELECT title,link,content,feed_id,comments,int_id,
+                       ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
+                       (SELECT icon_url FROM ttrss_feeds WHERE id = feed_id) as icon_url,
+                       (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
+                       num_comments,
+                       tag_cache,
+                       author,
+                       orig_feed_id,
+                       note
+                       FROM ttrss_entries,ttrss_user_entries
+                       WHERE   id = '$id' AND ref_id = id AND owner_uid = " . $_SESSION["uid"]);
+
+               if ($result) {
+
+                       $line = db_fetch_assoc($result);
+
+                       if ($line["icon_url"]) {
+                               $feed_icon = "<img src=\"" . $line["icon_url"] . "\">";
+                       } else {
+                               $feed_icon = "&nbsp;";
+                       }
+
+                       $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 = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
+                       } else {
+                               if ($line["comments"] && $line["link"] != $line["comments"]) {
+                                       $entry_comments = "<a target='_blank' href=\"".$line["comments"]."\">comments</a>";
+                               }
+                       }
+
+                       if ($zoom_mode) {
+                               header("Content-Type: text/html");
+                               $rv['content'] .= "<html><head>
+                                               <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
+                                               <title>Tiny Tiny RSS - ".$line["title"]."</title>
+                                               <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
+                                       </head><body>";
+                       }
+
+                       $rv['content'] .= "<div id=\"PTITLE-$id\" style=\"display : none\">" .
+                               truncate_string(strip_tags($line['title']), 15) . "</div>";
+
+                       $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
+
+                       $rv['content'] .= "<div onclick=\"return postClicked(event, $id)\"
+                               class=\"postHeader\" id=\"POSTHDR-$id\">";
+
+                       $entry_author = $line["author"];
+
+                       if ($entry_author) {
+                               $entry_author = __(" - ") . $entry_author;
+                       }
+
+                       $parsed_updated = make_local_datetime($link, $line["updated"], true,
+                               false, true);
+
+                       $rv['content'] .= "<div class=\"postDate$rtl_class\">$parsed_updated</div>";
+
+                       if ($line["link"]) {
+                               $rv['content'] .= "<div clear='both'><a target='_blank'
+                                       title=\"".htmlspecialchars($line['title'])."\"
+                                       href=\"" .
+                                       $line["link"] . "\">" .
+                                       truncate_string($line["title"], 100) .
+                                       "<span class='author'>$entry_author</span></a></div>";
+                       } else {
+                               $rv['content'] .= "<div clear='both'>" . $line["title"] . "$entry_author</div>";
+                       }
+
+                       $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 = "&nbsp;"; # placeholder
+
+                       $rv['content'] .= "<div style='float : right'>
+                               <img src='".theme_image($link, 'images/tag.png')."'
+                               class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
+
+                       if (!$zoom_mode) {
+                               $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
+                                       <a title=\"".__('Edit tags for this article')."\"
+                                       href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
+
+                               $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
+                                       id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
+                                       position=\"below\">$tags_str_full</div>";
+
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-zoom.png')."\"
+                                               class='tagsPic' style=\"cursor : pointer\"
+                                               onclick=\"postOpenInNewTab(event, $id)\"
+                                               alt='Zoom' title='".__('Open article in new tab')."'>";
+
+                               //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
+
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-pub-note.png')."\"
+                                               class='tagsPic' style=\"cursor : pointer\"
+                                               onclick=\"editArticleNote($id)\"
+                                               alt='PubNote' title='".__('Edit article note')."'>";
+
+                               if (DIGEST_ENABLE) {
+                                       $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
+                                               class='tagsPic' style=\"cursor : pointer\"
+                                               onclick=\"emailArticle($id)\"
+                                               alt='Zoom' title='".__('Forward by email')."'>";
+                               }
+
+                               if (ENABLE_TWEET_BUTTON) {
+                                       $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
+                                                       class='tagsPic' style=\"cursor : pointer\"
+                                                       onclick=\"tweetArticle($id)\"
+                                                       alt='Zoom' title='".__('Share on Twitter')."'>";
+                               }
+
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-share.png')."\"
+                                       class='tagsPic' style=\"cursor : pointer\"
+                                       onclick=\"shareArticle(".$line['int_id'].")\"
+                                       alt='Zoom' title='".__('Share by URL')."'>";
+
+                               $rv['content'] .= "<img src=\"".theme_image($link, 'images/digest_checkbox.png')."\"
+                                               class='tagsPic' style=\"cursor : pointer\"
+                                               onclick=\"closeArticlePanel($id)\"
+                                               alt='Zoom' title='".__('Close this panel')."'>";
+
+                       } else {
+                               $tags_str = strip_tags($tags_str);
+                               $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
+                       }
+                       $rv['content'] .= "</div>";
+                       $rv['content'] .= "<div clear='both'>$entry_comments</div>";
+
+                       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'] .= "<div clear='both'>";
+                                       $rv['content'] .= __("Originally from:");
+
+                                       $rv['content'] .= "&nbsp;";
+
+                                       $tmp_line = db_fetch_assoc($tmp_result);
+
+                                       $rv['content'] .= "<a target='_blank'
+                                               href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
+                                               $tmp_line['title'] . "</a>";
+
+                                       $rv['content'] .= "&nbsp;";
+
+                                       $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
+                                       $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
+
+                                       $rv['content'] .= "</div>";
+                               }
+                       }
+
+                       $rv['content'] .= "</div>";
+
+                       $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
+                               if ($line['note']) {
+                                       $rv['content'] .= format_article_note($id, $line['note']);
+                               }
+                       $rv['content'] .= "</div>";
+
+                       $rv['content'] .= "<div class=\"postIcon\">" .
+                               "<a target=\"_blank\" title=\"".__("Visit the website")."\"$
+                               href=\"".htmlspecialchars($feed_site_url)."\">".
+                               $feed_icon . "</a></div>";
+
+                       $rv['content'] .= "<div class=\"postContent\">";
+
+                       $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'] .= "</div>";
+
+                       $rv['content'] .= "</div>";
+
+               }
+
+               if ($zoom_mode) {
+                       $rv['content'] .= "
+                               <div style=\"text-align : center\">
+                               <button onclick=\"return window.close()\">".
+                                       __("Close this window")."</button></div>";
+                       $rv['content'] .= "</body></html>";
+               }
+
+               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'] = "<div align='center'>".__('Feed not found.')."</div>";
+                       }
+               }
+
+               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 = "<span id=\"HLLCTR-$id\">";
+                               $labels_str .= format_article_labels($labels, $id);
+                               $labels_str .= "</span>";
+
+                               if (count($topmost_article_ids) < 3) {
+                                       array_push($topmost_article_ids, $id);
+                               }
+
+                               if ($line["last_read"] == "" && !sql_bool_to_bool($line["unread"])) {
+
+                                       $update_pic = "<img id='FUPDPIC-$id' src=\"".
+                                               theme_image($link, 'images/updated.png')."\"
+                                               alt=\"Updated\">";
+                               } else {
+                                       $update_pic = "<img id='FUPDPIC-$id' src=\"images/blank_icon.gif\"
+                                               alt=\"Updated\">";
+                               }
+
+                               if (sql_bool_to_bool($line["unread"]) &&
+                                       time() - strtotime($line["updated_noms"]) < $fresh_intl) {
+
+                                       $update_pic = "<img id='FUPDPIC-$id' src=\"".
+                                               theme_image($link, 'images/fresh_sign.png')."\" alt=\"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 = "<img id=\"FMPIC-$id\"
+                                               src=\"".theme_image($link, 'images/mark_set.png')."\"
+                                               class=\"markedPic\" alt=\"Unstar article\"
+                                               onclick='javascript:toggleMark($id)'>";
+                               } else {
+                                       $marked_pic = "<img id=\"FMPIC-$id\"
+                                               src=\"".theme_image($link, 'images/mark_unset.png')."\"
+                                               class=\"markedPic\" alt=\"Star article\"
+                                               onclick='javascript:toggleMark($id)'>";
+                               }
+
+                               if ($line["published"] == "t" || $line["published"] == "1") {
+                                       $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($link,
+                                               'images/pub_set.png')."\"
+                                               class=\"markedPic\"
+                                               alt=\"Unpublish article\" onclick='javascript:togglePub($id)'>";
+                               } else {
+                                       $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($link,
+                                               'images/pub_unset.png')."\"
+                                               class=\"markedPic\"
+                                               alt=\"Publish article\" onclick='javascript:togglePub($id)'>";
+                               }
+
+#                              $content_link = "<a target=\"_blank\" href=\"".$line["link"]."\">" .
+#                                      $line["title"] . "</a>";
+
+#                              $content_link = "<a
+#                                      href=\"" . htmlspecialchars($line["link"]) . "\"
+#                                      onclick=\"view($id,$feed_id);\">" .
+#                                      $line["title"] . "</a>";
+
+#                              $content_link = "<a href=\"javascript:viewContentUrl('".$line["link"]."');\">" .
+#                                      $line["title"] . "</a>";
+
+                               $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 = "<img class='hlScorePic' src=\"images/$score_pic\"
+                                       onclick=\"adjustArticleScore($id, $score)\" title=\"$score $score_title\">"; */
+
+                               $score_pic = "<img class='hlScorePic' src=\"$score_pic\"
+                                       title=\"$score\">";
+
+                               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 = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
+                               } else {
+                                       $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/feed-icon-12x12.png\" alt=\"\">";
+                               }
+
+                               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 = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
+
+                                                       $reply['content'] .= "<div class='cdmFeedTitle'>".
+                                                               "<div style=\"float : right\">$feed_icon_img</div>".
+                                                               "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
+                                                               $line["feed_title"]."</a> $vf_catchup_link</div>";
+
+                                               }
+                                       }
+
+                                       $mouseover_attrs = "onmouseover='postMouseIn($id)'
+                                               onmouseout='postMouseOut($id)'";
+
+                                       $reply['content'] .= "<div class='$class' id='RROW-$id' $mouseover_attrs>";
+
+                                       $reply['content'] .= "<div class='hlUpdPic'>$update_pic</div>";
+
+                                       $reply['content'] .= "<div class='hlLeft'>";
+
+                                       $reply['content'] .= "<input type=\"checkbox\" onclick=\"tSR(this)\"
+                                                       id=\"RCHK-$id\">";
+
+                                       $reply['content'] .= "$marked_pic";
+                                       $reply['content'] .= "$published_pic";
+
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "<div onclick='return hlClicked(event, $id)'
+                                               class=\"hlTitle\"><span class='hlContent$hlc_suffix'>";
+                                       $reply['content'] .= "<a id=\"RTITLE-$id\"
+                                               href=\"" . htmlspecialchars($line["link"]) . "\"
+                                               onclick=\"\">" .
+                                               truncate_string($line["title"], 200);
+
+                                       if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) {
+                                               if ($content_preview) {
+                                                       $reply['content'] .= "<span class=\"contentPreview\"> - $content_preview</span>";
+                                               }
+                                       }
+
+                                       $reply['content'] .= "</a></span>";
+
+                                       $reply['content'] .= $labels_str;
+
+                                       if (!get_pref($link, 'VFEED_GROUP_BY_FEED') &&
+                                               defined('_SHOW_FEED_TITLE_IN_VFEEDS')) {
+                                               if (@$line["feed_title"]) {
+                                                       $reply['content'] .= "<span class=\"hlFeed\">
+                                                               (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
+                                                               $line["feed_title"]."</a>)
+                                                       </span>";
+                                               }
+                                       }
+
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "<span class=\"hlUpdated\">$updated_fmt</span>";
+                                       $reply['content'] .= "<div class=\"hlRight\">";
+
+                                       $reply['content'] .= $score_pic;
+
+                                       if ($line["feed_title"] && !get_pref($link, 'VFEED_GROUP_BY_FEED')) {
+
+                                               $reply['content'] .= "<span onclick=\"viewfeed($feed_id)\"
+                                                       style=\"cursor : pointer\"
+                                                       title=\"".htmlspecialchars($line['feed_title'])."\">
+                                                       $feed_icon_img<span>";
+                                       }
+
+                                       $reply['content'] .= "</div>";
+                                       $reply['content'] .= "</div>";
+
+                               } 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 = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
+
+                                                       $has_feed_icon = feed_has_icon($feed_id);
+
+                                                       if ($has_feed_icon) {
+                                                               $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
+                                                       } else {
+                                                               //$feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\" alt=\"\">";
+                                                       }
+
+                                                       $reply['content'] .= "<div class='cdmFeedTitle'>".
+                                                               "<div style=\"float : right\">$feed_icon_img</div>".
+                                                               "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
+                                                               $line["feed_title"]."</a> $vf_catchup_link</div>";
+                                               }
+                                       }
+
+                                       $expand_cdm = get_pref($link, 'CDM_EXPANDED');
+
+                                       $mouseover_attrs = "onmouseover='postMouseIn($id)'
+                                               onmouseout='postMouseOut($id)'";
+
+                                       $reply['content'] .= "<div class=\"$class\"
+                                               id=\"RROW-$id\" $mouseover_attrs'>";
+
+                                       $reply['content'] .= "<div class=\"cdmHeader\">";
+
+                                       $reply['content'] .= "<div>";
+
+                                       $reply['content'] .= "<input type=\"checkbox\" onclick=\"toggleSelectRowById(this,
+                                                       'RROW-$id')\" id=\"RCHK-$id\"/>";
+
+                                       $reply['content'] .= "$marked_pic";
+                                       $reply['content'] .= "$published_pic";
+
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "<span id=\"RTITLE-$id\"
+                                               onclick=\"return cdmClicked(event, $id);\"
+                                               class=\"titleWrap$hlc_suffix\">
+                                               <a class=\"title\"
+                                               title=\"".htmlspecialchars($line['title'])."\"
+                                               target=\"_blank\" href=\"".
+                                               htmlspecialchars($line["link"])."\">".
+                                               truncate_string($line["title"], 100) .
+                                               " $entry_author</a>";
+
+                                       $reply['content'] .= $labels_str;
+
+                                       if (!get_pref($link, 'VFEED_GROUP_BY_FEED') &&
+                                               defined('_SHOW_FEED_TITLE_IN_VFEEDS')) {
+                                               if (@$line["feed_title"]) {
+                                                       $reply['content'] .= "<span class=\"hlFeed\">
+                                                               (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
+                                                               $line["feed_title"]."</a>)
+                                                       </span>";
+                                               }
+                                       }
+
+                                       if (!$expand_cdm)
+                                               $content_hidden = "style=\"display : none\"";
+                                       else
+                                               $excerpt_hidden = "style=\"display : none\"";
+
+                                       $reply['content'] .= "<span $excerpt_hidden
+                                               id=\"CEXC-$id\" class=\"cdmExcerpt\"> - $content_preview</span>";
+
+                                       $reply['content'] .= "</span>";
+
+                                       $reply['content'] .= "<div>";
+                                       $reply['content'] .= "<span class='updated'>$updated_fmt</span>";
+                                       $reply['content'] .= "$score_pic";
+
+                                       if (!get_pref($link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) {
+                                               $reply['content'] .= "<span style=\"cursor : pointer\"
+                                                       title=\"".htmlspecialchars($line["feed_title"])."\"
+                                                       onclick=\"viewfeed($feed_id)\">$feed_icon_img</span>";
+                                       }
+                                       $reply['content'] .= "<div class=\"updPic\">$update_pic</div>";
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "<div class=\"cdmContent\" $content_hidden
+                                               onclick=\"return cdmClicked(event, $id);\"
+                                               id=\"CICD-$id\">";
+
+                                       $reply['content'] .= "<div class=\"cdmContentInner\">";
+
+                       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'] .= "<div clear='both'>";
+                                                       $reply['content'] .= __("Originally from:");
+
+                                                       $reply['content'] .= "&nbsp;";
+
+                                                       $tmp_line = db_fetch_assoc($tmp_result);
+
+                                                       $reply['content'] .= "<a target='_blank'
+                                                               href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
+                                                               $tmp_line['title'] . "</a>";
+
+                                                       $reply['content'] .= "&nbsp;";
+
+                                                       $reply['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
+                                                       $reply['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
+
+                                                       $reply['content'] .= "</div>";
+                                               }
+                                       }
+
+                                       $feed_site_url = $line["site_url"];
+
+                                       $article_content = sanitize_rss($link, $line["content_preview"],
+                                                       false, false, $feed_site_url);
+
+                                       $reply['content'] .= "<div id=\"POSTNOTE-$id\">";
+                                       if ($line['note']) {
+                                               $reply['content'] .= format_article_note($id, $line['note']);
+                                       }
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "<span id=\"CWRAP-$id\">";
+                                       $reply['content'] .= $expand_cdm ? $article_content : '';
+                                       $reply['content'] .= "</span>";
+
+/*                                     $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'] .= "</div>";
+
+                                       $reply['content'] .= "<div class=\"cdmFooter\">";
+
+                                       $tag_cache = $line["tag_cache"];
+
+                                       $tags_str = format_tags_string(
+                                               get_article_tags($link, $id, $_SESSION["uid"], $tag_cache),
+                                               $id);
+
+                                       $reply['content'] .= "<img src='".theme_image($link,
+                                                       'images/tag.png')."' alt='Tags' title='Tags'>
+                                               <span id=\"ATSTR-$id\">$tags_str</span>
+                                               <a title=\"".__('Edit tags for this article')."\"
+                                               href=\"#\" onclick=\"editArticleTags($id, $feed_id, true)\">(+)</a>";
+
+                                       $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 = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
+                                       } else {
+                                               if ($line["comments"] && $line["link"] != $line["comments"]) {
+                                                       $entry_comments = "<a target='_blank' href=\"".$line["comments"]."\">comments</a>";
+                                               }
+                                       }
+
+                                       if ($entry_comments) $reply['content'] .= "&nbsp;($entry_comments)";
+
+                                       $reply['content'] .= "<div style=\"float : right\">";
+
+                                       $reply['content'] .= "<img src=\"images/art-zoom.png\"
+                                               onclick=\"zoomToArticle(event, $id)\"
+                                               style=\"cursor : pointer\"
+                                               alt='Zoom'
+                                               title='".__('Open article in new tab')."'>";
+
+                                       //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
+
+                                       $reply['content'] .= "<img src=\"images/art-pub-note.png\"
+                                               style=\"cursor : pointer\" style=\"cursor : pointer\"
+                                               onclick=\"editArticleNote($id)\"
+                                               alt='PubNote' title='".__('Edit article note')."'>";
+
+                                       if (DIGEST_ENABLE) {
+                                               $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
+                                                       style=\"cursor : pointer\"
+                                                       onclick=\"emailArticle($id)\"
+                                                       alt='Zoom' title='".__('Forward by email')."'>";
+                                       }
+
+                                       if (ENABLE_TWEET_BUTTON) {
+                                               $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
+                                                       class='tagsPic' style=\"cursor : pointer\"
+                                                       onclick=\"tweetArticle($id)\"
+                                                       alt='Zoom' title='".__('Share on Twitter')."'>";
+                                       }
+
+                                       $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-share.png')."\"
+                                               class='tagsPic' style=\"cursor : pointer\"
+                                               onclick=\"shareArticle(".$line['int_id'].")\"
+                                               alt='Zoom' title='".__('Share by URL')."'>";
+
+                                       $reply['content'] .= "<img src=\"images/digest_checkbox.png\"
+                                               style=\"cursor : pointer\" style=\"cursor : pointer\"
+                                               onclick=\"dismissArticle($id)\"
+                                               alt='Dismiss' title='".__('Dismiss article')."'>";
+
+                                       $reply['content'] .= "</div>";
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "</div>";
+
+                                       $reply['content'] .= "</div>";
+
+                               }
+
+                               ++$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'] .= "<div class='whiteBox'>$message";
+
+                               $reply['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
+
+                               $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'] .= "<br/>";
+                                       $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
+                                               __('Some feeds have update errors (click for details)')."</a>";
+                               }
+                               $reply['content'] .= "</span></p></div>";
+                       }
+               }
+
+               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 "<a href=\"javascript:viewfeed('$key_escaped') \" style=\"font-size: " .
+                               $size . "px\" title=\"$value articles tagged with " .
+                               $key . '">' . $key . '</a> ';
+               }
+       }
+
+       function print_checkpoint($n, $s) {
+               $ts = getmicrotime();
+               echo sprintf("<!-- CP[$n] %.4f seconds -->", $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 = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
+
+                       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 .= ", &hellip;";
+               }
+
+               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("<span class='hlLabelRef'
+                               style='color : %s; background-color : %s'>%s</span>",
+                                       $l[2], $l[3], $l[1]);
+                       }
+
+               return $labels_str;
+
+       }
+
+       function format_article_note($id, $note) {
+
+               $str = "<div class='articleNote'        onclick=\"editArticleNote($id)\">
+                       <div class='noteEdit' onclick=\"editArticleNote($id)\">".
+                       __('(edit note)')."</div>$note</div>";
+
+               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'] = "<div class='whiteBox'>". $error . "</div>";
+
+               $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'] = "<div class='whiteBox'>".__('No feed selected.');
+
+               $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
+
+               $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'] .= "<br/>";
+                       $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
+                               __('Some feeds have update errors (click for details)')."</a>";
+               }
+               $reply['headlines']['content'] .= "</span></p>";
+
+               $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, '<html>') === false
+                       && stripos($content, '<html ') === false
+               ) {
+                       return false;
+               }
+
+               return true;
+       }
+
+       function print_label_select($link, $name, $value, $attributes = "") {
+
+               $result = db_query($link, "SELECT caption FROM ttrss_labels2
+                       WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
+
+               print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
+                       "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
+
+               while ($line = db_fetch_assoc($result)) {
+
+                       $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
+
+                       print "<option value=\"".htmlspecialchars($line["caption"])."\"
+                               $issel>" . htmlspecialchars($line["caption"]) . "</option>";
+
+               }
+
+#              print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
+
+               print "</select>";
+
+
+       }
+
+       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 .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
+#                                      $filename . " (" . $ctype . ")" . "</a>";
+
+                               array_push($entries_html, $entry);
+
+                               $entry = array();
+
+                               $entry["type"] = $ctype;
+                               $entry["filename"] = $filename;
+                               $entry["url"] = $url;
+
+                               array_push($entries, $entry);
+                       }
+
+                       $rv .= "<div class=\"postEnclosures\">";
+
+                       if (!get_pref($link, "STRIP_IMAGES")) {
+                               if ($always_display_enclosures ||
+                                                       !preg_match("/<img/i", $article_content)) {
+
+                                       foreach ($entries as $entry) {
+
+                                               if (preg_match("/image/", $entry["type"]) ||
+                                                               preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
+
+                                                               $rv .= "<p><img
+                                                               alt=\"".htmlspecialchars($entry["filename"])."\"
+                                                               src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
+                                               }
+                                       }
+                               }
+                       }
+
+                       if (count($entries) == 1) {
+                               $rv .= __("Attachment:") . " ";
+                       } else {
+                               $rv .= __("Attachments:") . " ";
+                       }
+
+                       $rv .= join(", ", $entries_html);
+
+                       $rv .= "</div>";
+               }
+
+               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 "<style type=\"text/css\">";
+                       print str_replace("<br/>", "\n", $value);
+                       print "</style>";
+               }
+
+       }
+
+       function rewrite_urls($line) {
+               global $url_regex;
+
+               $urls = null;
+
+               $result = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
+                       "<a target=\"_blank\" href=\"\\1\">\\1</a>", $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 "<select style='width: 100%' name='profile'>";
+
+                               print "<option value='0'>" . __("Default profile") . "</option>";
+
+                               while ($line = db_fetch_assoc($result)) {
+                                       $id = $line["id"];
+                                       $title = $line["title"];
+
+                                       print "<option value='$id'>$title</option>";
+                               }
+
+                               print "</select>";
+
+                               $_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 (file)
index 0000000..29663f3
--- /dev/null
@@ -0,0 +1,57 @@
+<?php # This file has been generated at:  Wed Nov 23 10:40:20 MSK 2011
+
+__("Title");
+__("Title or Content");
+__("Link");
+__("Content");
+__("Article Date");
+
+__("Delete article");
+__("Mark as read");
+__("Set starred");
+__("Publish article");
+__("Assign tags");
+__("Assign label");
+
+__('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.');
+__('Display expanded list of feed articles, instead of separate displays for headlines and article content');
+__('When "Mark as read" button is clicked in toolbar, automatically open next feed with unread articles.');
+__('This option enables sending daily digest of new (and unread) headlines on your configured e-mail address');
+__('This option enables marking articles as read automatically while you scroll article list.');
+__('Strip all but most common HTML tags when reading articles.');
+__('When auto-detecting tags in articles these tags will not be applied (comma-separated list).');
+__('When this option is enabled, headlines in Special feeds and Labels are grouped by feeds');
+__('Use feed-specified date to sort headlines instead of local import date.');
+__('Customize CSS stylesheet to your liking');
+__('Click to register your SSL client certificate with tt-rss');
+__('Purge old posts after this number of days (0 - disables)');
+__('Default interval between feed updates');
+__('Amount of articles to display at once');
+__('Allow duplicate posts');
+__('Enable feed categories');
+__('Show content preview in headlines list');
+__('Short date format');
+__('Long date format');
+__('Combined feed display');
+__('Hide feeds with no unread messages');
+__('On catchup show next feed');
+__('Sort feeds by unread articles count');
+__('Reverse headline order (oldest first)');
+__('Enable e-mail digest');
+__('Confirm marking feed as read');
+__('Automatically mark articles as read');
+__('Strip unsafe tags from articles');
+__('Blacklisted tags');
+__('Maximum age of fresh articles (in hours)');
+__('Mark articles in e-mail digest as read');
+__('Automatically expand articles in combined mode');
+__('Purge unread articles');
+__('Show special feeds when hiding read feeds');
+__('Group headlines in virtual feeds');
+__('Do not show images in articles');
+__('Enable external API');
+__('User timezone');
+__('Sort headlines by feed date');
+__('Customize stylesheet');
+__('Login with an SSL certificate');
+?>
diff --git a/include/login_form.php b/include/login_form.php
new file mode 100644 (file)
index 0000000..04ca3e6
--- /dev/null
@@ -0,0 +1,199 @@
+<html>
+<head>
+       <title>Tiny Tiny RSS : Login</title>
+       <link rel="stylesheet" type="text/css" href="lib/dijit/themes/claro/claro.css"/>
+       <link rel="stylesheet" type="text/css" href="tt-rss.css">
+       <link rel="shortcut icon" type="image/png" href="images/favicon.png">
+       <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+       <script type="text/javascript" src="lib/dojo/dojo.js" djConfig="parseOnLoad: true"></script>
+       <script type="text/javascript" src="lib/prototype.js"></script>
+       <script type="text/javascript" src="lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"></script>
+       <script type="text/javascript" src="functions.js"></script>
+       <script type="text/javascript" charset="utf-8" src="errors.php?mode=js"></script>
+</head>
+
+<body id="ttrssLogin" class="claro">
+
+<script type="text/javascript">
+function init() {
+
+       dojo.require("dijit.Dialog");
+
+       var test = setCookie("ttrss_test", "TEST");
+
+       if (getCookie("ttrss_test") != "TEST") {
+               return fatalError(2);
+       }
+
+       var limit_set = getCookie("ttrss_bwlimit");
+
+       if (limit_set == "true") {
+               document.forms["loginForm"].bw_limit.checked = true;
+       }
+
+       document.forms["loginForm"].login.focus();
+}
+
+function fetchProfiles() {
+       try {
+               var params = Form.serialize('loginForm');
+               var query = "?op=getProfiles&" + params;
+
+               if (query) {
+                       new Ajax.Request("backend.php", {
+                               parameters: query,
+                                       onComplete: function(transport) {
+                                               if (transport.responseText.match("select")) {
+                                                       $('profile_box').innerHTML = transport.responseText;
+                                               }
+                               } });
+               }
+
+       } catch (e) {
+               exception_error("fetchProfiles", e);
+       }
+}
+
+
+function languageChange(elem) {
+       try {
+               document.forms['loginForm']['click'].disabled = true;
+
+               var lang = elem[elem.selectedIndex].value;
+               setCookie("ttrss_lang", lang, <?php print SESSION_COOKIE_LIFETIME ?>);
+               window.location.reload();
+       } catch (e) {
+               exception_error("languageChange", e);
+       }
+}
+
+function gotoRegForm() {
+       window.location.href = "register.php";
+       return false;
+}
+
+function bwLimitChange(elem) {
+       try {
+               var limit_set = elem.checked;
+
+               setCookie("ttrss_bwlimit", limit_set,
+                       <?php print SESSION_COOKIE_LIFETIME ?>);
+
+       } catch (e) {
+               exception_error("bwLimitChange", e);
+       }
+}
+
+function validateLoginForm(f) {
+       try {
+
+               if (f.login.value.length == 0) {
+                       new Effect.Highlight(f.login);
+                       return false;
+               }
+
+               if (f.password.value.length == 0) {
+                       new Effect.Highlight(f.password);
+                       return false;
+               }
+
+               document.forms['loginForm']['click'].disabled = true;
+
+               return true;
+       } catch (e) {
+               exception_error("validateLoginForm", e);
+               return true;
+       }
+}
+</script>
+
+<script type="text/javascript">
+       Event.observe(window, 'load', function() {
+               init();
+       });
+</script>
+
+<form action="" method="POST" id="loginForm" name="loginForm" onsubmit="return validateLoginForm(this)">
+<input type="hidden" name="login_action" value="do_login">
+
+<table class="loginForm2">
+<tr>
+       <td class="loginTop" valign="bottom" align="left">
+               <img src="images/logo_wide.png">
+       </td>
+</tr><tr>
+       <td align="center" valign="middle" class="loginMiddle" height="100%">
+               <?php if ($_SESSION['login_error_msg']) { ?>
+                       <div class="loginError"><?php echo $_SESSION['login_error_msg'] ?></div>
+                       <?php $_SESSION['login_error_msg'] = ""; ?>
+               <?php } ?>
+               <table>
+                       <tr><td align="right"><?php echo __("Login:") ?></td>
+                       <td align="right"><input name="login"
+                               onchange="fetchProfiles()" onfocus="fetchProfiles()"
+                               value="<?php echo get_remote_user($link) ?>"></td></tr>
+                       <tr><td align="right"><?php echo __("Password:") ?></td>
+                       <td align="right"><input type="password" name="password"
+                               onchange="fetchProfiles()" onfocus="fetchProfiles()"
+                               value="<?php echo get_remote_fakepass($link) ?>"></td></tr>
+                       <tr><td align="right"><?php echo __("Language:") ?></td>
+                       <td align="right">
+                       <?php
+                               print_select_hash("language", $_COOKIE["ttrss_lang"], get_translations(),
+                                       "style='width : 100%' onchange='languageChange(this)'");
+
+                       ?>
+                       </td></tr>
+
+                       <tr><td align="right"><?php echo __("Profile:") ?></td>
+                       <td align="right" id="profile_box">
+                       <select style='width : 100%' disabled='disabled'>
+                               <option><?php echo __("Default profile") ?></option></select>
+                       </td></tr>
+
+                       <!-- <tr><td colspan="2">
+                               <input type="checkbox" name="remember_me" id="remember_me">
+                               <label for="remember_me">Remember me on this computer</label>
+                       </td></tr> -->
+
+                       <tr><td colspan="2" align="right" class="innerLoginCell">
+
+                       <button type="submit" name='click'><?php echo __('Log in') ?></button>
+                       <?php if (defined('ENABLE_REGISTRATION') && ENABLE_REGISTRATION) { ?>
+                               <button onclick="return gotoRegForm()">
+                                       <?php echo __("Create new account") ?></button>
+                       <?php } ?>
+
+                               <input type="hidden" name="action" value="login">
+                               <input type="hidden" name="rt"
+                                       value="<?php if ($return_to != 'none') { echo $return_to; } ?>">
+                       </td></tr>
+
+                       <tr><td colspan="2" align="right" class="innerLoginCell">
+
+                       <div class="small">
+                       <input name="bw_limit" id="bw_limit" type="checkbox"
+                               onchange="bwLimitChange(this)">
+                       <label for="bw_limit">
+                       <?php echo __("Use less traffic") ?></label></div>
+
+                       </td></tr>
+
+
+               </table>
+       </td>
+</tr><tr>
+       <td align="center" class="loginBottom">
+       <a href="http://tt-rss.org/">Tiny Tiny RSS</a>
+       <?php if (!defined('HIDE_VERSION')) { ?>
+                v<?php echo VERSION ?>
+       <?php } ?>
+       &copy; 2005&ndash;<?php echo date('Y') ?> <a href="http://fakecake.org/">Andrew Dolgov</a>
+       </td>
+</tr>
+
+</table>
+
+</form>
+
+</body></html>
diff --git a/include/sanity_check.php b/include/sanity_check.php
new file mode 100644 (file)
index 0000000..3e5c098
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+       require_once "functions.php";
+
+       define('EXPECTED_CONFIG_VERSION', 23);
+       define('SCHEMA_VERSION', 86);
+
+       if (!file_exists("config.php")) {
+               print "<b>Fatal Error</b>: You forgot to copy
+               <b>config.php-dist</b> to <b>config.php</b> 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 "<b>Fatal error</b>: 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 "<b>Fatal Error</b>: XML Import/Export tools (<b>xml-export.php</b>
+               and <b>xml-import.php</b>) could be used maliciously. Please remove them
+               from your TT-RSS instance.\n";
+               exit;
+       }
+
+       if (SINGLE_USER_MODE && DAEMON_UPDATE_LOGIN_LIMIT > 0) {
+               print "<b>Fatal Error</b>: 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 <a href='http://tt-rss.org/wiki/UpdatingFeeds'>wiki</a>.";
+
+       }
+
+       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 "<b>Fatal Error</b>: $err_msg\n";
+               exit;
+       }
+
+?>
diff --git a/include/sanity_config.php b/include/sanity_config.php
new file mode 100644 (file)
index 0000000..51c9d52
--- /dev/null
@@ -0,0 +1,3 @@
+<?php # This file has been generated at:  Tue Apr 26 18:40:48 MSD 2011
+define('GENERATED_CONFIG_CHECK', 23);
+$requred_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'SELF_URL_PATH', 'SINGLE_USER_MODE', 'CACHE_DIR', 'SIMPLEPIE_CACHE_IMAGES', 'ICONS_DIR', 'ICONS_URL', 'TMP_DIRECTORY', 'DAEMON_SLEEP_INTERVAL', 'DATABASE_BACKED_SESSIONS', 'SESSION_CHECK_ADDRESS', 'SESSION_COOKIE_LIFETIME', 'SESSION_EXPIRE_TIME', 'DAEMON_UPDATE_LOGIN_LIMIT', 'CHECK_FOR_NEW_VERSION', 'DIGEST_ENABLE', 'DIGEST_EMAIL_LIMIT', 'DAEMON_SENDS_DIGESTS', 'MYSQL_CHARSET', 'DEFAULT_UPDATE_METHOD', 'COUNTERS_MAX_AGE', 'DIGEST_FROM_NAME', 'DIGEST_FROM_ADDRESS', 'DIGEST_SUBJECT', 'DIGEST_SMTP_HOST', 'DIGEST_SMTP_LOGIN', 'DIGEST_SMTP_PASSWORD', 'DAEMON_FEED_LIMIT', 'ALLOW_REMOTE_USER_AUTH', 'AUTO_LOGIN', 'AUTO_CREATE_USER', 'LOCK_DIRECTORY', 'ENABLE_GZIP_OUTPUT', 'PHP_EXECUTABLE', 'ENABLE_REGISTRATION', 'REG_NOTIFY_ADDRESS', 'REG_MAX_USERS', 'FEEDBACK_URL', 'FORCE_ARTICLE_PURGE', 'SPHINX_ENABLED', 'SPHINX_INDEX', 'ENABLE_TWEET_BUTTON', 'CONSUMER_KEY', 'CONSUMER_SECRET', 'PUBSUBHUBBUB_HUB', 'PUBSUBHUBBUB_ENABLED', 'ISCONFIGURED', 'CONFIG_VERSION'); ?>
diff --git a/include/sessions.php b/include/sessions.php
new file mode 100644 (file)
index 0000000..8588f58
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+       // Original from http://www.daniweb.com/code/snippet43.html
+
+       require_once "config.php";
+       require_once "db.php";
+
+       $session_expire = SESSION_EXPIRE_TIME; //seconds
+       $session_name = (!defined('TTRSS_SESSION_NAME')) ? "ttrss_sid" : TTRSS_SESSION_NAME;
+
+       if ($_SERVER['HTTPS'] == "on") {
+               $session_name .= "_ssl";
+               ini_set("session.cookie_secure", true);
+       }
+
+       ini_set("session.gc_probability", 50);
+       ini_set("session.name", $session_name);
+       ini_set("session.use_only_cookies", true);
+       ini_set("session.gc_maxlifetime", SESSION_EXPIRE_TIME);
+
+       function ttrss_open ($s, $n) {
+
+               global $session_connection;
+
+               $session_connection = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
+
+               return true;
+       }
+
+       function ttrss_read ($id){
+
+               global $session_connection,$session_read;
+
+               $query = "SELECT data FROM ttrss_sessions WHERE id='$id'";
+
+               $res = db_query($session_connection, $query);
+
+               if (db_num_rows($res) != 1) {
+                       return "";
+               } else {
+                       $session_read = db_fetch_assoc($res);
+                       $session_read["data"] = base64_decode($session_read["data"]);
+                       return $session_read["data"];
+               }
+       }
+
+       function ttrss_write ($id, $data) {
+
+               if (! $data) {
+                       return false;
+               }
+
+               global $session_connection, $session_read, $session_expire;
+
+               $expire = time() + $session_expire;
+
+               $data = db_escape_string(base64_encode($data), $session_connection);
+
+               if ($session_read) {
+                       $query = "UPDATE ttrss_sessions SET data='$data',
+                                       expire='$expire' WHERE id='$id'";
+               } else {
+                       $query = "INSERT INTO ttrss_sessions (id, data, expire)
+                                       VALUES ('$id', '$data', '$expire')";
+               }
+
+               db_query($session_connection, $query);
+               return true;
+       }
+
+       function ttrss_close () {
+
+               global $session_connection;
+
+               db_close($session_connection);
+
+               return true;
+       }
+
+       function ttrss_destroy ($id) {
+
+               global $session_connection;
+
+               $query = "DELETE FROM ttrss_sessions WHERE id = '$id'";
+
+               db_query($session_connection, $query);
+
+               return true;
+       }
+
+       function ttrss_gc ($expire) {
+
+               global $session_connection;
+
+               $query = "DELETE FROM ttrss_sessions WHERE expire < " . time();
+
+               db_query($session_connection, $query);
+       }
+
+       if (DATABASE_BACKED_SESSIONS) {
+               session_set_save_handler("ttrss_open",
+                       "ttrss_close", "ttrss_read", "ttrss_write",
+                       "ttrss_destroy", "ttrss_gc");
+       }
+
+       session_set_cookie_params(SESSION_COOKIE_LIFETIME);
+
+       session_start();
+?>
diff --git a/include/version.php b/include/version.php
new file mode 100644 (file)
index 0000000..61445c5
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+       define('VERSION', "1.5.7");
+?>
index e73671ed4562b13bab8a31c8c19551fee4f6af5f..5b5560a9a548a2552edf4746db213bf56f960ec7 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1 +1,211 @@
-<?php header("Location: tt-rss.php"); ?>
+<?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
+       require_once "functions.php";
+       require_once "sessions.php";
+       require_once "sanity_check.php";
+       require_once "version.php";
+       require_once "config.php";
+       require_once "db-prefs.php";
+
+       $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
+
+       init_connection($link);
+
+       login_sequence($link);
+
+       $dt_add = time();
+
+       no_cache_incantation();
+
+       header('Content-Type: text/html; charset=utf-8');
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+       <title>Tiny Tiny RSS</title>
+       <link rel="stylesheet" type="text/css" href="lib/dijit/themes/claro/claro.css"/>
+       <link rel="stylesheet" type="text/css" href="tt-rss.css?<?php echo $dt_add ?>"/>
+       <link rel="stylesheet" type="text/css" href="cdm.css?<?php echo $dt_add ?>"/>
+
+       <?php print_theme_includes($link) ?>
+       <?php print_user_stylesheet($link) ?>
+
+       <link rel="shortcut icon" type="image/png" href="images/favicon.png"/>
+
+       <script type="text/javascript" src="lib/prototype.js"></script>
+       <script type="text/javascript" src="lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"></script>
+       <script type="text/javascript" src="lib/dojo/dojo.js"></script>
+       <script type="text/javascript" src="lib/dijit/dijit.js"></script>
+       <script type="text/javascript" src="lib/dojo/tt-rss-layer.js"></script>
+
+       <script type="text/javascript" charset="utf-8" src="localized_js.php?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/tt-rss.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/functions.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/feedlist.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/viewfeed.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="errors.php?mode=js"></script>
+
+       <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+       <script type="text/javascript">
+               Event.observe(window, 'load', function() {
+                       init();
+               });
+       </script>
+</head>
+
+<body id="ttrssMain" class="claro">
+
+<div id="overlay" style="display : block">
+       <div id="overlay_inner">
+               <div class="insensitive"><?php echo __("Loading, please wait...") ?></div>
+               <div dojoType="dijit.ProgressBar" places="0" style="width : 300px" id="loading_bar"
+            progress="0" maximum="100">
+               </div>
+               <noscript><br/><?php print_error('Javascript is disabled. Please enable it.') ?></noscript>
+       </div>
+</div>
+
+<div id="header">
+       <?php if (!SINGLE_USER_MODE) { ?>
+                       <?php echo __('Hello,') ?> <b><?php echo $_SESSION["name"] ?></b> |
+       <?php } ?>
+       <a href="prefs.php"><?php echo __('Preferences') ?></a>
+
+       <?php if (defined('FEEDBACK_URL') && FEEDBACK_URL) { ?>
+               | <a target="_blank" class="feedback" href="<?php echo FEEDBACK_URL ?>">
+                               <?php echo __('Comments?') ?></a>
+       <?php } ?>
+
+       <?php if (!SINGLE_USER_MODE) { ?>
+                       | <a href="backend.php?op=logout"><?php echo __('Logout') ?></a>
+       <?php } ?>
+
+       <img id="newVersionIcon" style="display:none" onclick="newVersionDlg()"
+               width="13" height="13"
+               src="<?php echo theme_image($link, 'images/new_version.png') ?>"
+               title="<?php echo __('New version of Tiny Tiny RSS is available!') ?>"
+               alt="new_version_icon"/>
+</div>
+
+<div id="hotkey_help_overlay" style="display : none" onclick="Element.hide(this)">
+       <?php include "help/3.php" ?>
+</div>
+
+<div id="notify" class="notify"><span id="notify_body">&nbsp;</span></div>
+<div id="cmdline" style="display : none"></div>
+<div id="auxDlg" style="display : none"></div>
+<div id="headlines-tmp" style="display : none"></div>
+
+<div id="main" dojoType="dijit.layout.BorderContainer">
+
+<div id="feeds-holder" dojoType="dijit.layout.ContentPane" region="leading" style="width : 20%" splitter="true">
+       <div id="feedlistLoading">
+               <img src='images/indicator_tiny.gif'/>
+               <?php echo  __("Loading, please wait..."); ?></div>
+       <div id="feedTree"></div>
+</div>
+
+<div dojoType="dijit.layout.BorderContainer" region="center" id="header-wrap" gutters="false">
+<div dojoType="dijit.layout.TabContainer" region="center" id="content-tabs">
+<div dojoType="dijit.layout.BorderContainer" region="center" id="content-wrap"
+       title="<?php echo __("News") ?>">
+
+<div id="toolbar" dojoType="dijit.layout.ContentPane" region="top">
+       <div id="main-toolbar" dojoType="dijit.Toolbar">
+
+               <form id="main_toolbar_form" action="" onsubmit='return false'>
+
+               <button dojoType="dijit.form.Button" id="collapse_feeds_btn"
+                       onclick="collapse_feedlist()"
+                       title="<?php echo __('Collapse feedlist') ?>" style="display : inline">
+                       &lt;&lt;</button>
+
+               <select name="view_mode" title="<?php echo __('Show articles') ?>"
+                       onchange="viewModeChanged()"
+                       dojoType="dijit.form.Select">
+                       <option selected="selected" value="adaptive"><?php echo __('Adaptive') ?></option>
+                       <option value="all_articles"><?php echo __('All Articles') ?></option>
+                       <option value="marked"><?php echo __('Starred') ?></option>
+                       <option value="published"><?php echo __('Published') ?></option>
+                       <option value="unread"><?php echo __('Unread') ?></option>
+                       <!-- <option value="noscores"><?php echo __('Ignore Scoring') ?></option> -->
+                       <option value="updated"><?php echo __('Updated') ?></option>
+               </select>
+
+               <select title="<?php echo __('Sort articles') ?>"
+                       onchange="viewModeChanged()"
+                       dojoType="dijit.form.Select" name="order_by">
+                       <option selected="selected" value="default"><?php echo __('Default') ?></option>
+                       <option value="date"><?php echo __('Date') ?></option>
+                       <option value="title"><?php echo __('Title') ?></option>
+                       <option value="score"><?php echo __('Score') ?></option>
+               </select>
+
+               <button dojoType="dijit.form.Button" name="update"
+                       onclick="scheduleFeedUpdate()">
+                       <?php echo __('Update') ?></button>
+
+               <button dojoType="dijit.form.Button"
+                       onclick="catchupCurrentFeed()">
+                       <?php echo __('Mark as read') ?></button>
+
+               </form>
+
+               <div class="actionChooser">
+                       <div dojoType="dijit.form.DropDownButton">
+                               <span><?php echo __('Actions...') ?></span>
+                               <div dojoType="dijit.Menu" style="display: none">
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcSearch')"><?php echo __('Search...') ?></div>
+                                       <div dojoType="dijit.MenuItem" disabled="1"><?php echo __('Feed actions:') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAddFeed')"><?php echo __('Subscribe to feed...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcEditFeed')"><?php echo __('Edit this feed...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcRescoreFeed')"><?php echo __('Rescore feed') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcRemoveFeed')"><?php echo __('Unsubscribe') ?></div>
+                                       <div dojoType="dijit.MenuItem" disabled="1"><?php echo __('All feeds:') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcCatchupAll')"><?php echo __('Mark as read') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcShowOnlyUnread')"><?php echo __('(Un)hide read feeds') ?></div>
+                                       <div dojoType="dijit.MenuItem" disabled="1"><?php echo __('Other actions:') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcDigest')"><?php echo __('Switch to digest...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcTagCloud')"><?php echo __('Show tag cloud...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcTagSelect')"><?php echo __('Select by tags...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAddLabel')"><?php echo __('Create label...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAddFilter')"><?php echo __('Create filter...') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcHKhelp')"><?php echo __('Keyboard shortcuts help') ?></div>
+                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAbout')"><?php echo __('About...') ?></div>
+                               </div>
+                       </div>
+               </div>
+       </div> <!-- toolbar -->
+</div> <!-- toolbar pane -->
+
+       <div id="headlines-wrap-inner" dojoType="dijit.layout.BorderContainer" region="center">
+
+               <div id="headlines-toolbar" dojoType="dijit.layout.ContentPane" region="top">
+               </div>
+
+               <div id="headlines-frame" dojoType="dijit.layout.ContentPane"
+                               onscroll="headlines_scroll_handler(this)" region="center">
+                       <div id="headlinesInnerContainer">
+                               <div class="whiteBox"><?php echo __('Loading, please wait...') ?></div>
+                       </div>
+               </div>
+
+               <?php if (!get_pref($link, 'COMBINED_DISPLAY_MODE')) { ?>
+               <div id="content-insert" dojoType="dijit.layout.ContentPane" region="bottom"
+                       style="height : 50%" splitter="true"></div>
+               <?php } ?>
+
+       </div>
+</div>
+</div>
+</div>
+</div>
+
+<?php db_close($link); ?>
+
+</body>
+</html>
diff --git a/js/FeedTree.js b/js/FeedTree.js
new file mode 100644 (file)
index 0000000..b5b7571
--- /dev/null
@@ -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 = "&alpha;";
+                       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(/&quot;/g, "\"");
+               name = name.replace(/&amp;/g, "&");
+               name = name.replace(/&mdash;/g, "-");
+               name = name.replace(/&lt;/g, "<");
+               name = name.replace(/&gt;/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 (file)
index 0000000..4ea4866
--- /dev/null
@@ -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 (file)
index 0000000..a4cf3da
--- /dev/null
@@ -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 = "<span class=\"labelFixedLength\">" + label + 
+                               "</span>" + 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 (file)
index 0000000..05a0c15
--- /dev/null
@@ -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 = '&alpha;';
+                       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 (file)
index 0000000..1d04a1a
--- /dev/null
@@ -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 (file)
index 0000000..7dba6d3
--- /dev/null
@@ -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 += "<a href=\"#\" onclick=\"viewfeed('" +
+                                                       //              article.tags[i] + "')\">" +
+                                                       //      article.tags[i] + "</a>, ";
+
+                                                       tags_part += article.tags[i] + ", ";
+                                               }
+
+                                               tags_part = tags_part.replace(/, $/, "");
+                                               tags_part = "<span class=\"tags\">" + tags_part + "</span>";
+
+                                       }
+
+                                       if (article.marked)
+                                               mark_part = "<img title='"+ __("Unstar article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_set.png'>";
+                                       else
+                                               mark_part =     "<img title='"+__("Star article")+"' onclick=\"toggle_mark(this, "+article.id+")\" src='images/mark_unset.png'>";
+
+                                       if (article.published)
+                                               publ_part = "<img title='"+__("Unpublish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_set.png'>";
+                                       else
+                                               publ_part =     "<img title='"+__("Publish article")+"' onclick=\"toggle_pub(this, "+article.id+")\" src='images/pub_unset.png'>";
+
+                                       var tmp = "<div id=\"toolbar\">" +
+                                               "<a target=\"_blank\" href=\""+article.url+"\">" + __("Original article") + "</a>" +
+                                               "<div style=\"float : right\"><a href=\"#\" onclick=\"close_article()\">" +
+                                               __("Close this panel") + "</a></div></div>" +
+                                               "<div id=\"inner\">" +
+                                               "<div id=\"ops\">" +
+                                               mark_part +
+                                               publ_part +
+                                               "</div>" +
+                                               "<h1>" + article.title + "</h1>" +
+                                               "<div id=\"tags\">" +
+                                               tags_part +
+                                               "</div>" +
+                                               article.content + "</div>";
+
+                                       $("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 = "<img src='" + get_feed_icon(feed) + "'/>";
+
+               var tmp_html = "<li id=\"F-"+feed.id+"\" onclick=\"viewfeed("+feed.id+")\">" +
+                       icon_part + feed.title +
+                       "<div class='unread-ctr'>" + "<span class=\"unread\">" + feed.unread + "</span>" +
+                       "</div>" + "</li>";
+
+               $("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 = "<img class='icon' src='" + get_feed_icon(feed) + "'/>";
+
+
+               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";
+
+               //"<img title='" + __("Share on Twitter") + "' onclick=\"tweet_article("+article.id+", true)\" src='images/art-tweet.png'>" +
+
+               //"<img title='" + __("Mark as read") + "' onclick=\"view("+article.id+", true)\" src='images/digest_checkbox.png'>" +
+
+               var checkbox_part = "<input type=\"checkbox\" class=\"cb\" onclick=\"toggle_select_article(this)\"/>";
+
+               var date = new Date(article.updated * 1000);
+
+               var date_part = date.toString().substring(0,21);
+
+               var tmp_html = "<li id=\"A-"+article.id+"\" "+style+" class=\""+li_class+"\">" +
+                       checkbox_part +
+                       icon_part +
+                       "<a target=\"_blank\" href=\""+article.link+"\""+
+                               "onclick=\"return view("+article.id+")\" class='title'>" +
+                               article.title + "</a>" +
+                       "<div class='body'>" +
+                       "<div onclick=\"view("+article.id+")\" class='excerpt'>" +
+                               article.excerpt + "</div>" +
+                       "<div class='info'>";
+
+/*             tmp_html += "<a href=\#\" onclick=\"viewfeed("+feed.id+")\">" +
+                                       feed.title + "</a> " + " @ "; */
+
+               tmp_html += date_part + "</div>" +
+                       "</div></li>";
+
+               $("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 += "<li id='F-MORE-PROMPT'>" +
+                               "<img src='images/blank_icon.gif'>" +
+                               "<a href=\"#\" onclick=\"expand_feeds()\">" +
+                               __("%d more...").replace("%d", feeds.length-10) +
+                               "</a>" + "</li>";
+               }
+
+               if (feeds.length == 0) {
+                       $('feeds-content').innerHTML =
+                               "<div class='insensitive' style='text-align : center'>" +
+                                       __("No unread feeds.") + "</div>";
+               }
+
+               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 += "<li id='H-MORE-PROMPT'>" +
+                                               "<div class='body'>" +
+                                               "<a href=\"#\" onclick=\"catchup_visible_articles()\">" +
+                                               __("Mark as read") + "</a> | " +
+                                               "<a href=\"javascript:load_more()\">" +
+                                               __("Load more...") + "</a>" +
+                                               "<img style=\"display : none\" "+
+                                               "id=\"H-LOADING-IMG\" src='images/indicator_tiny.gif'>" +
+                                               "</div></li>";
+                               }
+                       } 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 + "&note=" + param_escape(note);
+               } else {
+                       query = query + "&note=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 (file)
index 0000000..62c44b4
--- /dev/null
@@ -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 = "<a href='#' onclick='viewCurrentFeed()'>" +
+                       __("New articles available in this feed (click to show)") + "</a>";
+
+               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 (file)
index 0000000..02fbadf
--- /dev/null
@@ -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 = "<div class=\"fatalError\">" +
+                       "<pre>" + msg + "</pre>";
+
+               content += "<form name=\"exceptionForm\" id=\"exceptionForm\" target=\"_blank\" "+
+                 "action=\"http://tt-rss.org/report.php\" method=\"POST\">";
+
+               content += "<textarea style=\"display : none\" name=\"message\">" + msg + "</textarea>";
+               content += "<textarea style=\"display : none\" name=\"params\">N/A</textarea>";
+
+               if (ext_info) {
+                       content += "<div><b>Additional information:</b></div>" +
+                       "<textarea name=\"xinfo\" readonly=\"1\">" + ext_info + "</textarea>";
+               }
+
+               content += "<div><b>Stack trace:</b></div>" +
+                       "<textarea name=\"stack\" readonly=\"1\">" + e.stack + "</textarea>";
+
+               content += "</form>";
+
+               content += "</div>";
+
+               content += "<div class='dlgButtons'>";
+
+               content += "<button dojoType=\"dijit.form.Button\""+
+                               "onclick=\"dijit.byId('exceptionDlg').report()\">" +
+                               __('Report to tt-rss.org') + "</button> ";
+               content += "<button dojoType=\"dijit.form.Button\" "+
+                               "onclick=\"dijit.byId('exceptionDlg').hide()\">" +
+                               __('Close') + "</button>";
+               content += "</div>";
+
+               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 = "<img src='"+getInitParam("sign_progress")+"'> " + msg;
+       } else if (n_type == 3) {
+               n.className = "notifyError";
+               msg = "<img src='"+getInitParam("sign_excl")+"'> " + msg;
+       } else if (n_type == 4) {
+               n.className = "notifyInfo";
+               msg = "<img src='"+getInitParam("sign_info")+"'> " + msg;
+       }
+
+//     msg = "<img src='images/live_com_loading.gif'> " + 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<x.length;j++) {
+        // call isNumeric recursively for each character
+        number=isNumeric(x.substring(j,j+1));
+        if(!number) return number;
+      }
+      return number;
+    }
+    else {
+      // if x is number return true
+      if(numbers.indexOf(x)>=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) + "&param=" + 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 = "<div><b>Error code:</b> " + code + "</div>" +
+                               "<p>" + msg + "</p>";
+
+                       if (ext_info) {
+                               content = content + "<div><b>Additional information:</b></div>" +
+                                       "<textarea style='width: 100%' readonly=\"1\">" +
+                                       ext_info + "</textarea>";
+                       }
+
+                       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 += "&hellip;";
+       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&reg_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(/\&amp;key=.*$/,
+                                                               "&amp;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 (file)
index 0000000..e40d6d7
--- /dev/null
@@ -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&reg_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 (file)
index 0000000..5890919
--- /dev/null
@@ -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 = "<div id=\"feedlistLoading\">" +
+//                     __("Loading, please wait...") + "</div>";
+
+               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&param=" +
+               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("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
+                       return;
+               }
+
+               if (k == "daemon_stamp_ok" && v != 1) {
+                       notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", 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 = "&lt;&lt;";
+               } else {
+                       Element.hide('feeds-holder');
+                       Element.hide('feeds-holder_splitter');
+                       $("collapse_feeds_btn").innerHTML = "&gt;&gt;";
+               }
+
+               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 (file)
index 0000000..9cb9023
--- /dev/null
@@ -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 + "<div id='headlines-spacer'></div>";
+                               }
+
+                               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', "<div class='whiteBox'>" +
+                                       __('Could not update headlines (invalid object received - see error console for details)') +
+                                       "</div>");
+               }
+
+               _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("<div class='whiteBox'>" +
+                                       __('Could not display article (invalid object received - see error console for details)') + "</div>");
+               }
+
+               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 + "&note=" + param_escape(note);
+               } else {
+                       query = query + "&note=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&param=" + 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 = "<img src='images/indicator_tiny.gif'> " +
+                                               __("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 = "<div class=\"insensitive\">" +
+                                       __("Loading, please wait...") + "</div>";
+
+                               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&param=" + 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&param=" + 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&param=" + 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);
+       }
+}
+
+
index e51e973176868023c94b264554c3e958065d6584..1cf38e573128127dca59b2bd42de5f03ee797c6d 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        define('DISABLE_SESSIONS', true);
 
        require "functions.php";
diff --git a/localized_schema.php b/localized_schema.php
deleted file mode 100644 (file)
index 29663f3..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php # This file has been generated at:  Wed Nov 23 10:40:20 MSK 2011
-
-__("Title");
-__("Title or Content");
-__("Link");
-__("Content");
-__("Article Date");
-
-__("Delete article");
-__("Mark as read");
-__("Set starred");
-__("Publish article");
-__("Assign tags");
-__("Assign label");
-
-__('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.');
-__('Display expanded list of feed articles, instead of separate displays for headlines and article content');
-__('When "Mark as read" button is clicked in toolbar, automatically open next feed with unread articles.');
-__('This option enables sending daily digest of new (and unread) headlines on your configured e-mail address');
-__('This option enables marking articles as read automatically while you scroll article list.');
-__('Strip all but most common HTML tags when reading articles.');
-__('When auto-detecting tags in articles these tags will not be applied (comma-separated list).');
-__('When this option is enabled, headlines in Special feeds and Labels are grouped by feeds');
-__('Use feed-specified date to sort headlines instead of local import date.');
-__('Customize CSS stylesheet to your liking');
-__('Click to register your SSL client certificate with tt-rss');
-__('Purge old posts after this number of days (0 - disables)');
-__('Default interval between feed updates');
-__('Amount of articles to display at once');
-__('Allow duplicate posts');
-__('Enable feed categories');
-__('Show content preview in headlines list');
-__('Short date format');
-__('Long date format');
-__('Combined feed display');
-__('Hide feeds with no unread messages');
-__('On catchup show next feed');
-__('Sort feeds by unread articles count');
-__('Reverse headline order (oldest first)');
-__('Enable e-mail digest');
-__('Confirm marking feed as read');
-__('Automatically mark articles as read');
-__('Strip unsafe tags from articles');
-__('Blacklisted tags');
-__('Maximum age of fresh articles (in hours)');
-__('Mark articles in e-mail digest as read');
-__('Automatically expand articles in combined mode');
-__('Purge unread articles');
-__('Show special feeds when hiding read feeds');
-__('Group headlines in virtual feeds');
-__('Do not show images in articles');
-__('Enable external API');
-__('User timezone');
-__('Sort headlines by feed date');
-__('Customize stylesheet');
-__('Login with an SSL certificate');
-?>
diff --git a/login_form.php b/login_form.php
deleted file mode 100644 (file)
index 04ca3e6..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-<html>
-<head>
-       <title>Tiny Tiny RSS : Login</title>
-       <link rel="stylesheet" type="text/css" href="lib/dijit/themes/claro/claro.css"/>
-       <link rel="stylesheet" type="text/css" href="tt-rss.css">
-       <link rel="shortcut icon" type="image/png" href="images/favicon.png">
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-       <script type="text/javascript" src="lib/dojo/dojo.js" djConfig="parseOnLoad: true"></script>
-       <script type="text/javascript" src="lib/prototype.js"></script>
-       <script type="text/javascript" src="lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"></script>
-       <script type="text/javascript" src="functions.js"></script>
-       <script type="text/javascript" charset="utf-8" src="errors.php?mode=js"></script>
-</head>
-
-<body id="ttrssLogin" class="claro">
-
-<script type="text/javascript">
-function init() {
-
-       dojo.require("dijit.Dialog");
-
-       var test = setCookie("ttrss_test", "TEST");
-
-       if (getCookie("ttrss_test") != "TEST") {
-               return fatalError(2);
-       }
-
-       var limit_set = getCookie("ttrss_bwlimit");
-
-       if (limit_set == "true") {
-               document.forms["loginForm"].bw_limit.checked = true;
-       }
-
-       document.forms["loginForm"].login.focus();
-}
-
-function fetchProfiles() {
-       try {
-               var params = Form.serialize('loginForm');
-               var query = "?op=getProfiles&" + params;
-
-               if (query) {
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                                       onComplete: function(transport) {
-                                               if (transport.responseText.match("select")) {
-                                                       $('profile_box').innerHTML = transport.responseText;
-                                               }
-                               } });
-               }
-
-       } catch (e) {
-               exception_error("fetchProfiles", e);
-       }
-}
-
-
-function languageChange(elem) {
-       try {
-               document.forms['loginForm']['click'].disabled = true;
-
-               var lang = elem[elem.selectedIndex].value;
-               setCookie("ttrss_lang", lang, <?php print SESSION_COOKIE_LIFETIME ?>);
-               window.location.reload();
-       } catch (e) {
-               exception_error("languageChange", e);
-       }
-}
-
-function gotoRegForm() {
-       window.location.href = "register.php";
-       return false;
-}
-
-function bwLimitChange(elem) {
-       try {
-               var limit_set = elem.checked;
-
-               setCookie("ttrss_bwlimit", limit_set,
-                       <?php print SESSION_COOKIE_LIFETIME ?>);
-
-       } catch (e) {
-               exception_error("bwLimitChange", e);
-       }
-}
-
-function validateLoginForm(f) {
-       try {
-
-               if (f.login.value.length == 0) {
-                       new Effect.Highlight(f.login);
-                       return false;
-               }
-
-               if (f.password.value.length == 0) {
-                       new Effect.Highlight(f.password);
-                       return false;
-               }
-
-               document.forms['loginForm']['click'].disabled = true;
-
-               return true;
-       } catch (e) {
-               exception_error("validateLoginForm", e);
-               return true;
-       }
-}
-</script>
-
-<script type="text/javascript">
-       Event.observe(window, 'load', function() {
-               init();
-       });
-</script>
-
-<form action="" method="POST" id="loginForm" name="loginForm" onsubmit="return validateLoginForm(this)">
-<input type="hidden" name="login_action" value="do_login">
-
-<table class="loginForm2">
-<tr>
-       <td class="loginTop" valign="bottom" align="left">
-               <img src="images/logo_wide.png">
-       </td>
-</tr><tr>
-       <td align="center" valign="middle" class="loginMiddle" height="100%">
-               <?php if ($_SESSION['login_error_msg']) { ?>
-                       <div class="loginError"><?php echo $_SESSION['login_error_msg'] ?></div>
-                       <?php $_SESSION['login_error_msg'] = ""; ?>
-               <?php } ?>
-               <table>
-                       <tr><td align="right"><?php echo __("Login:") ?></td>
-                       <td align="right"><input name="login"
-                               onchange="fetchProfiles()" onfocus="fetchProfiles()"
-                               value="<?php echo get_remote_user($link) ?>"></td></tr>
-                       <tr><td align="right"><?php echo __("Password:") ?></td>
-                       <td align="right"><input type="password" name="password"
-                               onchange="fetchProfiles()" onfocus="fetchProfiles()"
-                               value="<?php echo get_remote_fakepass($link) ?>"></td></tr>
-                       <tr><td align="right"><?php echo __("Language:") ?></td>
-                       <td align="right">
-                       <?php
-                               print_select_hash("language", $_COOKIE["ttrss_lang"], get_translations(),
-                                       "style='width : 100%' onchange='languageChange(this)'");
-
-                       ?>
-                       </td></tr>
-
-                       <tr><td align="right"><?php echo __("Profile:") ?></td>
-                       <td align="right" id="profile_box">
-                       <select style='width : 100%' disabled='disabled'>
-                               <option><?php echo __("Default profile") ?></option></select>
-                       </td></tr>
-
-                       <!-- <tr><td colspan="2">
-                               <input type="checkbox" name="remember_me" id="remember_me">
-                               <label for="remember_me">Remember me on this computer</label>
-                       </td></tr> -->
-
-                       <tr><td colspan="2" align="right" class="innerLoginCell">
-
-                       <button type="submit" name='click'><?php echo __('Log in') ?></button>
-                       <?php if (defined('ENABLE_REGISTRATION') && ENABLE_REGISTRATION) { ?>
-                               <button onclick="return gotoRegForm()">
-                                       <?php echo __("Create new account") ?></button>
-                       <?php } ?>
-
-                               <input type="hidden" name="action" value="login">
-                               <input type="hidden" name="rt"
-                                       value="<?php if ($return_to != 'none') { echo $return_to; } ?>">
-                       </td></tr>
-
-                       <tr><td colspan="2" align="right" class="innerLoginCell">
-
-                       <div class="small">
-                       <input name="bw_limit" id="bw_limit" type="checkbox"
-                               onchange="bwLimitChange(this)">
-                       <label for="bw_limit">
-                       <?php echo __("Use less traffic") ?></label></div>
-
-                       </td></tr>
-
-
-               </table>
-       </td>
-</tr><tr>
-       <td align="center" class="loginBottom">
-       <a href="http://tt-rss.org/">Tiny Tiny RSS</a>
-       <?php if (!defined('HIDE_VERSION')) { ?>
-                v<?php echo VERSION ?>
-       <?php } ?>
-       &copy; 2005&ndash;<?php echo date('Y') ?> <a href="http://fakecake.org/">Andrew Dolgov</a>
-       </td>
-</tr>
-
-</table>
-
-</form>
-
-</body></html>
diff --git a/messages.pot b/messages.pot
deleted file mode 100644 (file)
index 5237e0c..0000000
+++ /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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\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 (<b>%d</b> to "
-"<b>%d</b>)."
-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 <b>%d</b> update(s) up to schema\n"
-"\t\t\tversion <b>%d</b>."
-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: <b>%d</b>, required: <b>%d</b>."
-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. &lt;a href='db-updater.php'&gt;Please "
-"update&lt;/a&gt;."
-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 "<li>Adding category <b>%s</b>.</li>"
-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. <a target=\"_blank\" class=\"visibleLink"
-"\" href=\"%s\">This file</a> 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 ""
-"<b>Hint:</b> 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 <b>%s</b>."
-msgstr ""
-
-#: modules/pref-feeds.php:1030
-#, php-format
-msgid "Could not subscribe to <b>%s</b>."
-msgstr ""
-
-#: modules/pref-feeds.php:1033
-#, php-format
-msgid "No feeds found in <b>%s</b>."
-msgstr ""
-
-#: modules/pref-feeds.php:1036
-#, php-format
-msgid "Already subscribed to <b>%s</b>."
-msgstr ""
-
-#: modules/pref-feeds.php:1044
-#, php-format
-msgid "Could not subscribe to <b>%s</b>.<br>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 <b>$%s</b> 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 "<b>Hint:</b> 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 <b>%s</b>"
-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 <b>%s</b>"
-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 <b>%s</b>."
-msgstr ""
-
-#: modules/pref-users.php:254
-#, php-format
-msgid "Added user <b>%s</b> with password <b>%s</b>"
-msgstr ""
-
-#: modules/pref-users.php:261
-#, php-format
-msgid "Could not create user <b>%s</b>"
-msgstr ""
-
-#: modules/pref-users.php:265
-#, php-format
-msgid "User <b>%s</b> already exists."
-msgstr ""
-
-#: modules/pref-users.php:285
-#, php-format
-msgid ""
-"Changed password of user <b>%s</b>\n"
-"\t\t\t\t\t to <b>%s</b>"
-msgstr ""
-
-#: modules/pref-users.php:289
-#, php-format
-msgid "Notifying <b>%s</b>."
-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 ""
-"<b>Note:</b> 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 ""
index cbdad84da329dc969ea4f16faae88105f3cee8e8..345767fbda26c82924bd2f4b9ef400b1dd5761de 100644 (file)
@@ -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);
 
index b75a4c046cb2b156c5bb5c574a129ac0125cc267..74d488e1fecf49f191ac3a3c28d81a2908397587 100644 (file)
@@ -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 (file)
index 5e1ba80..0000000
+++ /dev/null
@@ -1,789 +0,0 @@
-<?php
-       define('TTRSS_SESSION_NAME', 'ttrss_m_sid');
-
-       function render_feeds_list($link) {
-
-               $tags = $_GET["tags"];
-
-               print "<div id=\"heading\">";
-
-               if ($tags) {
-                       print __("Tags")."<span id=\"headingAddon\">
-                               (<a href=\"index.php\">".__("View feeds")."</a>, ";
-               } else {
-                       print __("Feeds")." <span id=\"headingAddon\">
-                               (<a href=\"index.php?tags=1\">".__("View tags")."</a>, ";
-               }
-
-               print "<a href=\"index.php?go=sform\">".__("Search")."</a>, ";
-
-               print "<a href=\"logout.php\">".__("Logout")."</a>)</span>";
-               print "</div>";
-
-               print "<ul class=\"feedList\">";
-
-               $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 "<li class=\"feedCat\">
-                                       <a href=\"?subop=tc&id=-1\">$tmp_category</a>$ellipsis
-                                               </li>";
-
-                               print "<li class=\"$holder_class\"><ul class=\"feedCatList\">";
-                       }
-
-                       foreach (array(-4, -3, -1, -2, 0) as $i) {
-                               printMobileFeedEntry($i, "virt", false, false, 
-                                       false, $link);
-                       }
-
-                       if (get_pref($link, 'ENABLE_FEED_CATS')) {
-                               print "</ul>";
-                       }
-
-       
-                               $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 "<li class=\"feedCat\">
-                                                       <a href=\"?subop=tc&id=-2\">$tmp_category</a>$ellipsis
-                                                               </li>";
-
-                                               print "<li class=\"$holder_class\"><ul class=\"feedCatList\">";
-                                       } else {
-//                                             print "<li><hr></li>";
-                                       }
-                               }
-               
-                               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 "</ul>";
-                                       }
-                               }
-
-
-                       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 "</ul></li>";
-                                       }
-                               
-                                       $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 "<li class=\"feedCat\">
-                                               <a href=\"?subop=tc&id=$cat_id\">$tmp_category</a>
-                                                       <a href=\"?go=vf&id=$cat_id&cat=true\">
-                                                               <span class=\"$catctr_class\">($cat_unread)$ellipsis</span>
-                                                       </a></li>";
-
-                                       print "<li id=\"feedCatHolder\" class=\"$holder_class\">
-                                               <ul class=\"feedCatList\">";
-                               }
-       
-                               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 = "<img src=\"$icon_file\">";
-               } else {
-                       $feed_icon = "<img src=\"../../images/blank_icon.gif\">";
-               }
-
-               if ($rtl_content) {
-                       $rtl_tag = "dir=\"rtl\"";
-               } else {
-                       $rtl_tag = "dir=\"ltr\"";
-               }
-
-               $feed = "<a href=\"?go=vf&id=$feed_id\">$feed_title</a>";
-
-               print "<li class=\"$class\">";
-               print "$feed_icon";
-               print "<span $rtl_tag>$feed</span> ";
-
-               if ($unread != 0) {
-                       print "<span $rtl_tag>($unread)</span>";
-               }
-               
-               print "</li>";
-
-       }
-
-       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 "<div id=\"headlines\" $rtl_tag>";
-
-               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 "<div align='center'>".
-                               __("Could not display feed (query failed). Please check label match syntax or local configuration.").
-                               "</div>";
-                       return;
-               }
-
-               print "<div id=\"heading\">";
-               #               if (!$cat_view && file_exists("../icons/$feed.ico") && filesize("../icons/$feed.ico") > 0) {
-                       #                       print "<img class=\"feedIcon\" src=\"../icons/$feed.ico\">";
-                       #               }
-               
-               print "$feed_title <span id=\"headingAddon\">(";
-               print "<a href=\"index.php\">".__("Back")."</a>, ";
-               print "<a href=\"index.php?go=sform&aid=$feed&ic=$cat_view\">".__("Search")."</a>, ";
-               print "<a href=\"index.php?go=vf&id=$feed&subop=ForceUpdate\">".__("Update")."</a>";
-
-#              print "Mark as read: ";
-#              print "<a href=\"index.php?go=vf&id=$feed&subop=MarkAsRead\">Page</a>, ";
-#              print "<a href=\"index.php?go=vf&id=$feed&subop=MarkAllRead\">Feed</a>";
-
-               print ")</span>";
-
-               print "&nbsp;" . __('View:');
-
-               print "<form style=\"display : inline\" method=\"GET\" action=\"index.php\">";
-
-               /* print "<select name=\"viewmode\">
-                       <option selected value=\"adaptive\"> " . __('Adaptive') . "</option>
-                       <option value=\"all_articles\">" . __('All Articles') . "</option>
-                       <option value=\"marked\">" . __('Starred') . "</option>
-                       <option value=\"unread\">" . __('Unread') . "</option>
-                       </select>"; */
-
-               $sel_values = array(
-                       "adaptive" => __("Adaptive"),
-                       "all_articles" => __("All Articles"),
-                       "unread" => __("Unread"),
-                       "marked" => __("Starred"));
-
-               print_select_hash("viewmode", $view_mode, $sel_values);
-
-               print "<input type=\"hidden\" name=\"id\" value=\"$feed\">
-               <input type=\"hidden\" name=\"cat\" value=\"$cat_view\">
-               <input type=\"hidden\" name=\"go\" value=\"vf\">
-               <input type=\"submit\" value=\"".__('Refresh')."\">";
-               print "</form>";
-
-               print "</div>";
-       
-               if (db_num_rows($result) > 0) {
-
-                       print "<form method=\"GET\" action=\"index.php\">";
-                       print "<input type=\"hidden\" name=\"go\" value=\"vf\">";
-                       print "<input type=\"hidden\" name=\"id\" value=\"$feed\">";
-                       print "<input type=\"hidden\" name=\"cat\" value=\"$cat_view\">";
-
-                       print "<ul class=\"headlines\" id=\"headlines\">";
-
-                       $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 = "<img id='FUPDPIC-$id' src=\"images/updated.png\" 
-                                               alt=\"".__("Updated")."\">";
-                               } else {
-                                       $update_pic = "<img id='FUPDPIC-$id' src=\"images/blank_icon.gif\" 
-                                               alt=\"".__("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 = "<img alt=\"S\" class='marked' src=\"../../images/mark_set.png\">";
-                               } else {
-                                       $marked_pic = "<img alt=\"s\" class='marked' src=\"../../images/mark_unset.png\">";
-                               }
-
-                               if ($line["published"] == "t" || $line["published"] == "1") {
-                                       $published_pic = "<img alt=\"P\" class='marked' src=\"../../images/pub_set.gif\">";
-                               } else {
-                                       $published_pic = "<img alt=\"p\" class='marked' src=\"../../images/pub_unset.gif\">";
-                               }
-
-                               $content_link = "<a href=\"?go=view&id=$id&cat=$cat_view&ret_feed=$feed&feed=$feed_id\">" .
-                                       $line["title"] . "</a>";
-
-                               $updated_fmt = make_local_datetime($link, $line['updated'], false);
-
-                               print "<li class='$class' id=\"HROW-$id\">";
-
-                               print "<input type=\"checkbox\" name=\"sel_ids[$id]\"
-                                       id=\"HSCB-$id\" onchange=\"toggleSelectRow(this, $id)\">";
-
-                               print "<a href=\"?go=vf&id=$feed&ts=$id&cat=$cat_view\">$marked_pic</a>";
-                               print "<a href=\"?go=vf&id=$feed&tp=$id&cat=$cat_view\">$published_pic</a>";
-
-                               print $content_link;
-       
-                               if ($line["feed_title"]) {                      
-                                       print " (<a href='?go=vf&id=$feed_id'>".
-                                                       $line["feed_title"]."</a>)";
-                               }
-
-                               print "<span class='hlUpdated'> ($updated_fmt)</span>";
-
-                               print "</li>";
-
-       
-                               ++$lnum;
-                       }
-
-                       print "</ul>";
-
-                       print "<div class='footerAddon'>";
-
-                       $_SESSION["last_page_ids.$feed"] = $page_art_ids;
-
-/*                     print "<a href=\"index.php?go=vf&id=$feed&subop=MarkPageRead\">Page</a>, ";
-                       print "<a href=\"index.php?go=vf&id=$feed&subop=MarkAllRead\">Feed</a></div>"; */
-
-                       print "Select: 
-                               <a href=\"javascript:selectHeadlines(1)\">".__("All")."</a>,
-                               <a href=\"javascript:selectHeadlines(2)\">".__("Unread")."</a>,
-                               <a href=\"javascript:selectHeadlines(3)\">".__("None")."</a>,
-                               <a href=\"javascript:selectHeadlines(4)\">".__("Invert")."</a>";
-
-                       print " ";
-
-                       print "<select name=\"catchup_op\">
-                               <option value=\"selection\">".__("Selection")."</option>
-                               <option value=\"page\">".__("Page")."</option>
-                               <option value=\"feed\">".__("Entire feed")."</option>
-                       </select>
-                       <input type=\"hidden\" name=\"cat\" value=\"$cat_view\">
-                       <input type=\"submit\" value=\"".__("Mark as read")."\">";
-
-                       print "</form>";
-
-               } else {
-                       print "<div align='center'>No articles found.</div>";
-               }
-
-       }
-
-       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 = "<a href=\"$comments_url\">$num_comments comments</a>";
-                       } else {
-                               if ($line["comments"] && $line["link"] != $line["comments"]) {
-                                       $entry_comments = "<a href=\"".$line["comments"]."\">comments</a>";
-                               }                               
-                       }
-
-                       $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 = "<a href=\"?go=vf&id=$tag\">$tag</a>, "; 
-                               $tags_str .= $tag_str;
-                       }
-
-                       $tags_str = preg_replace("/, $/", "", $tags_str);
-
-                       $parsed_updated = date(get_pref($link, 'SHORT_DATE_FORMAT'), 
-                               strtotime($line["updated"]));
-
-                       print "<div id=\"heading\">";
-
-                       #                       if (file_exists("../icons/$feed_id.ico") && filesize("../icons/$feed_id.ico") > 0) {
-                               #                               print "<img class=\"feedIcon\" src=\"../icons/$feed_id.ico\">";
-                               #                       }
-
-                       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 = "<a href=\"index.php?go=vf&id=$feed_id\">$feed_title_native</a>";
-                               $feed_link .= " in <a href=\"index.php?go=vf&id=$ret_feed_id&cat=$cat_view\">
-                                       $feed_title</a>";
-                       } else {
-                               $feed_link = "<a href=\"index.php?go=vf&id=$ret_feed_id\">$feed_title</a>";
-                       }
-
-                       $feedlist = "<a href=\"index.php\">".__('Back to feedlist')."</a>";
-
-                       print "<a href=\"" . $line["link"] . "\">" . 
-                               truncate_string($line["title"], 30) . "</a>";
-                       print " <span id=\"headingAddon\">$parsed_updated ($feed_link, $feedlist)</span>";
-                       print "</div>";
-
-                       if ($num_tags > 0) {
-                               print "<div class=\"postTags\">".__("Tags:")." $tags_str</div>";
-                       }
-
-                       if ($line["marked"] == "t" || $line["marked"] == "1") {
-                               $marked_pic = "<img class='marked' src=\"../../images/mark_set.png\">";
-                       } else {
-                               $marked_pic = "<img class='marked' src=\"../../images/mark_unset.png\">";
-                       }
-
-                       if ($line["published"] == "t" || $line["published"] == "1") {
-                               $published_pic = "<img class='marked' src=\"../../images/pub_set.gif\">";
-                       } else {
-                               $published_pic = "<img class='marked' src=\"../../images/pub_unset.gif\">";
-                       }
-
-
-                       print "<div class=\"postStarOps\">";
-                       print "<a title=\"".__('Toggle starred')."\"href=\"?go=view&id=$id&ret_feed=$ret_feed_id&feed=$feed_id&sop=ts\">$marked_pic</a>";
-                       print "<a title=\"".__('Toggle published')."\" href=\"?go=view&id=$id&ret_feed=$ret_feed_id&feed=$feed_id&sop=tp\">$published_pic</a>";
-                       // Mark unread
-                       print "<a title=\"".__('Mark as unread')."\" href=\"?go=vf&id=$ret_feed_id&feed=$feed_id&sop=mu&aid=$id";
-                       if ($cat_view) { print "&cat=$cat_view"; }
-                       print "\"><img class='marked' src=\"../../images/art-set-unread.png\"></a>";
-                       print "</div>";
-
-                       print sanitize_rss($link, $line["content"], true);; 
-               
-               }
-
-               print "</body></html>";
-       }
-
-       function render_search_form($link, $active_feed_id = false, $is_cat = false) {
-
-               print "<div id=\"heading\">";
-
-               print __("Search")." <span id=\"headingAddon\">
-                               (<a href=\"index.php\">".__("Go back")."</a>)</span></div>";
-
-               print "<form method=\"GET\" action=\"index.php\" class=\"searchForm\">";
-
-               print "<input type=\"hidden\" name=\"go\" value=\"vf\">";
-               print "<input type=\"hidden\" name=\"id\" value=\"$active_feed_id\">";
-               print "<input type=\"hidden\" name=\"cat\" value=\"$is_cat\">";
-
-               print "<table><tr><td>".__('Search:')."</td><td>";
-               print "<input name=\"query\"></td></tr>";
-
-               print "<tr><td>".__('Where:')."</td><td>";
-               
-               print "<select name=\"search_mode\">
-                       <option value=\"all_feeds\">".__('All feeds')."</option>";
-                       
-               $feed_title = getFeedTitle($link, $active_feed_id);
-
-               if (!$is_cat) {
-                       $feed_cat_title = getFeedCatTitle($link, $active_feed_id);
-               } else {
-                       $feed_cat_title = getCategoryTitle($link, $active_feed_id);
-               }
-                       
-               if ($active_feed_id && !$is_cat) {                              
-                       print "<option selected value=\"this_feed\">$feed_title</option>";
-               } else {
-                       print "<option disabled>".__('This feed')."</option>";
-               }
-
-               if ($is_cat) {
-                       $cat_preselected = "selected";
-               }
-
-               if (get_pref($link, 'ENABLE_FEED_CATS') && ($active_feed_id > 0 || $is_cat)) {
-                       print "<option $cat_preselected value=\"this_cat\">$feed_cat_title</option>";
-               } else {
-                       //print "<option disabled>".__('This category')."</option>";
-               }
-
-               print "</select></td></tr>"; 
-
-               print "<tr><td>".__('Match on:')."</td><td>";
-
-               $search_fields = array(
-                       "title" => __("Title"),
-                       "content" => __("Content"),
-                       "both" => __("Title or content"));
-
-               print_select_hash("match_on", 3, $search_fields); 
-                               
-               print "</td></tr></table>";
-
-               print "<input type=\"submit\" value=\"".__('Search')."\">";
-
-               print "</form>";
-
-               print "</div>";
-       }
-
-       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 (file)
index db2215b..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-       error_reporting(E_ERROR | E_WARNING | E_PARSE);
-
-       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";
-
-       $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
-
-       init_connection($link);
-
-       login_sequence($link, 2);
-
-       /* perform various redirect-needing subops */
-
-       $subop = db_escape_string($_GET["subop"]);
-       $go = $_GET["go"];
-
-       if ($subop == "tc" && !$go) {
-
-               $cat_id = db_escape_string($_GET["id"]);
-               toggle_collapse_cat($link, $cat_id);
-
-               header("Location: index.php");
-               return;
-       }
-
-       $ts_id = db_escape_string($_GET["ts"]);
-
-       if ($go == "vf" && $ts_id) {
-
-               toggleMarked($link, $ts_id);
-
-               $query_string = preg_replace("/&ts=[0-9]*/", "", $_SERVER["QUERY_STRING"]);
-               header("Location: index.php?$query_string");
-               return;
-       }
-
-       $tp_id = db_escape_string($_GET["tp"]);
-
-       if ($go == "vf" && $tp_id) {
-
-               togglePublished($link, $tp_id);
-
-               $query_string = preg_replace("/&tp=[0-9]*/", "", $_SERVER["QUERY_STRING"]);
-               header("Location: index.php?$query_string");
-               return;
-       }
-
-       $sop = db_escape_string($_GET["sop"]);
-
-       if ($sop) {
-               if ($go == "view") {
-                       $a_id = db_escape_string($_GET["id"]);
-               } elseif ($go == "vf") {
-                       $a_id = db_escape_string($_GET["aid"]);
-               }
-
-               if ($a_id) {
-
-                       if ($sop == "tp") {
-                               togglePublished($link, $a_id);
-                       }
-
-                       if ($sop == "ts") {
-                               toggleMarked($link, $a_id);
-                       }
-
-                       if ($sop == "mu") {
-                               markUnread($link, $a_id);
-                       }
-
-                       $query_string = str_replace("&sop=$sop", "", $_SERVER["QUERY_STRING"]);
-                       header("Location: index.php?$query_string");
-               }
-       }
-
-?>
-<html>
-<head>
-       <title>Tiny Tiny RSS</title>
-       <link rel="stylesheet" type="text/css" href="mobile.css">
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-       <script type="text/javascript" src="mobile.js"></script>
-       <?php print_user_stylesheet($link) ?>
-</head>
-<body id="ttrssMobile">
-
-<div id="content">
-<?php
-       if (!$go) {
-               render_feeds_list($link);
-       } else if ($go == "vf") {
-               render_headlines($link);
-       } else if ($go == "view") {
-               render_article($link);
-       } else if ($go == "sform") {
-               render_search_form($link, $_GET["aid"], $_GET["ic"]);
-       } else {
-               print __("Internal error: Function not implemented");
-       }
-
-?>
-</div>
-
-</body>
-</html>
diff --git a/mobile/classic/login_form.php b/mobile/classic/login_form.php
deleted file mode 100644 (file)
index bf4f77e..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<html>
-<head>
-       <title>Tiny Tiny RSS : Login</title>
-       <link rel="stylesheet" type="text/css" href="mobile.css">
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-       <script type="text/javascript" charset="utf-8" src="mobile.js"></script>
-</head>
-
-<script type="text/javascript">
-function init() {
-
-       if (arguments.callee.done) return;
-       arguments.callee.done = true;           
-
-       var login = document.forms["loginForm"].login;
-       var click = document.forms["loginForm"].click;
-
-       login.focus();
-       click.disabled = false;
-
-}
-function languageChange(elem) {
-       try {
-               document.forms['loginForm']['click'].disabled = true;
-       
-               var lang = elem[elem.selectedIndex].value;
-               setCookie("ttrss_lang", lang, <?php print SESSION_COOKIE_LIFETIME ?>);
-               window.location.reload();
-       } catch (e) {
-               exception_error("languageChange", e);
-       }
-}
-
-</script>
-
-<script type="text/javascript">
-if (document.addEventListener) {
-       document.addEventListener("DOMContentLoaded", init, null);
-}
-window.onload = init;
-</script>
-
-
-<body>
-
-       <div id="content">
-       <div id="heading">Tiny Tiny RSS</div>
-
-       <form action="index.php" method="POST" name="loginForm">
-       <input type="hidden" name="rt" value="<?php echo $_GET['rt'] ?>">
-       <input type="hidden" name="login_action" value="do_login">
-
-       <?php if ($_SESSION['login_error_msg']) { ?>
-               <div class="loginError"><?php echo $_SESSION['login_error_msg'] ?></div>
-               <?php $_SESSION['login_error_msg'] = ""; ?>
-       <?php } ?>
-
-       <table>
-               <tr><td align='right'><?php echo __("Login:") ?></td><td><input type="text" name="login"></td>
-               <tr><td align='right'><?php echo __("Password:") ?></td><td><input type="password" name="password"></tr>
-
-               <tr><td align="right"><?php echo __("Language:") ?></td>
-               <td>
-                       <?php
-                               print_select_hash("language", $_COOKIE["ttrss_lang"], get_translations(),
-                                       "style='width : 100%' onchange='languageChange(this)'");
-
-                       ?>
-               </td></tr>
-               <tr><td colspan='2'>
-               <input type="submit" class="button" value="<?php echo __('Log in') ?>" name="click">
-               </td></tr>
-               </table>
-       </form>
-       </div>
-
-</body>
-</html>
-
diff --git a/mobile/classic/logout.php b/mobile/classic/logout.php
deleted file mode 100644 (file)
index b20cfc9..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php  
-       require_once "functions.php";
-       require_once "../../sessions.php";
-       require_once "../../functions.php";
-
-       logout_user();
-
-       header("Location: index.php");
-?>
diff --git a/mobile/classic/mobile.css b/mobile/classic/mobile.css
deleted file mode 100644 (file)
index 786639e..0000000
+++ /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 (file)
index 599e007..0000000
+++ /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("<b>EXCEPTION: " + msg + "</b>");
-
-       if (!silent) {
-               alert(msg);
-       }
-}
-
index bd5804cca796af11f698c14f243df93315f44ea7..f7b7443d35efca587cb1fc25058b1055fa296fd2 100644 (file)
@@ -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 (file)
index 1063fca..0000000
+++ /dev/null
@@ -1,538 +0,0 @@
-<?php
-       define('TTRSS_SESSION_NAME', 'ttrss_m_sid');
-
-       /* TODO replace with interface to db-prefs */
-
-       function mobile_pref_toggled($link, $id) {
-               if (get_pref($link, "_MOBILE_$id"))
-                       return "true";
-               else
-                       return "";
-       }
-
-       function mobile_get_pref($link, $id) {
-               //return $_SESSION["mobile-prefs"][$id];
-               return get_pref($link, "_MOBILE_$id");
-       }
-
-       function mobile_set_pref($link, $id, $value) {
-               //$_SESSION["mobile-prefs"][$id] = $value;
-               return set_pref($link, "_MOBILE_$id", $value);
-       }
-
-       function mobile_feed_has_icon($id) {
-               $filename = "../".ICONS_DIR."/$id.ico";
-
-               return file_exists($filename) && filesize($filename) > 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 '<ul id="home" title="'.__('Home').'" selected="true"
-                       myBackLabel="'.__('Logout').'" myBackHref="logout.php" myBackTarget="_self">';
-
-
-       //              print "<li><a href='#cat-actions'>".__('Actions...')."</a></li>";
-
-                       $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 "<li class='$class'><a href='feed.php?id=$id'>" .
-                                               "<img class='tinyIcon' src='$icon_url'/>".
-                                               $line["title"] . "</a></li>";
-                               }
-
-                               ++$num_feeds;
-                       }
-
-/*                     $next_offset = $offset + $num_feeds;
-
-                       print "<li><a href=\"home.php?skip=$next_offset\"
-       target=\"_replace\">Show more feeds...</a></li>"; */
-
-                       if (!$offset) print "</ul>";
-
-       }
-
-       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 "<ul id='cat-$cat_id' title='$title' myBackLabel='".__("Home")."'
-                               myBackHref='home.php'>";
-
-       //              print "<li><a href='#cat-actions'>".__('Actions...')."</a></li>";
-
-                       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 "<li class='$class'><a href='feed.php?id=$id&cat=$cat_id'>" .
-                                               "<img class='tinyIcon' src='$icon_url'/>".
-                                               $line["title"] . "</a></li>";
-                               }
-                       }
-
-                       print "</ul>";
-               } else if ($cat_id == -1) {
-
-                       $title = __('Special');
-
-                       print "<ul id='cat--1' title='$title' myBackLabel='".__("Home")."'
-                               myBackHref='home.php'>";
-
-                       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 "<li class='$class'>
-                                               <a href='feed.php?id=$id&cat=-1'>
-                                               <img class='tinyIcon' src='../$icon'/>$title</a></li>";
-                               }
-                       }
-
-                       print "</ul>";
-               } else if ($cat_id == -2) {
-
-                       $title = __('Labels');
-
-                       print "<ul id='cat--2' title='$title' myBackLabel='".__("Home")."'
-                               myBackHref='home.php'>";
-
-                       $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 "<li class='$class'>
-                                               <a href='feed.php?id=$id&cat=-2'>$title</a></li>";
-                               }
-                       }
-                       print "</ul>";
-               }
-       }
-
-       function render_categories_list($link) {
-               $owner_uid = $_SESSION["uid"];
-
-               $cat_browse = mobile_get_pref($link, "BROWSE_CATS");
-
-               print '<ul id="home" title="'.__('Home').'" selected="true"
-                       myBackLabel="'.__('Logout').'" myBackHref="logout.php" myBackTarget="_self">';
-
-//             print "<li><a href='#searchForm'>Search...</a></li>";
-
-               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 "<li class='$class'><a href='cat.php?id=$id'>$title</a></li>";
-                       else
-                               print "<li class='$class'><a href='feed.php?id=$id&is_cat=true'>$title</a></li>";
-               }
-
-               $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 "<li class='$class'><a href='cat.php?id=$id'>" .
-                                                       $line["title"] . "</a></li>";
-                                       else
-                                               print "<li class='$class'><a href='feed.php?id=$id&is_cat=true'>".
-                                                       $line["title"] . "</a></li>";
-                               }
-                       }
-               }
-
-
-               $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 "<li class='$class'><a href='cat.php?id=0'>$title</a></li>";
-                               else
-                                       print "<li class='$class'><a href='feed.php?id=0&is_cat=true'>$title</a></li>";
-
-                       }
-               }
-
-               print "</ul>";
-       }
-
-       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 "<form id=\"searchForm-$feed_id-$cat_id\" class=\"dialog\" method=\"POST\"
-                               action=\"feed.php\">
-
-                               <input type=\"hidden\" name=\"id\" value=\"$feed_id\">
-                               <input type=\"hidden\" name=\"cat\" value=\"$cat_id\">
-
-               <fieldset>
-                         <h1>Search</h1>
-                   <a class=\"button leftButton\" type=\"cancel\">Cancel</a>
-                   <a class=\"button blueButton\" type=\"submit\">Search</a>
-
-                   <label>Search:</label>
-                                       <input id=\"search\" type=\"text\" name=\"search\"/>
-               </fieldset>
-                         </form>";
-
-                       if ($cat_id) {
-                               $cat_title = getCategoryTitle($link, $cat_id);
-
-                               print "<ul id=\"feed-$feed_id\" title=\"$feed_title\" selected=\"true\"
-                                       myBackLabel='$cat_title' myBackHref='cat.php?id=$cat_id'>";
-                       } else {
-                               print "<ul id=\"feed-$feed_id\" title=\"$feed_title\" selected=\"true\"
-                                       myBackLabel='".__("Home")."' myBackHref='home.php'>";
-                       }
-
-                       print "<li><a href='#searchForm-$feed_id-$cat_id'>Search...</a></li>";
-               }
-
-               $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 "<li class='$class'><a href='article.php?id=$id&feed=$feed_id&cat=$cat_id&is_cat=$is_cat'>
-                               <img class='tinyIcon' src='$icon_url'>";
-                       print $line["title"];
-                       print "</a></li>";
-
-                       ++$num_headlines;
-
-               }
-
-               if ($num_headlines == 0 && $search) {
-                       $articles_url = "feed.php?id=$feed_id&cat=$cat_id&skip=$next_offset";
-
-                       print "<li><a href=\"$articles_url\">" . __("Nothing found (click to reload feed).") . "</a></li>";
-
-               }
-
-//             print "<a target='_replace' href='feed.php?id=$feed_id&cat=$cat_id&skip=0'>Next $limit articles...</a>";
-
-               $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 "<li><a href=\"$articles_url\"
-                               target=\"_replace\">Get more articles...</a></li>";
-               }
-
-               if (!$offset) print "</ul>";
-
-       }
-
-       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 "<div class=\"panel\" id=\"article-$id\" title=\"$title\"
-                               selected=\"true\"
-                               myBackLabel='$feed_title' myBackHref='feed.php?id=$feed_id&cat=$cat_id&is_cat=$is_cat'>";
-
-                       if ($line['feed_id'] != $feed_id) {
-                               $real_feed_title = getFeedTitle($link, $line['feed_id']);
-                               $real_feed_id = $line['feed_id'];
-                               $feed_link = "(<a href=\"feed.php?id=$real_feed_id\">$real_feed_title</a>)";
-                       }
-//                     print "<fieldset>";
-
-                       print "<div style='float : right'>($updated_fmt)</div>";
-
-                       print "<h2><a target='_blank' href='$article_link'>$title</a> $feed_link</h2>";
-
-                       print "<hr>";
-
-/*                     print "<div class=\"row\">";
-                       print "<label id='title'><a target='_blank' href='$article_link'>$title</a></label>";
-                       print "</div>"; */
-
-                       $is_starred = (sql_bool_to_bool($line["marked"])) ? "true" : "false";
-                       $is_published = (sql_bool_to_bool($line["published"])) ? "true" : "false";
-
-                       //print "<div class=\"row\">";
-                       //print "<label id='updated'>Updated:</label>";
-                       //print "<input type='text' enabled='false' name='updated' disabled value='$updated_fmt'/>";
-                       //print "</div>";
-
-//                     print "</fieldset>";
-
-                       $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('/<img[^>]+>/is', '', $content);
-                       }
-
-                       print "<p>$content</p>";
-
-            print "<div class='nav'>
-                    <label>Navigation</label>
-                    <div class='button left' onclick='goPrev($id, $feed_id, this)'>Prev</div>
-                    <div class='button right' onclick='goNext($id, $feed_id, this)'>Next</div>
-                  </div>";
-
-                       print "<fieldset>";
-
-                       print "<div class=\"row\">
-                       <label>Starred</label>
-                       <div class=\"toggle\" onclick=\"toggleMarked($id, this)\" toggled=\"$is_starred\"><span class=\"thumb\"></span><span class=\"toggleOn\">ON</span><span class=\"toggleOff\">OFF</span></div>
-                   </div>";
-
-                       print "<div class=\"row\">
-                       <label>Published</label>
-                       <div class=\"toggle\" onclick=\"togglePublished($id, this)\" toggled=\"$is_published\"><span class=\"thumb\"></span><span class=\"toggleOn\">ON</span><span class=\"toggleOff\">OFF</span></div>
-                   </div>";
-
-                       print "<div class=\"row\">
-                       <label>Unread</label>
-                       <div class=\"toggle\" onclick=\"toggleUnread($id, this)\" toggled=\"$is_unread\"><span class=\"thumb\"></span><span class=\"toggleOn\">ON</span><span class=\"toggleOff\">OFF</span></div>
-                   </div>";
-
-
-                       print "</fieldset>";
-
-                       print "</div>";
-
-               }
-       }
-?>
index 2830f5530bbae1ad32abe84a9cb4519f8c7b84aa..71194da0a9d4d5566fd2488cebda1f1afb821cdb 100644 (file)
@@ -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);
        }
index b0b374497292e798fbb6c62002c6f05492783005..edbf586e05af01133b9ee3d42e87cd69965d2014 100644 (file)
@@ -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);
 
         <a class="button" href="prefs.php">Preferences</a>
     </div>
 
-       <?php    
+       <?php
        $use_cats = mobile_get_pref($link, 'ENABLE_CATS');
        $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);
        }
index b9cadaa5cbbad38a0970ce202932248028692623..46d7ec8ee7483c5926623995de091e495cf00fc1 100644 (file)
@@ -1,7 +1,5 @@
-<?php  
-       require_once "functions.php";
-       require_once "../sessions.php";
-       require_once "../functions.php";
+<?php
+       require_once "mobile-functions.php";
 
        logout_user();
 
diff --git a/mobile/mobile-functions.php b/mobile/mobile-functions.php
new file mode 100644 (file)
index 0000000..1765a65
--- /dev/null
@@ -0,0 +1,548 @@
+<?php
+       set_include_path(get_include_path() . PATH_SEPARATOR .
+               dirname(__FILE__) . PATH_SEPARATOR .
+               dirname(dirname(__FILE__)) . PATH_SEPARATOR .
+               dirname(dirname(__FILE__)) . "/include" );
+
+       require_once "include/functions.php";
+       require_once "include/sessions.php";
+       require_once "include/version.php";
+       require_once "include/db-prefs.php";
+
+       define('TTRSS_SESSION_NAME', 'ttrss_m_sid');
+
+       /* TODO replace with interface to db-prefs */
+
+       function mobile_pref_toggled($link, $id) {
+               if (get_pref($link, "_MOBILE_$id"))
+                       return "true";
+               else
+                       return "";
+       }
+
+       function mobile_get_pref($link, $id) {
+               //return $_SESSION["mobile-prefs"][$id];
+               return get_pref($link, "_MOBILE_$id");
+       }
+
+       function mobile_set_pref($link, $id, $value) {
+               //$_SESSION["mobile-prefs"][$id] = $value;
+               return set_pref($link, "_MOBILE_$id", $value);
+       }
+
+       function mobile_feed_has_icon($id) {
+               $filename = "../".ICONS_DIR."/$id.ico";
+
+               return file_exists($filename) && filesize($filename) > 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 '<ul id="home" title="'.__('Home').'" selected="true"
+                       myBackLabel="'.__('Logout').'" myBackHref="logout.php" myBackTarget="_self">';
+
+
+       //              print "<li><a href='#cat-actions'>".__('Actions...')."</a></li>";
+
+                       $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 "<li class='$class'><a href='feed.php?id=$id'>" .
+                                               "<img class='tinyIcon' src='$icon_url'/>".
+                                               $line["title"] . "</a></li>";
+                               }
+
+                               ++$num_feeds;
+                       }
+
+/*                     $next_offset = $offset + $num_feeds;
+
+                       print "<li><a href=\"home.php?skip=$next_offset\"
+       target=\"_replace\">Show more feeds...</a></li>"; */
+
+                       if (!$offset) print "</ul>";
+
+       }
+
+       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 "<ul id='cat-$cat_id' title='$title' myBackLabel='".__("Home")."'
+                               myBackHref='home.php'>";
+
+       //              print "<li><a href='#cat-actions'>".__('Actions...')."</a></li>";
+
+                       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 "<li class='$class'><a href='feed.php?id=$id&cat=$cat_id'>" .
+                                               "<img class='tinyIcon' src='$icon_url'/>".
+                                               $line["title"] . "</a></li>";
+                               }
+                       }
+
+                       print "</ul>";
+               } else if ($cat_id == -1) {
+
+                       $title = __('Special');
+
+                       print "<ul id='cat--1' title='$title' myBackLabel='".__("Home")."'
+                               myBackHref='home.php'>";
+
+                       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 "<li class='$class'>
+                                               <a href='feed.php?id=$id&cat=-1'>
+                                               <img class='tinyIcon' src='../$icon'/>$title</a></li>";
+                               }
+                       }
+
+                       print "</ul>";
+               } else if ($cat_id == -2) {
+
+                       $title = __('Labels');
+
+                       print "<ul id='cat--2' title='$title' myBackLabel='".__("Home")."'
+                               myBackHref='home.php'>";
+
+                       $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 "<li class='$class'>
+                                               <a href='feed.php?id=$id&cat=-2'>$title</a></li>";
+                               }
+                       }
+                       print "</ul>";
+               }
+       }
+
+       function render_categories_list($link) {
+               $owner_uid = $_SESSION["uid"];
+
+               $cat_browse = mobile_get_pref($link, "BROWSE_CATS");
+
+               print '<ul id="home" title="'.__('Home').'" selected="true"
+                       myBackLabel="'.__('Logout').'" myBackHref="logout.php" myBackTarget="_self">';
+
+//             print "<li><a href='#searchForm'>Search...</a></li>";
+
+               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 "<li class='$class'><a href='cat.php?id=$id'>$title</a></li>";
+                       else
+                               print "<li class='$class'><a href='feed.php?id=$id&is_cat=true'>$title</a></li>";
+               }
+
+               $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 "<li class='$class'><a href='cat.php?id=$id'>" .
+                                                       $line["title"] . "</a></li>";
+                                       else
+                                               print "<li class='$class'><a href='feed.php?id=$id&is_cat=true'>".
+                                                       $line["title"] . "</a></li>";
+                               }
+                       }
+               }
+
+
+               $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 "<li class='$class'><a href='cat.php?id=0'>$title</a></li>";
+                               else
+                                       print "<li class='$class'><a href='feed.php?id=0&is_cat=true'>$title</a></li>";
+
+                       }
+               }
+
+               print "</ul>";
+       }
+
+       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 "<form id=\"searchForm-$feed_id-$cat_id\" class=\"dialog\" method=\"POST\"
+                               action=\"feed.php\">
+
+                               <input type=\"hidden\" name=\"id\" value=\"$feed_id\">
+                               <input type=\"hidden\" name=\"cat\" value=\"$cat_id\">
+
+               <fieldset>
+                         <h1>Search</h1>
+                   <a class=\"button leftButton\" type=\"cancel\">Cancel</a>
+                   <a class=\"button blueButton\" type=\"submit\">Search</a>
+
+                   <label>Search:</label>
+                                       <input id=\"search\" type=\"text\" name=\"search\"/>
+               </fieldset>
+                         </form>";
+
+                       if ($cat_id) {
+                               $cat_title = getCategoryTitle($link, $cat_id);
+
+                               print "<ul id=\"feed-$feed_id\" title=\"$feed_title\" selected=\"true\"
+                                       myBackLabel='$cat_title' myBackHref='cat.php?id=$cat_id'>";
+                       } else {
+                               print "<ul id=\"feed-$feed_id\" title=\"$feed_title\" selected=\"true\"
+                                       myBackLabel='".__("Home")."' myBackHref='home.php'>";
+                       }
+
+                       print "<li><a href='#searchForm-$feed_id-$cat_id'>Search...</a></li>";
+               }
+
+               $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 "<li class='$class'><a href='article.php?id=$id&feed=$feed_id&cat=$cat_id&is_cat=$is_cat'>
+                               <img class='tinyIcon' src='$icon_url'>";
+                       print $line["title"];
+                       print "</a></li>";
+
+                       ++$num_headlines;
+
+               }
+
+               if ($num_headlines == 0 && $search) {
+                       $articles_url = "feed.php?id=$feed_id&cat=$cat_id&skip=$next_offset";
+
+                       print "<li><a href=\"$articles_url\">" . __("Nothing found (click to reload feed).") . "</a></li>";
+
+               }
+
+//             print "<a target='_replace' href='feed.php?id=$feed_id&cat=$cat_id&skip=0'>Next $limit articles...</a>";
+
+               $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 "<li><a href=\"$articles_url\"
+                               target=\"_replace\">Get more articles...</a></li>";
+               }
+
+               if (!$offset) print "</ul>";
+
+       }
+
+       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 "<div class=\"panel\" id=\"article-$id\" title=\"$title\"
+                               selected=\"true\"
+                               myBackLabel='$feed_title' myBackHref='feed.php?id=$feed_id&cat=$cat_id&is_cat=$is_cat'>";
+
+                       if ($line['feed_id'] != $feed_id) {
+                               $real_feed_title = getFeedTitle($link, $line['feed_id']);
+                               $real_feed_id = $line['feed_id'];
+                               $feed_link = "(<a href=\"feed.php?id=$real_feed_id\">$real_feed_title</a>)";
+                       }
+//                     print "<fieldset>";
+
+                       print "<div style='float : right'>($updated_fmt)</div>";
+
+                       print "<h2><a target='_blank' href='$article_link'>$title</a> $feed_link</h2>";
+
+                       print "<hr>";
+
+/*                     print "<div class=\"row\">";
+                       print "<label id='title'><a target='_blank' href='$article_link'>$title</a></label>";
+                       print "</div>"; */
+
+                       $is_starred = (sql_bool_to_bool($line["marked"])) ? "true" : "false";
+                       $is_published = (sql_bool_to_bool($line["published"])) ? "true" : "false";
+
+                       //print "<div class=\"row\">";
+                       //print "<label id='updated'>Updated:</label>";
+                       //print "<input type='text' enabled='false' name='updated' disabled value='$updated_fmt'/>";
+                       //print "</div>";
+
+//                     print "</fieldset>";
+
+                       $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('/<img[^>]+>/is', '', $content);
+                       }
+
+                       print "<p>$content</p>";
+
+            print "<div class='nav'>
+                    <label>Navigation</label>
+                    <div class='button left' onclick='goPrev($id, $feed_id, this)'>Prev</div>
+                    <div class='button right' onclick='goNext($id, $feed_id, this)'>Next</div>
+                  </div>";
+
+                       print "<fieldset>";
+
+                       print "<div class=\"row\">
+                       <label>Starred</label>
+                       <div class=\"toggle\" onclick=\"toggleMarked($id, this)\" toggled=\"$is_starred\"><span class=\"thumb\"></span><span class=\"toggleOn\">ON</span><span class=\"toggleOff\">OFF</span></div>
+                   </div>";
+
+                       print "<div class=\"row\">
+                       <label>Published</label>
+                       <div class=\"toggle\" onclick=\"togglePublished($id, this)\" toggled=\"$is_published\"><span class=\"thumb\"></span><span class=\"toggleOn\">ON</span><span class=\"toggleOff\">OFF</span></div>
+                   </div>";
+
+                       print "<div class=\"row\">
+                       <label>Unread</label>
+                       <div class=\"toggle\" onclick=\"toggleUnread($id, this)\" toggled=\"$is_unread\"><span class=\"thumb\"></span><span class=\"toggleOn\">ON</span><span class=\"toggleOff\">OFF</span></div>
+                   </div>";
+
+
+                       print "</fieldset>";
+
+                       print "</div>";
+
+               }
+       }
+?>
index a5a9403e96f4b76c5413d487e095814d6df0e8e5..59c2d04aee0c126393725ae99a690a9b780f58b6 100644 (file)
@@ -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);
 
index 991f93e0cc00b2b89d160f6afba0e692825f67e6..32432a9a750cb610d298d2a216f9b11286e64928 100644 (file)
--- a/opml.php
+++ b/opml.php
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "functions.php";
        require_once "sessions.php";
        require_once "sanity_check.php";
diff --git a/prefs.js b/prefs.js
deleted file mode 100644 (file)
index 5a75060..0000000
--- a/prefs.js
+++ /dev/null
@@ -1,1967 +0,0 @@
-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&reg_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", "../..");
-
-               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]);
-}
-
index 98e4a2fd94b850ee2e017ac288990cdea1d7cde2..3787a79b610dc7323572a30dd93295844e423a4f 100644 (file)
--- a/prefs.php
+++ b/prefs.php
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "functions.php";
        require_once "sessions.php";
        require_once "sanity_check.php";
 
        <script type="text/javascript" charset="utf-8" src="localized_js.php?<?php echo $dt_add ?>"></script>
 
-       <script type="text/javascript" charset="utf-8" src="functions.js?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="deprecated.js?<?php echo $dt_add ?>"></script>
-
-       <script type="text/javascript" charset="utf-8" src="prefs.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/functions.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/deprecated.js?<?php echo $dt_add ?>"></script>
+       <script type="text/javascript" charset="utf-8" src="js/prefs.js?<?php echo $dt_add ?>"></script>
 
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 
index cc453c0f28714170e4a22acde67f0b0584c5657d..3694a5e75e108ccc4e38ce2dfd0c70b5b4cc327b 100644 (file)
@@ -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 @@
 <title>Create new account</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" type="text/css" href="utility.css">
-<script type="text/javascript" src="functions.js"></script>
+<script type="text/javascript" src="js/functions.js"></script>
 <script type="text/javascript" src="lib/prototype.js"></script>
 <script type="text/javascript" src="lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"></script>
 </head>
        </table>
        </form>
 
-       <?php print "<p><form method=\"GET\" action=\"tt-rss.php\">
+       <?php print "<p><form method=\"GET\" action=\"index.php\">
                                <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                </form>"; ?>
 
 
                if (!$login || !$email || !$test) {
                        print_error(__("Your registration information is incomplete."));
-                       print "<p><form method=\"GET\" action=\"tt-rss.php\">
+                       print "<p><form method=\"GET\" action=\"index.php\">
                                <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                </form>";
                        return;
 
                        if ($is_registered) {
                                print_error(__('Sorry, this username is already taken.'));
-                               print "<p><form method=\"GET\" action=\"tt-rss.php\">
+                               print "<p><form method=\"GET\" action=\"index.php\">
                                <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                </form>";
                        } else {
 
                                if (db_num_rows($result) != 1) {
                                        print_error(__('Registration failed.'));
-                                       print "<p><form method=\"GET\" action=\"tt-rss.php\">
+                                       print "<p><form method=\"GET\" action=\"index.php\">
                                        <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                        </form>";
                                } else {
 
                                        print_notice(__("Account created successfully."));
 
-                                       print "<p><form method=\"GET\" action=\"tt-rss.php\">
+                                       print "<p><form method=\"GET\" action=\"index.php\">
                                        <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                        </form>";
 
 
                        } else {
                                print_error('Plese check the form again, you have failed the robot test.');
-                               print "<p><form method=\"GET\" action=\"tt-rss.php\">
+                               print "<p><form method=\"GET\" action=\"index.php\">
                                <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                </form>";
 
 
        <?php print_notice(__('New user registrations are currently closed.')) ?>
 
-       <?php print "<p><form method=\"GET\" action=\"tt-rss.php\">
+       <?php print "<p><form method=\"GET\" action=\"index.php\">
                                <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
                                </form>"; ?>
 
diff --git a/sanity_check.php b/sanity_check.php
deleted file mode 100644 (file)
index 3e5c098..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-       require_once "functions.php";
-
-       define('EXPECTED_CONFIG_VERSION', 23);
-       define('SCHEMA_VERSION', 86);
-
-       if (!file_exists("config.php")) {
-               print "<b>Fatal Error</b>: You forgot to copy
-               <b>config.php-dist</b> to <b>config.php</b> 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 "<b>Fatal error</b>: 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 "<b>Fatal Error</b>: XML Import/Export tools (<b>xml-export.php</b>
-               and <b>xml-import.php</b>) could be used maliciously. Please remove them
-               from your TT-RSS instance.\n";
-               exit;
-       }
-
-       if (SINGLE_USER_MODE && DAEMON_UPDATE_LOGIN_LIMIT > 0) {
-               print "<b>Fatal Error</b>: 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 <a href='http://tt-rss.org/wiki/UpdatingFeeds'>wiki</a>.";
-
-       }
-
-       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 "<b>Fatal Error</b>: $err_msg\n";
-               exit;
-       }
-
-?>
diff --git a/sanity_config.php b/sanity_config.php
deleted file mode 100644 (file)
index 51c9d52..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php # This file has been generated at:  Tue Apr 26 18:40:48 MSD 2011
-define('GENERATED_CONFIG_CHECK', 23);
-$requred_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'SELF_URL_PATH', 'SINGLE_USER_MODE', 'CACHE_DIR', 'SIMPLEPIE_CACHE_IMAGES', 'ICONS_DIR', 'ICONS_URL', 'TMP_DIRECTORY', 'DAEMON_SLEEP_INTERVAL', 'DATABASE_BACKED_SESSIONS', 'SESSION_CHECK_ADDRESS', 'SESSION_COOKIE_LIFETIME', 'SESSION_EXPIRE_TIME', 'DAEMON_UPDATE_LOGIN_LIMIT', 'CHECK_FOR_NEW_VERSION', 'DIGEST_ENABLE', 'DIGEST_EMAIL_LIMIT', 'DAEMON_SENDS_DIGESTS', 'MYSQL_CHARSET', 'DEFAULT_UPDATE_METHOD', 'COUNTERS_MAX_AGE', 'DIGEST_FROM_NAME', 'DIGEST_FROM_ADDRESS', 'DIGEST_SUBJECT', 'DIGEST_SMTP_HOST', 'DIGEST_SMTP_LOGIN', 'DIGEST_SMTP_PASSWORD', 'DAEMON_FEED_LIMIT', 'ALLOW_REMOTE_USER_AUTH', 'AUTO_LOGIN', 'AUTO_CREATE_USER', 'LOCK_DIRECTORY', 'ENABLE_GZIP_OUTPUT', 'PHP_EXECUTABLE', 'ENABLE_REGISTRATION', 'REG_NOTIFY_ADDRESS', 'REG_MAX_USERS', 'FEEDBACK_URL', 'FORCE_ARTICLE_PURGE', 'SPHINX_ENABLED', 'SPHINX_INDEX', 'ENABLE_TWEET_BUTTON', 'CONSUMER_KEY', 'CONSUMER_SECRET', 'PUBSUBHUBBUB_HUB', 'PUBSUBHUBBUB_ENABLED', 'ISCONFIGURED', 'CONFIG_VERSION'); ?>
diff --git a/sessions.php b/sessions.php
deleted file mode 100644 (file)
index 8588f58..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-       // Original from http://www.daniweb.com/code/snippet43.html
-
-       require_once "config.php";
-       require_once "db.php";
-
-       $session_expire = SESSION_EXPIRE_TIME; //seconds
-       $session_name = (!defined('TTRSS_SESSION_NAME')) ? "ttrss_sid" : TTRSS_SESSION_NAME;
-
-       if ($_SERVER['HTTPS'] == "on") {
-               $session_name .= "_ssl";
-               ini_set("session.cookie_secure", true);
-       }
-
-       ini_set("session.gc_probability", 50);
-       ini_set("session.name", $session_name);
-       ini_set("session.use_only_cookies", true);
-       ini_set("session.gc_maxlifetime", SESSION_EXPIRE_TIME);
-
-       function ttrss_open ($s, $n) {
-
-               global $session_connection;
-
-               $session_connection = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
-
-               return true;
-       }
-
-       function ttrss_read ($id){
-
-               global $session_connection,$session_read;
-
-               $query = "SELECT data FROM ttrss_sessions WHERE id='$id'";
-
-               $res = db_query($session_connection, $query);
-
-               if (db_num_rows($res) != 1) {
-                       return "";
-               } else {
-                       $session_read = db_fetch_assoc($res);
-                       $session_read["data"] = base64_decode($session_read["data"]);
-                       return $session_read["data"];
-               }
-       }
-
-       function ttrss_write ($id, $data) {
-
-               if (! $data) {
-                       return false;
-               }
-
-               global $session_connection, $session_read, $session_expire;
-
-               $expire = time() + $session_expire;
-
-               $data = db_escape_string(base64_encode($data), $session_connection);
-
-               if ($session_read) {
-                       $query = "UPDATE ttrss_sessions SET data='$data',
-                                       expire='$expire' WHERE id='$id'";
-               } else {
-                       $query = "INSERT INTO ttrss_sessions (id, data, expire)
-                                       VALUES ('$id', '$data', '$expire')";
-               }
-
-               db_query($session_connection, $query);
-               return true;
-       }
-
-       function ttrss_close () {
-
-               global $session_connection;
-
-               db_close($session_connection);
-
-               return true;
-       }
-
-       function ttrss_destroy ($id) {
-
-               global $session_connection;
-
-               $query = "DELETE FROM ttrss_sessions WHERE id = '$id'";
-
-               db_query($session_connection, $query);
-
-               return true;
-       }
-
-       function ttrss_gc ($expire) {
-
-               global $session_connection;
-
-               $query = "DELETE FROM ttrss_sessions WHERE expire < " . time();
-
-               db_query($session_connection, $query);
-       }
-
-       if (DATABASE_BACKED_SESSIONS) {
-               session_set_save_handler("ttrss_open",
-                       "ttrss_close", "ttrss_read", "ttrss_write",
-                       "ttrss_destroy", "ttrss_gc");
-       }
-
-       session_set_cookie_params(SESSION_COOKIE_LIFETIME);
-
-       session_start();
-?>
diff --git a/tt-rss.js b/tt-rss.js
deleted file mode 100644 (file)
index 36e0d8c..0000000
--- 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 = "<div id=\"feedlistLoading\">" +
-//                     __("Loading, please wait...") + "</div>";
-
-               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&param=" +
-               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("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
-                       return;
-               }
-
-               if (k == "daemon_stamp_ok" && v != 1) {
-                       notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", 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 = "&lt;&lt;";
-               } else {
-                       Element.hide('feeds-holder');
-                       Element.hide('feeds-holder_splitter');
-                       $("collapse_feeds_btn").innerHTML = "&gt;&gt;";
-               }
-
-               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 (file)
index 0275614..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-       require_once "functions.php";
-       require_once "sessions.php";
-       require_once "sanity_check.php";
-       require_once "version.php";
-       require_once "config.php";
-       require_once "db-prefs.php";
-
-       $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
-
-       init_connection($link);
-
-       login_sequence($link);
-
-       $dt_add = time();
-
-       no_cache_incantation();
-
-       header('Content-Type: text/html; charset=utf-8');
-
-?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html>
-<head>
-       <title>Tiny Tiny RSS</title>
-       <link rel="stylesheet" type="text/css" href="lib/dijit/themes/claro/claro.css"/>
-       <link rel="stylesheet" type="text/css" href="tt-rss.css?<?php echo $dt_add ?>"/>
-       <link rel="stylesheet" type="text/css" href="cdm.css?<?php echo $dt_add ?>"/>
-
-       <?php print_theme_includes($link) ?>
-       <?php print_user_stylesheet($link) ?>
-
-       <link rel="shortcut icon" type="image/png" href="images/favicon.png"/>
-
-       <script type="text/javascript" src="lib/prototype.js"></script>
-       <script type="text/javascript" src="lib/scriptaculous/scriptaculous.js?load=effects,dragdrop,controls"></script>
-       <script type="text/javascript" src="lib/dojo/dojo.js"></script>
-       <script type="text/javascript" src="lib/dijit/dijit.js"></script>
-       <script type="text/javascript" src="lib/dojo/tt-rss-layer.js"></script>
-
-       <script type="text/javascript" charset="utf-8" src="localized_js.php?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="tt-rss.js?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="functions.js?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="feedlist.js?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="viewfeed.js?<?php echo $dt_add ?>"></script>
-       <script type="text/javascript" charset="utf-8" src="errors.php?mode=js"></script>
-
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-
-       <script type="text/javascript">
-               Event.observe(window, 'load', function() {
-                       init();
-               });
-       </script>
-</head>
-
-<body id="ttrssMain" class="claro">
-
-<div id="overlay" style="display : block">
-       <div id="overlay_inner">
-               <div class="insensitive"><?php echo __("Loading, please wait...") ?></div>
-               <div dojoType="dijit.ProgressBar" places="0" style="width : 300px" id="loading_bar"
-            progress="0" maximum="100">
-               </div>
-               <noscript><br/><?php print_error('Javascript is disabled. Please enable it.') ?></noscript>
-       </div>
-</div>
-
-<div id="header">
-       <?php if (!SINGLE_USER_MODE) { ?>
-                       <?php echo __('Hello,') ?> <b><?php echo $_SESSION["name"] ?></b> |
-       <?php } ?>
-       <a href="prefs.php"><?php echo __('Preferences') ?></a>
-
-       <?php if (defined('FEEDBACK_URL') && FEEDBACK_URL) { ?>
-               | <a target="_blank" class="feedback" href="<?php echo FEEDBACK_URL ?>">
-                               <?php echo __('Comments?') ?></a>
-       <?php } ?>
-
-       <?php if (!SINGLE_USER_MODE) { ?>
-                       | <a href="backend.php?op=logout"><?php echo __('Logout') ?></a>
-       <?php } ?>
-
-       <img id="newVersionIcon" style="display:none" onclick="newVersionDlg()"
-               width="13" height="13"
-               src="<?php echo theme_image($link, 'images/new_version.png') ?>"
-               title="<?php echo __('New version of Tiny Tiny RSS is available!') ?>"
-               alt="new_version_icon"/>
-</div>
-
-<div id="hotkey_help_overlay" style="display : none" onclick="Element.hide(this)">
-       <?php include "help/3.php" ?>
-</div>
-
-<div id="notify" class="notify"><span id="notify_body">&nbsp;</span></div>
-<div id="cmdline" style="display : none"></div>
-<div id="auxDlg" style="display : none"></div>
-<div id="headlines-tmp" style="display : none"></div>
-
-<div id="main" dojoType="dijit.layout.BorderContainer">
-
-<div id="feeds-holder" dojoType="dijit.layout.ContentPane" region="leading" style="width : 20%" splitter="true">
-       <div id="feedlistLoading">
-               <img src='images/indicator_tiny.gif'/>
-               <?php echo  __("Loading, please wait..."); ?></div>
-       <div id="feedTree"></div>
-</div>
-
-<div dojoType="dijit.layout.BorderContainer" region="center" id="header-wrap" gutters="false">
-<div dojoType="dijit.layout.TabContainer" region="center" id="content-tabs">
-<div dojoType="dijit.layout.BorderContainer" region="center" id="content-wrap"
-       title="<?php echo __("News") ?>">
-
-<div id="toolbar" dojoType="dijit.layout.ContentPane" region="top">
-       <div id="main-toolbar" dojoType="dijit.Toolbar">
-
-               <form id="main_toolbar_form" action="" onsubmit='return false'>
-
-               <button dojoType="dijit.form.Button" id="collapse_feeds_btn"
-                       onclick="collapse_feedlist()"
-                       title="<?php echo __('Collapse feedlist') ?>" style="display : inline">
-                       &lt;&lt;</button>
-
-               <select name="view_mode" title="<?php echo __('Show articles') ?>"
-                       onchange="viewModeChanged()"
-                       dojoType="dijit.form.Select">
-                       <option selected="selected" value="adaptive"><?php echo __('Adaptive') ?></option>
-                       <option value="all_articles"><?php echo __('All Articles') ?></option>
-                       <option value="marked"><?php echo __('Starred') ?></option>
-                       <option value="published"><?php echo __('Published') ?></option>
-                       <option value="unread"><?php echo __('Unread') ?></option>
-                       <!-- <option value="noscores"><?php echo __('Ignore Scoring') ?></option> -->
-                       <option value="updated"><?php echo __('Updated') ?></option>
-               </select>
-
-               <select title="<?php echo __('Sort articles') ?>"
-                       onchange="viewModeChanged()"
-                       dojoType="dijit.form.Select" name="order_by">
-                       <option selected="selected" value="default"><?php echo __('Default') ?></option>
-                       <option value="date"><?php echo __('Date') ?></option>
-                       <option value="title"><?php echo __('Title') ?></option>
-                       <option value="score"><?php echo __('Score') ?></option>
-               </select>
-
-               <button dojoType="dijit.form.Button" name="update"
-                       onclick="scheduleFeedUpdate()">
-                       <?php echo __('Update') ?></button>
-
-               <button dojoType="dijit.form.Button"
-                       onclick="catchupCurrentFeed()">
-                       <?php echo __('Mark as read') ?></button>
-
-               </form>
-
-               <div class="actionChooser">
-                       <div dojoType="dijit.form.DropDownButton">
-                               <span><?php echo __('Actions...') ?></span>
-                               <div dojoType="dijit.Menu" style="display: none">
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcSearch')"><?php echo __('Search...') ?></div>
-                                       <div dojoType="dijit.MenuItem" disabled="1"><?php echo __('Feed actions:') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAddFeed')"><?php echo __('Subscribe to feed...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcEditFeed')"><?php echo __('Edit this feed...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcRescoreFeed')"><?php echo __('Rescore feed') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcRemoveFeed')"><?php echo __('Unsubscribe') ?></div>
-                                       <div dojoType="dijit.MenuItem" disabled="1"><?php echo __('All feeds:') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcCatchupAll')"><?php echo __('Mark as read') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcShowOnlyUnread')"><?php echo __('(Un)hide read feeds') ?></div>
-                                       <div dojoType="dijit.MenuItem" disabled="1"><?php echo __('Other actions:') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcDigest')"><?php echo __('Switch to digest...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcTagCloud')"><?php echo __('Show tag cloud...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcTagSelect')"><?php echo __('Select by tags...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAddLabel')"><?php echo __('Create label...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAddFilter')"><?php echo __('Create filter...') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcHKhelp')"><?php echo __('Keyboard shortcuts help') ?></div>
-                                       <div dojoType="dijit.MenuItem" onclick="quickMenuGo('qmcAbout')"><?php echo __('About...') ?></div>
-                               </div>
-                       </div>
-               </div>
-       </div> <!-- toolbar -->
-</div> <!-- toolbar pane -->
-
-       <div id="headlines-wrap-inner" dojoType="dijit.layout.BorderContainer" region="center">
-
-               <div id="headlines-toolbar" dojoType="dijit.layout.ContentPane" region="top">
-               </div>
-
-               <div id="headlines-frame" dojoType="dijit.layout.ContentPane"
-                               onscroll="headlines_scroll_handler(this)" region="center">
-                       <div id="headlinesInnerContainer">
-                               <div class="whiteBox"><?php echo __('Loading, please wait...') ?></div>
-                       </div>
-               </div>
-
-               <?php if (!get_pref($link, 'COMBINED_DISPLAY_MODE')) { ?>
-               <div id="content-insert" dojoType="dijit.layout.ContentPane" region="bottom"
-                       style="height : 50%" splitter="true"></div>
-               <?php } ?>
-
-       </div>
-</div>
-</div>
-</div>
-</div>
-
-<?php db_close($link); ?>
-
-</body>
-</html>
index 9d0c8e50900e464c0d17224b7548db2bb7743e78..2c325140bdc85b3bb3107b0fe77ec874c2514d77 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        require_once "functions.php";
        require_once "sessions.php";
        require_once "sanity_check.php";
@@ -7,15 +9,15 @@
        //require_once "lib/twitteroauth/twitteroauth.php";
        require_once "lib/tmhoauth/tmhOAuth.php";
 
-       $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME); 
+       $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
 
-       init_connection($link); 
+       init_connection($link);
        login_sequence($link);
-       
+
        $owner_uid = $_SESSION["uid"];
        $op = $_REQUEST['op'];
 
-       if (!SINGLE_USER_MODE && !$_SESSION['uid']) { 
+       if (!SINGLE_USER_MODE && !$_SESSION['uid']) {
                render_login_form($link);
                exit;
        }
@@ -25,7 +27,7 @@
        $tmhOAuth = new tmhOAuth(array(
          'consumer_key'    => 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");
index 7b17c60f894d3c2249ea5fe4eecd21dd0cfc50a2..e6063a9e829e15b92d5641876a43377be9023535 100755 (executable)
@@ -1,5 +1,7 @@
 #!/usr/bin/php
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        define('DISABLE_SESSIONS', true);
 
        chdir(dirname(__FILE__));
        }
 
        db_close($link);
-       
+
        if ($lock_handle != false) {
                fclose($lock_handle);
        }
index 9ad914307a0019ae122cca5d10e88d029c577c19..06271de85187dd72b635257eafe3525ce9c46a89 100755 (executable)
@@ -1,5 +1,7 @@
 #!/usr/bin/php
 <?php
+       set_include_path(get_include_path() . PATH_SEPARATOR . "include");
+
        // This is an experimental multiprocess update daemon.
        // Some configurable variable may be found below.
 
diff --git a/version.php b/version.php
deleted file mode 100644 (file)
index 61445c5..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-       define('VERSION', "1.5.7");
-?>
diff --git a/viewfeed.js b/viewfeed.js
deleted file mode 100644 (file)
index 9cb9023..0000000
+++ /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 + "<div id='headlines-spacer'></div>";
-                               }
-
-                               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', "<div class='whiteBox'>" +
-                                       __('Could not update headlines (invalid object received - see error console for details)') +
-                                       "</div>");
-               }
-
-               _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("<div class='whiteBox'>" +
-                                       __('Could not display article (invalid object received - see error console for details)') + "</div>");
-               }
-
-               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 + "&note=" + param_escape(note);
-               } else {
-                       query = query + "&note=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&param=" + 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 = "<img src='images/indicator_tiny.gif'> " +
-                                               __("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 = "<div class=\"insensitive\">" +
-                                       __("Loading, please wait...") + "</div>";
-
-                               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&param=" + 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&param=" + 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&param=" + 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);
-       }
-}
-
-