summaryrefslogtreecommitdiff
path: root/classes/pref
diff options
context:
space:
mode:
Diffstat (limited to 'classes/pref')
-rw-r--r--classes/pref/feeds.php1681
-rw-r--r--classes/pref/filters.php657
-rw-r--r--classes/pref/instances.php210
-rw-r--r--classes/pref/labels.php326
-rw-r--r--classes/pref/prefs.php499
-rw-r--r--classes/pref/users.php494
6 files changed, 3867 insertions, 0 deletions
diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php
new file mode 100644
index 000000000..5899115b4
--- /dev/null
+++ b/classes/pref/feeds.php
@@ -0,0 +1,1681 @@
+<?php
+class Pref_Feeds extends Handler_Protected {
+
+ function csrf_ignore($method) {
+ $csrf_ignored = array("index", "getfeedtree", "add", "editcats", "editfeed",
+ "savefeedorder", "uploadicon");
+
+ return array_search($method, $csrf_ignored) !== false;
+ }
+
+ function batch_edit_cbox($elem, $label = false) {
+ print "<input type=\"checkbox\" title=\"".__("Check to enable field")."\"
+ onchange=\"dijit.byId('feedEditDlg').toggleField(this, '$elem', '$label')\">";
+ }
+
+ function renamecat() {
+ $title = db_escape_string($_REQUEST['title']);
+ $id = db_escape_string($_REQUEST['id']);
+
+ if ($title) {
+ db_query($this->link, "UPDATE ttrss_feed_categories SET
+ title = '$title' WHERE id = '$id' AND owner_uid = " . $_SESSION["uid"]);
+ }
+ return;
+ }
+
+ function remtwitterinfo() {
+
+ db_query($this->link, "UPDATE ttrss_users SET twitter_oauth = NULL
+ WHERE id = " . $_SESSION['uid']);
+
+ return;
+ }
+
+ private function get_category_items($cat_id) {
+ $show_empty_cats = $_REQUEST['mode'] != 2 &&
+ get_pref($this->link, '_PREFS_SHOW_EMPTY_CATS');
+
+ $items = array();
+
+ $result = db_query($this->link, "SELECT id, title, collapsed FROM ttrss_feed_categories
+ WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat = '$cat_id' ORDER BY order_id, title");
+
+ while ($line = db_fetch_assoc($result)) {
+
+ $cat = array();
+ $cat['id'] = 'CAT:' . $line['id'];
+ $cat['bare_id'] = (int)$line['id'];
+ $cat['name'] = $line['title'];
+ $cat['items'] = array();
+ $cat['checkbox'] = false;
+ $cat['hidden'] = sql_bool_to_bool($line['collapsed']);
+ $cat['type'] = 'category';
+ $cat['unread'] = 0;
+ $cat['child_unread'] = 0;
+
+ $cat['items'] = $this->get_category_items($line['id']);
+
+ $cat['param'] = T_sprintf('(%d feeds)', count($cat['items']));
+
+ if (count($cat['items']) > 0 || $show_empty_cats)
+ array_push($items, $cat);
+
+ }
+
+ $feed_result = db_query($this->link, "SELECT id, title, last_error,
+ ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
+ FROM ttrss_feeds
+ WHERE cat_id = '$cat_id' AND owner_uid = ".$_SESSION["uid"].
+ "$search_qpart ORDER BY order_id, title");
+
+ while ($feed_line = db_fetch_assoc($feed_result)) {
+ $feed = array();
+ $feed['id'] = 'FEED:' . $feed_line['id'];
+ $feed['bare_id'] = (int)$feed_line['id'];
+ $feed['name'] = $feed_line['title'];
+ $feed['checkbox'] = false;
+ $feed['unread'] = 0;
+ $feed['error'] = $feed_line['last_error'];
+ $feed['icon'] = getFeedIcon($feed_line['id']);
+ $feed['param'] = make_local_datetime($this->link,
+ $feed_line['last_updated'], true);
+
+ array_push($items, $feed);
+ }
+
+ return $items;
+ }
+
+ function getfeedtree() {
+
+ $search = $_SESSION["prefs_feed_search"];
+
+ if ($search) $search_qpart = " AND LOWER(title) LIKE LOWER('%$search%')";
+
+ $root = array();
+ $root['id'] = 'root';
+ $root['name'] = __('Feeds');
+ $root['items'] = array();
+ $root['type'] = 'category';
+
+ $enable_cats = get_pref($this->link, 'ENABLE_FEED_CATS');
+
+ if ($_REQUEST['mode'] == 2) {
+
+ if ($enable_cats) {
+ $cat_hidden = get_pref($this->link, "_COLLAPSED_SPECIAL");
+ $cat = $this->feedlist_init_cat(-1, $cat_hidden);
+ } else {
+ $cat['items'] = array();
+ }
+
+ foreach (array(-4, -3, -1, -2, 0) as $i) {
+ array_push($cat['items'], $this->feedlist_init_feed($i));
+ }
+
+ if ($enable_cats) {
+ array_push($root['items'], $cat);
+ } else {
+ $root['items'] = array_merge($root['items'], $cat['items']);
+ }
+
+ $result = db_query($this->link, "SELECT * FROM
+ ttrss_labels2 WHERE owner_uid = ".$_SESSION['uid']." ORDER by caption");
+
+ if (db_num_rows($result) > 0) {
+
+ if (get_pref($this->link, 'ENABLE_FEED_CATS')) {
+ $cat_hidden = get_pref($this->link, "_COLLAPSED_LABELS");
+ $cat = $this->feedlist_init_cat(-2, $cat_hidden);
+ } else {
+ $cat['items'] = array();
+ }
+
+ while ($line = db_fetch_assoc($result)) {
+
+ $label_id = -$line['id'] - 11;
+ $count = getFeedUnread($this->link, $label_id);
+
+ $feed = $this->feedlist_init_feed($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($root['items'], $cat);
+ } else {
+ $root['items'] = array_merge($root['items'], $cat['items']);
+ }
+ }
+ }
+
+ if ($enable_cats) {
+ $show_empty_cats = $_REQUEST['mode'] != 2 &&
+ get_pref($this->link, '_PREFS_SHOW_EMPTY_CATS');
+
+ $result = db_query($this->link, "SELECT id, title, collapsed FROM ttrss_feed_categories
+ WHERE owner_uid = " . $_SESSION["uid"] . " AND parent_cat IS NULL ORDER BY order_id, title");
+
+ while ($line = db_fetch_assoc($result)) {
+ $cat = array();
+ $cat['id'] = 'CAT:' . $line['id'];
+ $cat['bare_id'] = (int)$line['id'];
+ $cat['name'] = $line['title'];
+ $cat['items'] = array();
+ $cat['checkbox'] = false;
+ $cat['hidden'] = sql_bool_to_bool($line['collapsed']);
+ $cat['type'] = 'category';
+ $cat['unread'] = 0;
+ $cat['child_unread'] = 0;
+
+ $cat['items'] = $this->get_category_items($line['id']);
+
+ $cat['param'] = T_sprintf('(%d feeds)', count($cat['items']));
+
+ if (count($cat['items']) > 0 || $show_empty_cats)
+ array_push($root['items'], $cat);
+
+ $root['param'] += count($cat['items']);
+ }
+
+ /* Uncategorized is a special case */
+
+ $cat = array();
+ $cat['id'] = 'CAT:0';
+ $cat['bare_id'] = 0;
+ $cat['name'] = __("Uncategorized");
+ $cat['items'] = array();
+ $cat['hidden'] = get_pref($this->link, "_COLLAPSED_UNCAT");
+ $cat['type'] = 'category';
+ $cat['checkbox'] = false;
+ $cat['unread'] = 0;
+ $cat['child_unread'] = 0;
+
+ $feed_result = db_query($this->link, "SELECT id, title,last_error,
+ ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
+ FROM ttrss_feeds
+ WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"].
+ "$search_qpart ORDER BY order_id, title");
+
+ while ($feed_line = db_fetch_assoc($feed_result)) {
+ $feed = array();
+ $feed['id'] = 'FEED:' . $feed_line['id'];
+ $feed['bare_id'] = (int)$feed_line['id'];
+ $feed['name'] = $feed_line['title'];
+ $feed['checkbox'] = false;
+ $feed['error'] = $feed_line['last_error'];
+ $feed['icon'] = getFeedIcon($feed_line['id']);
+ $feed['param'] = make_local_datetime($this->link,
+ $feed_line['last_updated'], true);
+ $feed['unread'] = 0;
+ $feed['type'] = 'feed';
+
+ array_push($cat['items'], $feed);
+ }
+
+ $cat['param'] = T_sprintf('(%d feeds)', count($cat['items']));
+
+ if (count($cat['items']) > 0 || $show_empty_cats)
+ array_push($root['items'], $cat);
+
+ $root['param'] += count($cat['items']);
+ $root['param'] = T_sprintf('(%d feeds)', $root['param']);
+
+ } else {
+ $feed_result = db_query($this->link, "SELECT id, title, last_error,
+ ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
+ FROM ttrss_feeds
+ WHERE owner_uid = ".$_SESSION["uid"].
+ "$search_qpart ORDER BY order_id, title");
+
+ while ($feed_line = db_fetch_assoc($feed_result)) {
+ $feed = array();
+ $feed['id'] = 'FEED:' . $feed_line['id'];
+ $feed['bare_id'] = (int)$feed_line['id'];
+ $feed['name'] = $feed_line['title'];
+ $feed['checkbox'] = false;
+ $feed['error'] = $feed_line['last_error'];
+ $feed['icon'] = getFeedIcon($feed_line['id']);
+ $feed['param'] = make_local_datetime($this->link,
+ $feed_line['last_updated'], true);
+ $feed['unread'] = 0;
+ $feed['type'] = 'feed';
+
+ array_push($root['items'], $feed);
+ }
+
+ $root['param'] = T_sprintf('(%d feeds)', count($root['items']));
+ }
+
+ $fl = array();
+ $fl['identifier'] = 'id';
+ $fl['label'] = 'name';
+
+ if ($_REQUEST['mode'] != 2) {
+ $fl['items'] = array($root);
+ } else {
+ $fl['items'] =& $root['items'];
+ }
+
+ print json_encode($fl);
+ return;
+ }
+
+ function catsortreset() {
+ db_query($this->link, "UPDATE ttrss_feed_categories
+ SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]);
+ return;
+ }
+
+ function feedsortreset() {
+ db_query($this->link, "UPDATE ttrss_feeds
+ SET order_id = 0 WHERE owner_uid = " . $_SESSION["uid"]);
+ return;
+ }
+
+ function togglehiddenfeedcats() {
+ set_pref($this->link, '_PREFS_SHOW_EMPTY_CATS',
+ (get_pref($this->link, '_PREFS_SHOW_EMPTY_CATS') ? 'false' : 'true'));
+ }
+
+ private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) {
+ $debug = isset($_REQUEST["debug"]);
+
+ $prefix = "";
+ for ($i = 0; $i < $nest_level; $i++)
+ $prefix .= " ";
+
+ if ($debug) _debug("$prefix C: $item_id P: $parent_id");
+
+ $bare_item_id = substr($item_id, strpos($item_id, ':')+1);
+
+ if ($item_id != 'root') {
+ if ($parent_id && $parent_id != 'root') {
+ $parent_bare_id = substr($parent_id, strpos($parent_id, ':')+1);
+ $parent_qpart = db_escape_string($parent_bare_id);
+ } else {
+ $parent_qpart = 'NULL';
+ }
+
+ db_query($this->link, "UPDATE ttrss_feed_categories
+ SET parent_cat = $parent_qpart WHERE id = '$bare_item_id' AND
+ owner_uid = " . $_SESSION["uid"]);
+ }
+
+ $order_id = 0;
+
+ $cat = $data_map[$item_id];
+
+ if ($cat && is_array($cat)) {
+ foreach ($cat as $item) {
+ $id = $item['_reference'];
+ $bare_id = substr($id, strpos($id, ':')+1);
+
+ if ($debug) _debug("$prefix [$order_id] $id/$bare_id");
+
+ if ($item['_reference']) {
+
+ if (strpos($id, "FEED") === 0) {
+
+ $cat_id = ($item_id != "root") ?
+ db_escape_string($bare_item_id) : "NULL";
+
+ db_query($this->link, "UPDATE ttrss_feeds
+ SET order_id = $order_id, cat_id = '$cat_id'
+ WHERE id = '$bare_id' AND
+ owner_uid = " . $_SESSION["uid"]);
+
+ } else if (strpos($id, "CAT:") === 0) {
+ $this->process_category_order($data_map, $item['_reference'], $item_id,
+ $nest_level+1);
+
+ if ($item_id != 'root') {
+ $parent_qpart = db_escape_string($bare_id);
+ } else {
+ $parent_qpart = 'NULL';
+ }
+
+ db_query($this->link, "UPDATE ttrss_feed_categories
+ SET order_id = '$order_id' WHERE id = '$bare_id' AND
+ owner_uid = " . $_SESSION["uid"]);
+ }
+ }
+
+ ++$order_id;
+ }
+ }
+ }
+
+ function savefeedorder() {
+ $data = json_decode($_POST['payload'], true);
+
+ #file_put_contents("/tmp/saveorder.json", $_POST['payload']);
+ #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true);
+
+ if (!is_array($data['items']))
+ $data['items'] = json_decode($data['items'], true);
+
+# print_r($data['items']);
+
+ if (is_array($data) && is_array($data['items'])) {
+ $cat_order_id = 0;
+
+ $data_map = array();
+ $root_item = false;
+
+ foreach ($data['items'] as $item) {
+
+# if ($item['id'] != 'root') {
+ if (is_array($item['items'])) {
+ if (isset($item['items']['_reference'])) {
+ $data_map[$item['id']] = array($item['items']);
+ } else {
+ $data_map[$item['id']] =& $item['items'];
+ }
+ }
+ if ($item['id'] == 'root') {
+ $root_item = $item['id'];
+ }
+ }
+
+ $this->process_category_order($data_map, $root_item);
+
+ /* foreach ($data['items'][0]['items'] as $item) {
+ $id = $item['_reference'];
+ $bare_id = substr($id, strpos($id, ':')+1);
+
+ ++$cat_order_id;
+
+ if ($bare_id > 0) {
+ db_query($this->link, "UPDATE ttrss_feed_categories
+ SET order_id = '$cat_order_id' WHERE id = '$bare_id' AND
+ owner_uid = " . $_SESSION["uid"]);
+ }
+
+ $feed_order_id = 0;
+
+ if (is_array($data_map[$id])) {
+ foreach ($data_map[$id] as $feed) {
+ $id = $feed['_reference'];
+ $feed_id = substr($id, strpos($id, ':')+1);
+
+ if ($bare_id != 0)
+ $cat_query = "cat_id = '$bare_id'";
+ else
+ $cat_query = "cat_id = NULL";
+
+ db_query($this->link, "UPDATE ttrss_feeds
+ SET order_id = '$feed_order_id',
+ $cat_query
+ WHERE id = '$feed_id' AND
+ owner_uid = " . $_SESSION["uid"]);
+
+ ++$feed_order_id;
+ }
+ }
+ } */
+ }
+
+ return;
+ }
+
+ function removeicon() {
+ $feed_id = db_escape_string($_REQUEST["feed_id"]);
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_feeds
+ WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]);
+
+ if (db_num_rows($result) != 0) {
+ unlink(ICONS_DIR . "/$feed_id.ico");
+ }
+
+ return;
+ }
+
+ function uploadicon() {
+ $icon_file = $_FILES['icon_file']['tmp_name'];
+ $feed_id = db_escape_string($_REQUEST["feed_id"]);
+
+ if (is_file($icon_file) && $feed_id) {
+ if (filesize($icon_file) < 20000) {
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_feeds
+ WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]);
+
+ if (db_num_rows($result) != 0) {
+ unlink(ICONS_DIR . "/$feed_id.ico");
+ move_uploaded_file($icon_file, ICONS_DIR . "/$feed_id.ico");
+ $rc = 0;
+ } else {
+ $rc = 2;
+ }
+ } else {
+ $rc = 1;
+ }
+ } else {
+ $rc = 2;
+ }
+
+ print "<script type=\"text/javascript\">";
+ print "parent.uploadIconHandler($rc);";
+ print "</script>";
+ return;
+ }
+
+ function editfeed() {
+ global $purge_intervals;
+ global $update_intervals;
+ global $update_methods;
+
+ $feed_id = db_escape_string($_REQUEST["id"]);
+
+ $result = db_query($this->link,
+ "SELECT * FROM ttrss_feeds WHERE id = '$feed_id' AND
+ owner_uid = " . $_SESSION["uid"]);
+
+ $title = htmlspecialchars(db_fetch_result($result,
+ 0, "title"));
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$feed_id\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
+
+ print "<div class=\"dlgSec\">".__("Feed")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ /* Title */
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ placeHolder=\"".__("Feed Title")."\"
+ style=\"font-size : 16px; width: 20em\" name=\"title\" value=\"$title\">";
+
+ /* Feed URL */
+
+ $feed_url = db_fetch_result($result, 0, "feed_url");
+ $feed_url = htmlspecialchars(db_fetch_result($result,
+ 0, "feed_url"));
+
+ print "<hr/>";
+
+ print __('URL:') . " ";
+ print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ placeHolder=\"".__("Feed URL")."\"
+ regExp='^(http|https)://.*' style=\"width : 20em\"
+ name=\"feed_url\" value=\"$feed_url\">";
+
+ $last_error = db_fetch_result($result, 0, "last_error");
+
+ if ($last_error) {
+ print "&nbsp;<span title=\"".htmlspecialchars($last_error)."\"
+ class=\"feed_error\">(error)</span>";
+
+ }
+
+ /* Category */
+
+ if (get_pref($this->link, 'ENABLE_FEED_CATS')) {
+
+ $cat_id = db_fetch_result($result, 0, "cat_id");
+
+ print "<hr/>";
+
+ print __('Place in category:') . " ";
+
+ print_feed_cat_select($this->link, "cat_id", $cat_id,
+ 'dojoType="dijit.form.Select"');
+ }
+
+ print "</div>";
+
+ print "<div class=\"dlgSec\">".__("Update")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ /* Update Interval */
+
+ $update_interval = db_fetch_result($result, 0, "update_interval");
+
+ print_select_hash("update_interval", $update_interval, $update_intervals,
+ 'dojoType="dijit.form.Select"');
+
+ /* Update method */
+
+ $update_method = db_fetch_result($result, 0, "update_method",
+ 'dojoType="dijit.form.Select"');
+
+ print " " . __('using') . " ";
+ print_select_hash("update_method", $update_method, $update_methods,
+ 'dojoType="dijit.form.Select"');
+
+ $purge_interval = db_fetch_result($result, 0, "purge_interval");
+
+
+ /* Purge intl */
+
+ print "<hr/>";
+ print __('Article purging:') . " ";
+
+ print_select_hash("purge_interval", $purge_interval, $purge_intervals,
+ 'dojoType="dijit.form.Select" ' .
+ ((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"'));
+
+ print "</div>";
+ print "<div class=\"dlgSec\">".__("Authentication")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ $auth_login = htmlspecialchars(db_fetch_result($result, 0, "auth_login"));
+
+ print "<input dojoType=\"dijit.form.TextBox\" id=\"feedEditDlg_login\"
+ placeHolder=\"".__("Login")."\"
+ name=\"auth_login\" value=\"$auth_login\"><hr/>";
+
+ $auth_pass = htmlspecialchars(db_fetch_result($result, 0, "auth_pass"));
+
+ print "<input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
+ placeHolder=\"".__("Password")."\"
+ value=\"$auth_pass\">";
+
+ print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedEditDlg_login\" position=\"below\">
+ ".__('<b>Hint:</b> you need to fill in your login information if your feed requires authentication, except for Twitter feeds.')."
+ </div>";
+
+ print "</div>";
+ print "<div class=\"dlgSec\">".__("Options")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ $private = sql_bool_to_bool(db_fetch_result($result, 0, "private"));
+
+ if ($private) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"private\" id=\"private\"
+ $checked>&nbsp;<label for=\"private\">".__('Hide from Popular feeds')."</label>";
+
+ $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content"));
+
+ if ($rtl_content) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"rtl_content\" name=\"rtl_content\"
+ $checked>&nbsp;<label for=\"rtl_content\">".__('Right-to-left content')."</label>";
+
+ $include_in_digest = sql_bool_to_bool(db_fetch_result($result, 0, "include_in_digest"));
+
+ if ($include_in_digest) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"include_in_digest\"
+ name=\"include_in_digest\"
+ $checked>&nbsp;<label for=\"include_in_digest\">".__('Include in e-mail digest')."</label>";
+
+
+ $always_display_enclosures = sql_bool_to_bool(db_fetch_result($result, 0, "always_display_enclosures"));
+
+ if ($always_display_enclosures) {
+ $checked = "checked";
+ } else {
+ $checked = "";
+ }
+
+ print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"always_display_enclosures\"
+ name=\"always_display_enclosures\"
+ $checked>&nbsp;<label for=\"always_display_enclosures\">".__('Always display image attachments')."</label>";
+
+
+ $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images"));
+
+ if ($cache_images) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"cache_images\"
+ name=\"cache_images\"
+ $checked>&nbsp;<label for=\"cache_images\">".
+ __('Cache images locally')."</label>";
+
+ $mark_unread_on_update = sql_bool_to_bool(db_fetch_result($result, 0, "mark_unread_on_update"));
+
+ if ($mark_unread_on_update) {
+ $checked = "checked";
+ } else {
+ $checked = "";
+ }
+
+ print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"mark_unread_on_update\"
+ name=\"mark_unread_on_update\"
+ $checked>&nbsp;<label for=\"mark_unread_on_update\">".__('Mark updated articles as unread')."</label>";
+
+ $update_on_checksum_change = sql_bool_to_bool(db_fetch_result($result, 0, "update_on_checksum_change"));
+
+ if ($update_on_checksum_change) {
+ $checked = "checked";
+ } else {
+ $checked = "";
+ }
+
+ print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"update_on_checksum_change\"
+ name=\"update_on_checksum_change\"
+ $checked>&nbsp;<label for=\"update_on_checksum_change\">".__('Mark posts as updated on content change')."</label>";
+
+ print "</div>";
+
+ /* Icon */
+
+ print "<div class=\"dlgSec\">".__("Icon")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print "<iframe name=\"icon_upload_iframe\"
+ style=\"width: 400px; height: 100px; display: none;\"></iframe>";
+
+ print "<form style='display : block' target=\"icon_upload_iframe\"
+ enctype=\"multipart/form-data\" method=\"POST\"
+ action=\"backend.php\">
+ <input id=\"icon_file\" size=\"10\" name=\"icon_file\" type=\"file\">
+ <input type=\"hidden\" name=\"op\" value=\"pref-feeds\">
+ <input type=\"hidden\" name=\"feed_id\" value=\"$feed_id\">
+ <input type=\"hidden\" name=\"method\" value=\"uploadicon\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\"
+ type=\"submit\">".__('Replace')."</button>
+ <button dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\"
+ type=\"submit\">".__('Remove')."</button>
+ </form>";
+
+ print "</div>";
+
+ $title = htmlspecialchars($title, ENT_QUOTES);
+
+ print "<div class='dlgButtons'>
+ <div style=\"float : left\">
+ <button dojoType=\"dijit.form.Button\" onclick='return unsubscribeFeed($feed_id, \"$title\")'>".
+ __('Unsubscribe')."</button>";
+
+ if (PUBSUBHUBBUB_ENABLED) {
+ $pubsub_state = db_fetch_result($result, 0, "pubsub_state");
+ $pubsub_btn_disabled = ($pubsub_state == 2) ? "" : "disabled=\"1\"";
+
+ print "<button dojoType=\"dijit.form.Button\" id=\"pubsubReset_Btn\" $pubsub_btn_disabled
+ onclick='return resetPubSub($feed_id, \"$title\")'>".__('Resubscribe to push updates').
+ "</button>";
+ }
+
+ print "</div>";
+
+ print "<div dojoType=\"dijit.Tooltip\" connectId=\"pubsubReset_Btn\" position=\"below\">".
+ __('Resets PubSubHubbub subscription status for push-enabled feeds.')."</div>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button>
+ <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button>
+ </div>";
+
+ return;
+ }
+
+ function editfeeds() {
+ global $purge_intervals;
+ global $update_intervals;
+ global $update_methods;
+
+ $feed_ids = db_escape_string($_REQUEST["ids"]);
+
+ print "<div class=\"dialogNotice\">" . __("Enable the options you wish to apply using checkboxes on the right:") . "</div>";
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"ids\" value=\"$feed_ids\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-feeds\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"batchEditSave\">";
+
+ print "<div class=\"dlgSec\">".__("Feed")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ /* Title */
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\"
+ disabled=\"1\" style=\"font-size : 16px; width : 20em;\" required=\"1\"
+ name=\"title\" value=\"$title\">";
+
+ $this->batch_edit_cbox("title");
+
+ /* Feed URL */
+
+ print "<br/>";
+
+ print __('URL:') . " ";
+ print "<input dojoType=\"dijit.form.ValidationTextBox\" disabled=\"1\"
+ required=\"1\" regExp='^(http|https)://.*' style=\"width : 20em\"
+ name=\"feed_url\" value=\"$feed_url\">";
+
+ $this->batch_edit_cbox("feed_url");
+
+ /* Category */
+
+ if (get_pref($this->link, 'ENABLE_FEED_CATS')) {
+
+ print "<br/>";
+
+ print __('Place in category:') . " ";
+
+ print_feed_cat_select($this->link, "cat_id", $cat_id,
+ 'disabled="1" dojoType="dijit.form.Select"');
+
+ $this->batch_edit_cbox("cat_id");
+
+ }
+
+ print "</div>";
+
+ print "<div class=\"dlgSec\">".__("Update")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ /* Update Interval */
+
+ print_select_hash("update_interval", $update_interval, $update_intervals,
+ 'disabled="1" dojoType="dijit.form.Select"');
+
+ $this->batch_edit_cbox("update_interval");
+
+ /* Update method */
+
+ print " " . __('using') . " ";
+ print_select_hash("update_method", $update_method, $update_methods,
+ 'disabled="1" dojoType="dijit.form.Select"');
+ $this->batch_edit_cbox("update_method");
+
+ /* Purge intl */
+
+ if (FORCE_ARTICLE_PURGE == 0) {
+
+ print "<br/>";
+
+ print __('Article purging:') . " ";
+
+ print_select_hash("purge_interval", $purge_interval, $purge_intervals,
+ 'disabled="1" dojoType="dijit.form.Select"');
+
+ $this->batch_edit_cbox("purge_interval");
+ }
+
+ print "</div>";
+ print "<div class=\"dlgSec\">".__("Authentication")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print "<input dojoType=\"dijit.form.TextBox\"
+ placeHolder=\"".__("Login")."\" disabled=\"1\"
+ name=\"auth_login\" value=\"$auth_login\">";
+
+ $this->batch_edit_cbox("auth_login");
+
+ print "<br/><input dojoType=\"dijit.form.TextBox\" type=\"password\" name=\"auth_pass\"
+ placeHolder=\"".__("Password")."\" disabled=\"1\"
+ value=\"$auth_pass\">";
+
+ $this->batch_edit_cbox("auth_pass");
+
+ print "</div>";
+ print "<div class=\"dlgSec\">".__("Options")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print "<input disabled=\"1\" type=\"checkbox\" name=\"private\" id=\"private\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"private_l\" class='insensitive' for=\"private\">".__('Hide from Popular feeds')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("private", "private_l");
+
+ print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"rtl_content\" name=\"rtl_content\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label class='insensitive' id=\"rtl_content_l\" for=\"rtl_content\">".__('Right-to-left content')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("rtl_content", "rtl_content_l");
+
+ print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"include_in_digest\"
+ name=\"include_in_digest\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"include_in_digest_l\" class='insensitive' for=\"include_in_digest\">".__('Include in e-mail digest')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("include_in_digest", "include_in_digest_l");
+
+ print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"always_display_enclosures\"
+ name=\"always_display_enclosures\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"always_display_enclosures_l\" class='insensitive' for=\"always_display_enclosures\">".__('Always display image attachments')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l");
+
+ print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"cache_images\"
+ name=\"cache_images\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label class='insensitive' id=\"cache_images_l\"
+ for=\"cache_images\">".
+ __('Cache images locally')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("cache_images", "cache_images_l");
+
+ print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"mark_unread_on_update\"
+ name=\"mark_unread_on_update\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"mark_unread_on_update_l\" class='insensitive' for=\"mark_unread_on_update\">".__('Mark updated articles as unread')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("mark_unread_on_update", "mark_unread_on_update_l");
+
+ print "<br/><input disabled=\"1\" type=\"checkbox\" id=\"update_on_checksum_change\"
+ name=\"update_on_checksum_change\"
+ dojoType=\"dijit.form.CheckBox\">&nbsp;<label id=\"update_on_checksum_change_l\" class='insensitive' for=\"update_on_checksum_change\">".__('Mark posts as updated on content change')."</label>";
+
+ print "&nbsp;"; $this->batch_edit_cbox("update_on_checksum_change", "update_on_checksum_change_l");
+
+ print "</div>";
+
+ print "<div class='dlgButtons'>
+ <button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('feedEditDlg').execute()\">".
+ __('Save')."</button>
+ <button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('feedEditDlg').hide()\">".
+ __('Cancel')."</button>
+ </div>";
+
+ return;
+ }
+
+ function batchEditSave() {
+ return $this->editsaveops(true);
+ }
+
+ function editSave() {
+ return $this->editsaveops(false);
+ }
+
+ function editsaveops($batch) {
+
+ $feed_title = db_escape_string(trim($_POST["title"]));
+ $feed_link = db_escape_string(trim($_POST["feed_url"]));
+ $upd_intl = (int) db_escape_string($_POST["update_interval"]);
+ $purge_intl = (int) db_escape_string($_POST["purge_interval"]);
+ $feed_id = (int) db_escape_string($_POST["id"]); /* editSave */
+ $feed_ids = db_escape_string($_POST["ids"]); /* batchEditSave */
+ $cat_id = (int) db_escape_string($_POST["cat_id"]);
+ $auth_login = db_escape_string(trim($_POST["auth_login"]));
+ $auth_pass = db_escape_string(trim($_POST["auth_pass"]));
+ $private = checkbox_to_sql_bool(db_escape_string($_POST["private"]));
+ $rtl_content = checkbox_to_sql_bool(db_escape_string($_POST["rtl_content"]));
+ $include_in_digest = checkbox_to_sql_bool(
+ db_escape_string($_POST["include_in_digest"]));
+ $cache_images = checkbox_to_sql_bool(
+ db_escape_string($_POST["cache_images"]));
+ $update_method = (int) db_escape_string($_POST["update_method"]);
+
+ $always_display_enclosures = checkbox_to_sql_bool(
+ db_escape_string($_POST["always_display_enclosures"]));
+
+ $mark_unread_on_update = checkbox_to_sql_bool(
+ db_escape_string($_POST["mark_unread_on_update"]));
+
+ $update_on_checksum_change = checkbox_to_sql_bool(
+ db_escape_string($_POST["update_on_checksum_change"]));
+
+ if (get_pref($this->link, 'ENABLE_FEED_CATS')) {
+ if ($cat_id && $cat_id != 0) {
+ $category_qpart = "cat_id = '$cat_id',";
+ $category_qpart_nocomma = "cat_id = '$cat_id'";
+ } else {
+ $category_qpart = 'cat_id = NULL,';
+ $category_qpart_nocomma = 'cat_id = NULL';
+ }
+ } else {
+ $category_qpart = "";
+ $category_qpart_nocomma = "";
+ }
+
+ $cache_images_qpart = "cache_images = $cache_images,";
+
+ if (!$batch) {
+
+ $result = db_query($this->link, "UPDATE ttrss_feeds SET
+ $category_qpart
+ title = '$feed_title', feed_url = '$feed_link',
+ update_interval = '$upd_intl',
+ purge_interval = '$purge_intl',
+ auth_login = '$auth_login',
+ auth_pass = '$auth_pass',
+ private = $private,
+ rtl_content = $rtl_content,
+ $cache_images_qpart
+ include_in_digest = $include_in_digest,
+ always_display_enclosures = $always_display_enclosures,
+ mark_unread_on_update = $mark_unread_on_update,
+ update_on_checksum_change = $update_on_checksum_change,
+ update_method = '$update_method'
+ WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]);
+
+ } else {
+ $feed_data = array();
+
+ foreach (array_keys($_POST) as $k) {
+ if ($k != "op" && $k != "method" && $k != "ids") {
+ $feed_data[$k] = $_POST[$k];
+ }
+ }
+
+ db_query($this->link, "BEGIN");
+
+ foreach (array_keys($feed_data) as $k) {
+
+ $qpart = "";
+
+ switch ($k) {
+ case "title":
+ $qpart = "title = '$feed_title'";
+ break;
+
+ case "feed_url":
+ $qpart = "feed_url = '$feed_link'";
+ break;
+
+ case "update_interval":
+ $qpart = "update_interval = '$upd_intl'";
+ break;
+
+ case "purge_interval":
+ $qpart = "purge_interval = '$purge_intl'";
+ break;
+
+ case "auth_login":
+ $qpart = "auth_login = '$auth_login'";
+ break;
+
+ case "auth_pass":
+ $qpart = "auth_pass = '$auth_pass'";
+ break;
+
+ case "private":
+ $qpart = "private = $private";
+ break;
+
+ case "include_in_digest":
+ $qpart = "include_in_digest = $include_in_digest";
+ break;
+
+ case "always_display_enclosures":
+ $qpart = "always_display_enclosures = $always_display_enclosures";
+ break;
+
+ case "mark_unread_on_update":
+ $qpart = "mark_unread_on_update = $mark_unread_on_update";
+ break;
+
+ case "update_on_checksum_change":
+ $qpart = "update_on_checksum_change = $update_on_checksum_change";
+ break;
+
+ case "cache_images":
+ $qpart = "cache_images = $cache_images";
+ break;
+
+ case "rtl_content":
+ $qpart = "rtl_content = $rtl_content";
+ break;
+
+ case "update_method":
+ $qpart = "update_method = '$update_method'";
+ break;
+
+ case "cat_id":
+ $qpart = $category_qpart_nocomma;
+ break;
+
+ }
+
+ if ($qpart) {
+ db_query($this->link,
+ "UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids)
+ AND owner_uid = " . $_SESSION["uid"]);
+ print "<br/>";
+ }
+ }
+
+ db_query($this->link, "COMMIT");
+ }
+ return;
+ }
+
+ function resetPubSub() {
+
+ $ids = db_escape_string($_REQUEST["ids"]);
+
+ db_query($this->link, "UPDATE ttrss_feeds SET pubsub_state = 0 WHERE id IN ($ids)
+ AND owner_uid = " . $_SESSION["uid"]);
+
+ return;
+ }
+
+ function remove() {
+
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+
+ foreach ($ids as $id) {
+ remove_feed($this->link, $id, $_SESSION["uid"]);
+ }
+
+ return;
+ }
+
+ function clear() {
+ $id = db_escape_string($_REQUEST["id"]);
+ clear_feed_articles($this->link, $id);
+ }
+
+ function rescore() {
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+
+ foreach ($ids as $id) {
+
+ $filters = load_filters($this->link, $id, $_SESSION["uid"], 6);
+
+ $result = db_query($this->link, "SELECT
+ title, content, link, ref_id, author,".
+ SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated
+ FROM
+ ttrss_user_entries, ttrss_entries
+ WHERE ref_id = id AND feed_id = '$id' AND
+ owner_uid = " .$_SESSION['uid']."
+ ");
+
+ $scores = array();
+
+ while ($line = db_fetch_assoc($result)) {
+
+ $tags = get_article_tags($this->link, $line["ref_id"]);
+
+ $article_filters = get_article_filters($filters, $line['title'],
+ $line['content'], $line['link'], strtotime($line['updated']),
+ $line['author'], $tags);
+
+ $new_score = calculate_article_score($article_filters);
+
+ if (!$scores[$new_score]) $scores[$new_score] = array();
+
+ array_push($scores[$new_score], $line['ref_id']);
+ }
+
+ foreach (array_keys($scores) as $s) {
+ if ($s > 1000) {
+ db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s',
+ marked = true WHERE
+ ref_id IN (" . join(',', $scores[$s]) . ")");
+ } else if ($s < -500) {
+ db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s',
+ unread = false WHERE
+ ref_id IN (" . join(',', $scores[$s]) . ")");
+ } else {
+ db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s' WHERE
+ ref_id IN (" . join(',', $scores[$s]) . ")");
+ }
+ }
+ }
+
+ print __("All done.");
+
+ }
+
+ function rescoreAll() {
+
+ $result = db_query($this->link,
+ "SELECT id FROM ttrss_feeds WHERE owner_uid = " . $_SESSION['uid']);
+
+ while ($feed_line = db_fetch_assoc($result)) {
+
+ $id = $feed_line["id"];
+
+ $filters = load_filters($this->link, $id, $_SESSION["uid"], 6);
+
+ $tmp_result = db_query($this->link, "SELECT
+ title, content, link, ref_id, author,".
+ SUBSTRING_FOR_DATE."(updated, 1, 19) AS updated
+ FROM
+ ttrss_user_entries, ttrss_entries
+ WHERE ref_id = id AND feed_id = '$id' AND
+ owner_uid = " .$_SESSION['uid']."
+ ");
+
+ $scores = array();
+
+ while ($line = db_fetch_assoc($tmp_result)) {
+
+ $tags = get_article_tags($this->link, $line["ref_id"]);
+
+ $article_filters = get_article_filters($filters, $line['title'],
+ $line['content'], $line['link'], strtotime($line['updated']),
+ $line['author'], $tags);
+
+ $new_score = calculate_article_score($article_filters);
+
+ if (!$scores[$new_score]) $scores[$new_score] = array();
+
+ array_push($scores[$new_score], $line['ref_id']);
+ }
+
+ foreach (array_keys($scores) as $s) {
+ if ($s > 1000) {
+ db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s',
+ marked = true WHERE
+ ref_id IN (" . join(',', $scores[$s]) . ")");
+ } else {
+ db_query($this->link, "UPDATE ttrss_user_entries SET score = '$s' WHERE
+ ref_id IN (" . join(',', $scores[$s]) . ")");
+ }
+ }
+ }
+
+ print __("All done.");
+
+ }
+
+ function add() {
+ $feed_url = db_escape_string(trim($_REQUEST["feed_url"]));
+ $cat_id = db_escape_string($_REQUEST["cat_id"]);
+ $p_from = db_escape_string($_REQUEST["from"]);
+
+ /* only read authentication information from POST */
+
+ $auth_login = db_escape_string(trim($_POST["auth_login"]));
+ $auth_pass = db_escape_string(trim($_POST["auth_pass"]));
+
+ if ($p_from != 'tt-rss') {
+ header('Content-Type: text/html; charset=utf-8');
+ print "<html>
+ <head>
+ <title>Tiny Tiny RSS</title>
+ <link rel=\"stylesheet\" type=\"text/css\" href=\"utility.css\">
+ <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
+ </head>
+ <body>
+ <img class=\"floatingLogo\" src=\"images/logo_wide.png\"
+ alt=\"Tiny Tiny RSS\"/>
+ <h1>Subscribe to feed...</h1>";
+ }
+
+ $rc = subscribe_to_feed($this->link, $feed_url, $cat_id, $auth_login, $auth_pass);
+
+ switch ($rc) {
+ case 1:
+ print_notice(T_sprintf("Subscribed to <b>%s</b>.", $feed_url));
+ break;
+ case 2:
+ print_error(T_sprintf("Could not subscribe to <b>%s</b>.", $feed_url));
+ break;
+ case 3:
+ print_error(T_sprintf("No feeds found in <b>%s</b>.", $feed_url));
+ break;
+ case 0:
+ print_warning(T_sprintf("Already subscribed to <b>%s</b>.", $feed_url));
+ break;
+ case 4:
+ print_notice(__("Multiple feed URLs found."));
+
+ $feed_urls = get_feeds_from_html($feed_url);
+ break;
+ case 5:
+ print_error(T_sprintf("Could not subscribe to <b>%s</b>.<br>Can't download the Feed URL.", $feed_url));
+ break;
+ }
+
+ if ($p_from != 'tt-rss') {
+
+ if ($feed_urls) {
+
+ print "<form action=\"backend.php\">";
+ print "<input type=\"hidden\" name=\"op\" value=\"pref-feeds\">";
+ print "<input type=\"hidden\" name=\"quiet\" value=\"1\">";
+ print "<input type=\"hidden\" name=\"method\" value=\"add\">";
+
+ print "<select name=\"feed_url\">";
+
+ foreach ($feed_urls as $url => $name) {
+ $url = htmlspecialchars($url);
+ $name = htmlspecialchars($name);
+
+ print "<option value=\"$url\">$name</option>";
+ }
+
+ print "<input type=\"submit\" value=\"".__("Subscribe to selected feed").
+ "\">";
+
+ print "</form>";
+ }
+
+ $tp_uri = get_self_url_prefix() . "/prefs.php";
+ $tt_uri = get_self_url_prefix();
+
+ if ($rc <= 2){
+ $result = db_query($this->link, "SELECT id FROM ttrss_feeds WHERE
+ feed_url = '$feed_url' AND owner_uid = " . $_SESSION["uid"]);
+
+ $feed_id = db_fetch_result($result, 0, "id");
+ } else {
+ $feed_id = 0;
+ }
+ print "<p>";
+
+ if ($feed_id) {
+ print "<form method=\"GET\" style='display: inline'
+ action=\"$tp_uri\">
+ <input type=\"hidden\" name=\"tab\" value=\"feedConfig\">
+ <input type=\"hidden\" name=\"method\" value=\"editFeed\">
+ <input type=\"hidden\" name=\"methodparam\" value=\"$feed_id\">
+ <input type=\"submit\" value=\"".__("Edit subscription options")."\">
+ </form>";
+ }
+
+ print "<form style='display: inline' method=\"GET\" action=\"$tt_uri\">
+ <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
+ </form></p>";
+
+ print "</body></html>";
+ return;
+ }
+ }
+
+ function categorize() {
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+
+ $cat_id = db_escape_string($_REQUEST["cat_id"]);
+
+ if ($cat_id == 0) {
+ $cat_id_qpart = 'NULL';
+ } else {
+ $cat_id_qpart = "'$cat_id'";
+ }
+
+ db_query($this->link, "BEGIN");
+
+ foreach ($ids as $id) {
+
+ db_query($this->link, "UPDATE ttrss_feeds SET cat_id = $cat_id_qpart
+ WHERE id = '$id'
+ AND owner_uid = " . $_SESSION["uid"]);
+
+ }
+
+ db_query($this->link, "COMMIT");
+ }
+
+ function removeCat() {
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+ foreach ($ids as $id) {
+ remove_feed_category($this->link, $id, $_SESSION["uid"]);
+ }
+ }
+
+ function addCat() {
+ $feed_cat = db_escape_string(trim($_REQUEST["cat"]));
+
+ add_feed_category($this->link, $feed_cat);
+ }
+
+ function index() {
+
+ print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">";
+ print "<div id=\"pref-feeds-feeds\" dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Feeds')."\">";
+
+ $result = db_query($this->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) {
+
+ $error_button = "<button dojoType=\"dijit.form.Button\"
+ onclick=\"showFeedsWithErrors()\" id=\"errorButton\">" .
+ __("Feeds with errors") . "</button>";
+ }
+
+ if (DB_TYPE == "pgsql") {
+ $interval_qpart = "NOW() - INTERVAL '3 months'";
+ } else {
+ $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
+ }
+
+ $result = db_query($this->link, "SELECT COUNT(*) AS num_inactive FROM ttrss_feeds WHERE
+ (SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
+ ttrss_entries.id = ref_id AND
+ ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND
+ ttrss_feeds.owner_uid = ".$_SESSION["uid"]);
+
+ $num_inactive = db_fetch_result($result, 0, "num_inactive");
+
+ if ($num_inactive > 0) {
+ $inactive_button = "<button dojoType=\"dijit.form.Button\"
+ onclick=\"showInactiveFeeds()\">" .
+ __("Inactive feeds") . "</button>";
+ }
+
+ $feed_search = db_escape_string($_REQUEST["search"]);
+
+ if (array_key_exists("search", $_REQUEST)) {
+ $_SESSION["prefs_feed_search"] = $feed_search;
+ } else {
+ $feed_search = $_SESSION["prefs_feed_search"];
+ }
+
+ print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
+
+ print "<div region='top' dojoType=\"dijit.Toolbar\">"; #toolbar
+
+ print "<div style='float : right; padding-right : 4px;'>
+ <input dojoType=\"dijit.form.TextBox\" id=\"feed_search\" size=\"20\" type=\"search\"
+ value=\"$feed_search\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"updateFeedList()\">".
+ __('Search')."</button>
+ </div>";
+
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Select')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(true)\"
+ dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(false)\"
+ dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ print "</div></div>";
+
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Feeds')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"quickAddFeed()\"
+ dojoType=\"dijit.MenuItem\">".__('Subscribe to feed')."</div>";
+ print "<div onclick=\"editSelectedFeed()\"
+ dojoType=\"dijit.MenuItem\">".__('Edit selected feeds')."</div>";
+ print "<div onclick=\"resetFeedOrder()\"
+ dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>";
+ print "<div onclick=\"batchSubscribe()\"
+ dojoType=\"dijit.MenuItem\">".__('Batch subscribe')."</div>";
+ print "</div></div>";
+
+ if (get_pref($this->link, 'ENABLE_FEED_CATS')) {
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Categories')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"createCategory()\"
+ dojoType=\"dijit.MenuItem\">".__('Add category')."</div>";
+ print "<div onclick=\"toggleHiddenFeedCats()\"
+ dojoType=\"dijit.MenuItem\">".__('(Un)hide empty categories')."</div>";
+ print "<div onclick=\"resetCatOrder()\"
+ dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>";
+ print "</div></div>";
+
+ }
+
+ print $error_button;
+ print $inactive_button;
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedFeeds()\">"
+ .__('Unsubscribe')."</button dojoType=\"dijit.form.Button\"> ";
+
+ if (defined('_ENABLE_FEED_DEBUGGING')) {
+
+ print "<select id=\"feedActionChooser\" onchange=\"feedActionChange()\">
+ <option value=\"facDefault\" selected>".__('More actions...')."</option>";
+
+ if (FORCE_ARTICLE_PURGE == 0) {
+ print
+ "<option value=\"facPurge\">".__('Manual purge')."</option>";
+ }
+
+ print "
+ <option value=\"facClear\">".__('Clear feed data')."</option>
+ <option value=\"facRescore\">".__('Rescore articles')."</option>";
+
+ print "</select>";
+
+ }
+
+ print "</div>"; # toolbar
+
+ //print '</div>';
+ print '<div dojoType="dijit.layout.ContentPane" region="center">';
+
+ print "<div id=\"feedlistLoading\">
+ <img src='images/indicator_tiny.gif'>".
+ __("Loading, please wait...")."</div>";
+
+ print "<div dojoType=\"fox.PrefFeedStore\" jsId=\"feedStore\"
+ url=\"backend.php?op=pref-feeds&method=getfeedtree\">
+ </div>
+ <div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"feedModel\" store=\"feedStore\"
+ query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Feeds\"
+ childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
+ </div>
+ <div dojoType=\"fox.PrefFeedTree\" id=\"feedTree\"
+ dndController=\"dijit.tree.dndSource\"
+ betweenThreshold=\"5\"
+ model=\"feedModel\" openOnClick=\"false\">
+ <script type=\"dojo/method\" event=\"onClick\" args=\"item\">
+ var id = String(item.id);
+ var bare_id = id.substr(id.indexOf(':')+1);
+
+ if (id.match('FEED:')) {
+ editFeed(bare_id);
+ } else if (id.match('CAT:')) {
+ editCat(bare_id, item);
+ }
+ </script>
+ <script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
+ Element.hide(\"feedlistLoading\");
+ </script>
+ </div>";
+
+# print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedTree\" position=\"below\">
+# ".__('<b>Hint:</b> you can drag feeds and categories around.')."
+# </div>";
+
+ print '</div>';
+ print '</div>';
+
+ print "</div>"; # feeds pane
+
+ print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Import and export')."\">";
+
+ print "<h3>" . __("OPML") . "</h3>";
+
+ print "<p>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . " ";
+
+ print __("Only main settings profile can be migrated using OPML.") . "</p>";
+
+ print "<iframe id=\"upload_iframe\"
+ name=\"upload_iframe\" onload=\"opmlImportComplete(this)\"
+ style=\"width: 400px; height: 100px; display: none;\"></iframe>";
+
+ print "<form name=\"opml_form\" style='display : block' target=\"upload_iframe\"
+ enctype=\"multipart/form-data\" method=\"POST\"
+ action=\"backend.php\">
+ <input id=\"opml_file\" name=\"opml_file\" type=\"file\">&nbsp;
+ <input type=\"hidden\" name=\"op\" value=\"dlg\">
+ <input type=\"hidden\" name=\"method\" value=\"importOpml\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"return opmlImport();\" type=\"submit\">" .
+ __('Import my OPML') . "</button>";
+
+ print "<hr>";
+
+ print "<p>" . __('Filename:') .
+ " <input type=\"text\" id=\"filename\" value=\"TinyTinyRSS.opml\" />&nbsp;" .
+ __('Include settings') . "<input type=\"checkbox\" id=\"settings\" checked=\"1\"/>";
+
+ print "</p><button dojoType=\"dijit.form.Button\"
+ onclick=\"gotoExportOpml(document.opml_form.filename.value, document.opml_form.settings.checked)\" >" .
+ __('Export OPML') . "</button></p></form>";
+
+ print "<hr>";
+
+ print "<p>".__('Your OPML can be published publicly and can be subscribed by anyone who knows the URL below.') . " ";
+
+ print __("Published OPML does not include your Tiny Tiny RSS settings, feeds that require authentication or feeds hidden from Popular feeds.") . "</p>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('pubOPMLUrl')\">".
+ __('Display published OPML URL')."</button> ";
+
+
+ print "<h3>" . __("Article archive") . "</h3>";
+
+ print "<p>" . __("You can export and import your Starred and Archived articles for safekeeping or when migrating between tt-rss instances.") . "</p>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return exportData()\">".
+ __('Export my data')."</button> ";
+
+ print "<hr>";
+
+ print "<iframe id=\"data_upload_iframe\"
+ name=\"data_upload_iframe\" onload=\"dataImportComplete(this)\"
+ style=\"width: 400px; height: 100px; display: none;\"></iframe>";
+
+ print "<form name=\"import_form\" style='display : block' target=\"data_upload_iframe\"
+ enctype=\"multipart/form-data\" method=\"POST\"
+ action=\"backend.php\">
+ <input id=\"export_file\" name=\"export_file\" type=\"file\">&nbsp;
+ <input type=\"hidden\" name=\"op\" value=\"dlg\">
+ <input type=\"hidden\" name=\"method\" value=\"dataimport\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"return importData();\" type=\"submit\">" .
+ __('Import') . "</button>";
+
+
+ print "</div>"; # pane
+
+ if (strpos($_SERVER['HTTP_USER_AGENT'], "Firefox") !== false) {
+
+ print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Firefox integration')."\">";
+
+ print "<p>" . __('This Tiny Tiny RSS site can be used as a Firefox Feed Reader by clicking the link below.') . "</p>";
+
+ print "<p>";
+
+ print "<button onclick='window.navigator.registerContentHandler(" .
+ "\"application/vnd.mozilla.maybe.feed\", " .
+ "\"" . add_feed_url() . "\", " . " \"Tiny Tiny RSS\")'>" .
+ __('Click here to register this site as a feed reader.') .
+ "</button>";
+
+ print "</p>";
+
+ print "</div>"; # pane
+ }
+
+ print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Subscribing using bookmarklet')."\">";
+
+ print "<p>" . __("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.") . "</p>";
+
+ $bm_subscribe_url = str_replace('%s', '', add_feed_url());
+
+ $confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?'));
+
+ $bm_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url'+window.location.href}");
+
+ print "<a href=\"$bm_url\" class='bookmarklet'>" . __('Subscribe in Tiny Tiny RSS'). "</a>";
+
+ print "</div>"; #pane
+
+ print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Published & shared articles / Generated feeds')."\">";
+
+ print "<h3>" . __("Published articles and generated feeds") . "</h3>";
+
+ print "<p>".__('Published articles are exported as a public RSS feed and can be subscribed by anyone who knows the URL specified below.')."</p>";
+
+ $rss_url = '-2::' . htmlspecialchars(get_self_url_prefix() .
+ "/public.php?op=rss&id=-2&view-mode=all_articles");;
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return displayDlg('generatedFeed', '$rss_url')\">".
+ __('Display URL')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearFeedAccessKeys()\">".
+ __('Clear all generated URLs')."</button> ";
+
+ print "<h3>" . __("Articles shared by URL") . "</h3>";
+
+ print "<p>" . __("You can disable all articles shared by unique URLs here.") . "</p>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return clearArticleAccessKeys()\">".
+ __('Unshare all articles')."</button> ";
+
+ print "</div>"; #pane
+
+ if (defined('CONSUMER_KEY') && CONSUMER_KEY != '') {
+
+ print "<div id=\"pref-feeds-twitter\" dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Twitter')."\">";
+
+ $result = db_query($this->link, "SELECT COUNT(*) AS cid FROM ttrss_users
+ WHERE twitter_oauth IS NOT NULL AND twitter_oauth != '' AND
+ id = " . $_SESSION['uid']);
+
+ $is_registered = db_fetch_result($result, 0, "cid") != 0;
+
+ if (!$is_registered) {
+ print_notice(__('Before you can update your Twitter feeds, you must register this instance of Tiny Tiny RSS with Twitter.com.'));
+ } else {
+ print_notice(__('You have been successfully registered with Twitter.com and should be able to access your Twitter feeds.'));
+ }
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"window.location.href = 'twitter.php?op=register'\">".
+ __("Register with Twitter.com")."</button>";
+
+ print " ";
+
+ print "<button dojoType=\"dijit.form.Button\"
+ onclick=\"return clearTwitterCredentials()\">".
+ __("Clear stored credentials")."</button>";
+
+ print "</div>"; # pane
+
+ }
+
+ print "</div>"; #container
+
+ }
+
+ private function feedlist_init_cat($cat_id, $hidden = false) {
+ $obj = array();
+ $cat_id = (int) $cat_id;
+
+ if ($cat_id > 0) {
+ $cat_unread = ccache_find($this->link, $cat_id, $_SESSION["uid"], true);
+ } else if ($cat_id == 0 || $cat_id == -2) {
+ $cat_unread = getCategoryUnread($this->link, $cat_id);
+ }
+
+ $obj['id'] = 'CAT:' . $cat_id;
+ $obj['items'] = array();
+ $obj['name'] = getCategoryTitle($this->link, $cat_id);
+ $obj['type'] = 'category';
+ $obj['unread'] = (int) $cat_unread;
+ $obj['hidden'] = $hidden;
+ $obj['bare_id'] = $cat_id;
+
+ return $obj;
+ }
+
+ private function feedlist_init_feed($feed_id, $title = false, $unread = false, $error = '', $updated = '') {
+ $obj = array();
+ $feed_id = (int) $feed_id;
+
+ if (!$title)
+ $title = getFeedTitle($this->link, $feed_id, false);
+
+ if ($unread === false)
+ $unread = getFeedUnread($this->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;
+ }
+
+}
+?>
diff --git a/classes/pref/filters.php b/classes/pref/filters.php
new file mode 100644
index 000000000..9cd59e96f
--- /dev/null
+++ b/classes/pref/filters.php
@@ -0,0 +1,657 @@
+<?php
+class Pref_Filters extends Handler_Protected {
+
+ function csrf_ignore($method) {
+ $csrf_ignored = array("index", "getfiltertree", "edit");
+
+ return array_search($method, $csrf_ignored) !== false;
+ }
+
+ function filter_test($filter_type, $reg_exp,
+ $action_id, $action_param, $filter_param, $inverse, $feed_id, $cat_id,
+ $cat_filter) {
+
+ $result = db_query($this->link, "SELECT name FROM ttrss_filter_types WHERE
+ id = " . $filter_type);
+ $type_name = db_fetch_result($result, 0, "name");
+
+ $result = db_query($this->link, "SELECT name FROM ttrss_filter_actions WHERE
+ id = " . $action_id);
+ $action_name = db_fetch_result($result, 0, "name");
+
+ $filter["reg_exp"] = $reg_exp;
+ $filter["action"] = $action_name;
+ $filter["type"] = $type_name;
+ $filter["action_param"] = $action_param;
+ $filter["filter_param"] = $filter_param;
+ $filter["inverse"] = $inverse;
+
+ $filters[$type_name] = array($filter);
+
+ if ($feed_id)
+ $feed = $feed_id;
+ else
+ $feed = -4;
+
+ $regexp_valid = preg_match('/' . $filter['reg_exp'] . '/',
+ $filter['reg_exp']) !== FALSE;
+
+ print __("Articles matching this filter:");
+
+ print "<div class=\"filterTestHolder\">";
+ print "<table width=\"100%\" cellspacing=\"0\" id=\"prefErrorFeedList\">";
+
+ if ($regexp_valid) {
+
+ $feed_title = getFeedTitle($this->link, $feed);
+
+ $qfh_ret = queryFeedHeadlines($this->link, $cat_filter ? $cat_id : $feed,
+ 30, "", $cat_filter, false, false,
+ false, "date_entered DESC", 0, $_SESSION["uid"], $filter);
+
+ $result = $qfh_ret[0];
+
+ $articles = array();
+ $found = 0;
+
+ while ($line = db_fetch_assoc($result)) {
+
+ $entry_timestamp = strtotime($line["updated"]);
+ $entry_tags = get_article_tags($this->link, $line["id"], $_SESSION["uid"]);
+
+ $content_preview = truncate_string(
+ strip_tags($line["content_preview"]), 100, '...');
+
+ if ($line["feed_title"])
+ $feed_title = $line["feed_title"];
+
+ print "<tr>";
+
+ print "<td width='5%' align='center'><input
+ dojoType=\"dijit.form.CheckBox\" checked=\"1\"
+ disabled=\"1\" type=\"checkbox\"></td>";
+ print "<td>";
+
+ print $line["title"];
+ print "&nbsp;(";
+ print "<b>" . $feed_title . "</b>";
+ print "):&nbsp;";
+ print "<span class=\"insensitive\">" . $content_preview . "</span>";
+ print " " . mb_substr($line["date_entered"], 0, 16);
+
+ print "</td></tr>";
+
+ $found++;
+ }
+
+ if ($found == 0) {
+ print "<tr><td align='center'>" .
+ __("No articles matching this filter has been found.") . "</td></tr>";
+ }
+ } else {
+ print "<tr><td align='center' class='error'>" .
+ __("Invalid regular expression.") . "</td></tr>";
+
+ }
+
+ print "</table>";
+ print "</div>";
+
+ }
+
+ function getfiltertree() {
+ $root = array();
+ $root['id'] = 'root';
+ $root['name'] = __('Filters');
+ $root['items'] = array();
+
+ $search = $_SESSION["prefs_filter_search"];
+
+ if ($search) $search_qpart = " (LOWER(reg_exp) LIKE LOWER('%$search%')
+ OR LOWER(ttrss_feeds.title) LIKE LOWER('%$search%')
+ OR LOWER(COALESCE(ttrss_feed_categories.title, '".__('Uncategorized')."'))
+ LIKE LOWER('%$search%') AND cat_filter = true) AND ";
+
+ $result = db_query($this->link, "SELECT
+ ttrss_filters.id AS id,reg_exp,
+ ttrss_filter_types.name AS filter_type_name,
+ ttrss_filter_types.description AS filter_type_descr,
+ enabled,
+ inverse,
+ cat_filter,
+ feed_id,
+ ttrss_filters.cat_id,
+ action_id,
+ filter_param,
+ filter_type,
+ ttrss_filter_actions.description AS action_description,
+ ttrss_feeds.title AS feed_title,
+ COALESCE(ttrss_feed_categories.title, '".__('Uncategorized')."') AS cat_title,
+ ttrss_filter_actions.name AS action_name,
+ ttrss_filters.action_param AS action_param
+ FROM
+ ttrss_filter_types,ttrss_filter_actions,ttrss_filters LEFT JOIN
+ ttrss_feeds ON (ttrss_filters.feed_id = ttrss_feeds.id) LEFT JOIN
+ ttrss_feed_categories ON (ttrss_filters.cat_id = ttrss_feed_categories.id)
+ WHERE
+ filter_type = ttrss_filter_types.id AND
+ ttrss_filter_actions.id = action_id AND
+ $search_qpart
+ ttrss_filters.owner_uid = ".$_SESSION["uid"]."
+ ORDER by action_description, reg_exp");
+
+ $cat = false;
+ $cur_action_description = "";
+
+ if (db_num_rows($result) > 0) {
+
+ while ($line = db_fetch_assoc($result)) {
+ if ($cur_action_description != $line['action_description']) {
+
+ if ($cat)
+ array_push($root['items'], $cat);
+
+ $cat = array();
+ $cat['id'] = 'ACTION:' . $line['action_id'];
+ $cat['name'] = $line['action_description'];
+ $cat['items'] = array();
+
+ $cur_action_description = $line['action_description'];
+ }
+
+ if (array_search($line["action_name"],
+ array("score", "tag", "label")) === false) {
+
+ $line["action_param"] = '';
+ } else {
+ if ($line['action_name'] == 'label') {
+
+ $tmp_result = db_query($this->link, "SELECT fg_color, bg_color
+ FROM ttrss_labels2 WHERE caption = '".
+ db_escape_string($line["action_param"])."' AND
+ owner_uid = " . $_SESSION["uid"]);
+
+ if (db_num_rows($tmp_result) != 0) {
+ $fg_color = db_fetch_result($tmp_result, 0, "fg_color");
+ $bg_color = db_fetch_result($tmp_result, 0, "bg_color");
+
+ $tmp = "<span class=\"labelColorIndicator\" style='color : $fg_color; background-color : $bg_color'>&alpha;</span> " . $line['action_param'];
+
+ $line['action_param'] = $tmp;
+ }
+ }
+ }
+
+ $filter = array();
+ $filter['id'] = 'FILTER:' . $line['id'];
+ $filter['bare_id'] = $line['id'];
+ $filter['name'] = $line['reg_exp'];
+ $filter['type'] = $line['filter_type'];
+ $filter['enabled'] = sql_bool_to_bool($line['enabled']);
+ $filter['param'] = $line['action_param'];
+ $filter['inverse'] = sql_bool_to_bool($line['inverse']);
+ $filter['checkbox'] = false;
+
+ if (sql_bool_to_bool($line['cat_filter']))
+ if ($line['cat_id'] != 0) {
+ $filter['feed'] = $line['cat_title'];
+ } else {
+ $filter['feed'] = __('Uncategorized');
+ }
+ else if ($line['feed_id'])
+ $filter['feed'] = $line['feed_title'];
+
+ array_push($cat['items'], $filter);
+ }
+
+ array_push($root['items'], $cat);
+ }
+
+ $fl = array();
+ $fl['identifier'] = 'id';
+ $fl['label'] = 'name';
+ $fl['items'] = array($root);
+
+ print json_encode($fl);
+ return;
+ }
+
+ function edit() {
+
+ $filter_id = db_escape_string($_REQUEST["id"]);
+
+ $result = db_query($this->link,
+ "SELECT * FROM ttrss_filters WHERE id = '$filter_id' AND owner_uid = " . $_SESSION["uid"]);
+
+ $reg_exp = htmlspecialchars(db_fetch_result($result, 0, "reg_exp"));
+ $filter_type = db_fetch_result($result, 0, "filter_type");
+ $feed_id = db_fetch_result($result, 0, "feed_id");
+ $cat_id = db_fetch_result($result, 0, "cat_id");
+ $action_id = db_fetch_result($result, 0, "action_id");
+ $action_param = db_fetch_result($result, 0, "action_param");
+ $filter_param = db_fetch_result($result, 0, "filter_param");
+
+ $enabled = sql_bool_to_bool(db_fetch_result($result, 0, "enabled"));
+ $inverse = sql_bool_to_bool(db_fetch_result($result, 0, "inverse"));
+ $cat_filter = sql_bool_to_bool(db_fetch_result($result, 0, "cat_filter"));
+
+ print "<form id=\"filter_edit_form\" onsubmit='return false'>";
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-filters\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$filter_id\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"csrf_token\" value=\"".$_SESSION['csrf_token']."\">";
+
+ $result = db_query($this->link, "SELECT id,description
+ FROM ttrss_filter_types ORDER BY description");
+
+ $filter_types = array();
+
+ while ($line = db_fetch_assoc($result)) {
+ //array_push($filter_types, $line["description"]);
+ $filter_types[$line["id"]] = __($line["description"]);
+ }
+
+ print "<div class=\"dlgSec\">".__("Match")."</div>";
+
+ print "<div class=\"dlgSecCont\">";
+
+ if ($filter_type != 5) {
+ $date_ops_invisible = 'style="display : none"';
+ }
+
+ print "<span id=\"filterDlg_dateModBox\" $date_ops_invisible>";
+ print __("Date") . " ";
+
+ $filter_params = array(
+ "before" => __("before"),
+ "after" => __("after"));
+
+ print_select_hash("filter_date_modifier", $filter_param,
+ $filter_params, 'dojoType="dijit.form.Select"');
+
+ print "&nbsp;</span>";
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\"
+ required=\"1\"
+ name=\"reg_exp\" style=\"font-size : 16px;\" value=\"$reg_exp\">";
+
+ print "<span id=\"filterDlg_dateChkBox\" $date_ops_invisible>";
+ print "&nbsp;<button dojoType=\"dijit.form.Button\" onclick=\"return filterDlgCheckDate()\">".
+ __('Check it')."</button>";
+ print "</span>";
+
+ print "<hr/> " . __("on field") . " ";
+ print_select_hash("filter_type", $filter_type, $filter_types,
+ 'onchange="filterDlgCheckType(this)" dojoType="dijit.form.Select"');
+
+ print "<hr/>";
+
+ print __("in") . " ";
+
+ $hidden = $cat_filter ? "style='display:none'" : "";
+
+ print "<span id='filterDlg_feeds' $hidden>";
+ print_feed_select($this->link, "feed_id", $feed_id,
+ 'dojoType="dijit.form.FilteringSelect"');
+ print "</span>";
+
+ $hidden = $cat_filter ? "" : "style='display:none'";
+
+ print "<span id='filterDlg_cats' $hidden>";
+ print_feed_cat_select($this->link, "cat_id", $cat_id,
+ 'dojoType="dijit.form.FilteringSelect"');
+ print "</span>";
+
+
+ print "</div>";
+
+ print "<div class=\"dlgSec\">".__("Perform Action")."</div>";
+
+ print "<div class=\"dlgSecCont\">";
+
+ print "<select name=\"action_id\" dojoType=\"dijit.form.Select\"
+ onchange=\"filterDlgCheckAction(this)\">";
+
+ $result = db_query($this->link, "SELECT id,description FROM ttrss_filter_actions
+ ORDER BY name");
+
+ while ($line = db_fetch_assoc($result)) {
+ $is_sel = ($line["id"] == $action_id) ? "selected=\"1\"" : "";
+ printf("<option value='%d' $is_sel>%s</option>", $line["id"], __($line["description"]));
+ }
+
+ print "</select>";
+
+ $param_hidden = ($action_id == 4 || $action_id == 6 || $action_id == 7) ? "" : "display : none";
+
+ print "<span id=\"filterDlg_paramBox\" style=\"$param_hidden\">";
+ print " " . __("with parameters:") . " ";
+
+ $param_int_hidden = ($action_id != 7) ? "" : "display : none";
+
+ print "<input style=\"$param_int_hidden\"
+ dojoType=\"dijit.form.TextBox\" id=\"filterDlg_actionParam\"
+ name=\"action_param\" value=\"$action_param\">";
+
+ $param_int_hidden = ($action_id == 7) ? "" : "display : none";
+
+ print_label_select($this->link, "action_param_label", $action_param,
+ "style=\"$param_int_hidden\"" .
+ 'id="filterDlg_actionParamLabel" dojoType="dijit.form.Select"');
+
+ print "</span>";
+
+ print "&nbsp;"; // tiny layout hack
+
+ print "</div>";
+
+ print "<div class=\"dlgSec\">".__("Options")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print "<div style=\"line-height : 100%\">";
+
+ if ($enabled) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"enabled\" id=\"enabled\" $checked>
+ <label for=\"enabled\">".__('Enabled')."</label><hr/>";
+
+ if ($inverse) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\" $checked>
+ <label for=\"inverse\">".__('Inverse match')."</label><hr/>";
+
+ if ($cat_filter) {
+ $checked = "checked=\"1\"";
+ } else {
+ $checked = "";
+ }
+
+ print "<input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"cat_filter\" id=\"cat_filter\" onchange=\"filterDlgCheckCat(this)\" $checked>
+ <label for=\"cat_filter\">".__('Apply to category')."</label><hr/>";
+
+ print "</div>";
+ print "</div>";
+
+ print "<div class=\"dlgButtons\">";
+
+ print "<div style=\"float : left\">";
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').removeFilter()\">".
+ __('Remove')."</button>";
+ print "</div>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').test()\">".
+ __('Test')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').execute()\">".
+ __('Save')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').hide()\">".
+ __('Cancel')."</button>";
+
+ print "</div>";
+ }
+
+ function editSave() {
+
+ $savemode = db_escape_string($_REQUEST["savemode"]);
+ $reg_exp = db_escape_string(trim($_REQUEST["reg_exp"]));
+ $filter_type = db_escape_string(trim($_REQUEST["filter_type"]));
+ $filter_id = db_escape_string($_REQUEST["id"]);
+ $feed_id = db_escape_string($_REQUEST["feed_id"]);
+ $action_id = db_escape_string($_REQUEST["action_id"]);
+ $action_param = db_escape_string($_REQUEST["action_param"]);
+ $action_param_label = db_escape_string($_REQUEST["action_param_label"]);
+ $enabled = checkbox_to_sql_bool(db_escape_string($_REQUEST["enabled"]));
+ $inverse = checkbox_to_sql_bool(db_escape_string($_REQUEST["inverse"]));
+ $cat_filter = checkbox_to_sql_bool(db_escape_string($_REQUEST["cat_filter"]));
+ $cat_id = db_escape_string($_REQUEST['cat_id']);
+
+ # for the time being, no other filters use params anyway...
+ $filter_param = db_escape_string($_REQUEST["filter_date_modifier"]);
+
+ if (!$feed_id) {
+ $feed_id = 'NULL';
+ } else {
+ $feed_id = sprintf("'%s'", db_escape_string($feed_id));
+ }
+
+ if (!$cat_id) {
+ $cat_id = 'NULL';
+ } else {
+ $cat_id = sprintf("'%d'", db_escape_string($cat_id));
+ }
+
+ /* When processing 'assign label' filters, action_param_label dropbox
+ * overrides action_param */
+
+ if ($action_id == 7) {
+ $action_param = $action_param_label;
+ }
+
+ if ($action_id == 6) {
+ $action_param = (int) str_replace("+", "", $action_param);
+ }
+
+ if ($savemode != "test") {
+ $result = db_query($this->link, "UPDATE ttrss_filters SET
+ reg_exp = '$reg_exp',
+ feed_id = $feed_id,
+ cat_id = $cat_id,
+ action_id = '$action_id',
+ filter_type = '$filter_type',
+ enabled = $enabled,
+ inverse = $inverse,
+ cat_filter = $cat_filter,
+ action_param = '$action_param',
+ filter_param = '$filter_param'
+ WHERE id = '$filter_id' AND owner_uid = " . $_SESSION["uid"]);
+ } else {
+
+ $this->filter_test($filter_type, $reg_exp,
+ $action_id, $action_param, $filter_param, sql_bool_to_bool($inverse),
+ (int) $_REQUEST["feed_id"], (int) $_REQUEST['cat_id'],
+ sql_bool_to_bool($cat_filter));
+
+ print "<div align='center'>";
+ print "<button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('filterTestDlg').hide()\">".
+ __('Close this window')."</button>";
+ print "</div>";
+
+ }
+ }
+
+ function remove() {
+
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+
+ foreach ($ids as $id) {
+ db_query($this->link, "DELETE FROM ttrss_filters WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]);
+ }
+ }
+
+ function add() {
+
+ $savemode = db_escape_string($_REQUEST["savemode"]);
+ $regexp = db_escape_string(trim($_REQUEST["reg_exp"]));
+ $filter_type = db_escape_string(trim($_REQUEST["filter_type"]));
+ $feed_id = db_escape_string($_REQUEST["feed_id"]);
+ $cat_id = db_escape_string($_REQUEST["cat_id"]);
+ $action_id = db_escape_string($_REQUEST["action_id"]);
+ $action_param = db_escape_string($_REQUEST["action_param"]);
+ $action_param_label = db_escape_string($_REQUEST["action_param_label"]);
+ $inverse = checkbox_to_sql_bool(db_escape_string($_REQUEST["inverse"]));
+ $cat_filter = checkbox_to_sql_bool(db_escape_string($_REQUEST["cat_filter"]));
+
+ # for the time being, no other filters use params anyway...
+ $filter_param = db_escape_string($_REQUEST["filter_date_modifier"]);
+
+ if (!$regexp) return;
+
+ if (!$feed_id) {
+ $feed_id = 'NULL';
+ } else {
+ $feed_id = sprintf("'%s'", db_escape_string($feed_id));
+ }
+
+ if (!$cat_id) {
+ $cat_id = 'NULL';
+ } else {
+ $cat_id = sprintf("'%d'", db_escape_string($cat_id));
+ }
+
+ /* When processing 'assign label' filters, action_param_label dropbox
+ * overrides action_param */
+
+ if ($action_id == 7) {
+ $action_param = $action_param_label;
+ }
+
+ if ($action_id == 6) {
+ $action_param = (int) str_replace("+", "", $action_param);
+ }
+
+ if ($savemode != "test") {
+ $result = db_query($this->link,
+ "INSERT INTO ttrss_filters (reg_exp,filter_type,owner_uid,feed_id,
+ action_id, action_param, inverse, filter_param, cat_id, cat_filter)
+ VALUES
+ ('$regexp', '$filter_type','".$_SESSION["uid"]."',
+ $feed_id, '$action_id', '$action_param', $inverse,
+ '$filter_param', $cat_id, $cat_filter)");
+
+ if (db_affected_rows($this->link, $result) != 0) {
+ print T_sprintf("Created filter <b>%s</b>", htmlspecialchars($regexp));
+ }
+
+ } else {
+
+ $this->filter_test($filter_type, $regexp,
+ $action_id, $action_param, $filter_param, sql_bool_to_bool($inverse),
+ (int) $_REQUEST["feed_id"], (int) $_REQUEST['cat_id'],
+ sql_bool_to_bool($cat_filter));
+
+ print "<div align='center'>";
+ print "<button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('filterTestDlg').hide()\">".
+ __('Close this window')."</button>";
+ print "</div>";
+
+ }
+ }
+
+ function index() {
+
+ $sort = db_escape_string($_REQUEST["sort"]);
+
+ if (!$sort || $sort == "undefined") {
+ $sort = "reg_exp";
+ }
+
+ $result = db_query($this->link, "SELECT id,description
+ FROM ttrss_filter_types ORDER BY description");
+
+ $filter_types = array();
+
+ while ($line = db_fetch_assoc($result)) {
+ //array_push($filter_types, $line["description"]);
+ $filter_types[$line["id"]] = $line["description"];
+ }
+
+
+ $filter_search = db_escape_string($_REQUEST["search"]);
+
+ if (array_key_exists("search", $_REQUEST)) {
+ $_SESSION["prefs_filter_search"] = $filter_search;
+ } else {
+ $filter_search = $_SESSION["prefs_filter_search"];
+ }
+
+ print "<div id=\"pref-filter-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
+ print "<div id=\"pref-filter-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
+ print "<div id=\"pref-filter-toolbar\" dojoType=\"dijit.Toolbar\">";
+
+ $filter_search = db_escape_string($_REQUEST["search"]);
+
+ if (array_key_exists("search", $_REQUEST)) {
+ $_SESSION["prefs_filter_search"] = $filter_search;
+ } else {
+ $filter_search = $_SESSION["prefs_filter_search"];
+ }
+
+ print "<div style='float : right; padding-right : 4px;'>
+ <input dojoType=\"dijit.form.TextBox\" id=\"filter_search\" size=\"20\" type=\"search\"
+ value=\"$filter_search\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"updateFilterList()\">".
+ __('Search')."</button>
+ </div>";
+
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Select')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"dijit.byId('filterTree').model.setAllChecked(true)\"
+ dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ print "<div onclick=\"dijit.byId('filterTree').model.setAllChecked(false)\"
+ dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ print "</div></div>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return quickAddFilter()\">".
+ __('Create filter')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return editSelectedFilter()\">".
+ __('Edit')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return removeSelectedFilters()\">".
+ __('Remove')."</button> ";
+
+ if (defined('_ENABLE_FEED_DEBUGGING')) {
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"rescore_all_feeds()\">".
+ __('Rescore articles')."</button> ";
+ }
+
+ print "</div>"; # toolbar
+ print "</div>"; # toolbar-frame
+ print "<div id=\"pref-filter-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">";
+
+ print "<div id=\"filterlistLoading\">
+ <img src='images/indicator_tiny.gif'>".
+ __("Loading, please wait...")."</div>";
+
+ print "<div dojoType=\"dojo.data.ItemFileWriteStore\" jsId=\"filterStore\"
+ url=\"backend.php?op=pref-filters&method=getfiltertree\">
+ </div>
+ <div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"filterModel\" store=\"filterStore\"
+ query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Feeds\"
+ childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
+ </div>
+ <div dojoType=\"fox.PrefFilterTree\" id=\"filterTree\"
+ model=\"filterModel\" openOnClick=\"true\">
+ <script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
+ Element.hide(\"filterlistLoading\");
+ </script>
+ <script type=\"dojo/method\" event=\"onClick\" args=\"item\">
+ var id = String(item.id);
+ var bare_id = id.substr(id.indexOf(':')+1);
+
+ if (id.match('FILTER:')) {
+ editFilter(bare_id);
+ }
+ </script>
+
+ </div>";
+
+ print "</div>"; #pane
+ print "</div>"; #container
+
+ }
+}
+?>
diff --git a/classes/pref/instances.php b/classes/pref/instances.php
new file mode 100644
index 000000000..c017ee9d2
--- /dev/null
+++ b/classes/pref/instances.php
@@ -0,0 +1,210 @@
+<?php
+class Pref_Instances extends Handler_Protected {
+
+ function csrf_ignore($method) {
+ $csrf_ignored = array("index", "edit");
+
+ return array_search($method, $csrf_ignored) !== false;
+ }
+
+ function before($method) {
+ if (parent::before($method)) {
+ if ($_SESSION["access_level"] < 10) {
+ print __("Your access level is insufficient to open this tab.");
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ function remove() {
+ $ids = db_escape_string($_REQUEST['ids']);
+
+ db_query($this->link, "DELETE FROM ttrss_linked_instances WHERE
+ id IN ($ids)");
+ }
+
+ function add() {
+ $id = db_escape_string($_REQUEST["id"]);
+ $access_url = db_escape_string($_REQUEST["access_url"]);
+ $access_key = db_escape_string($_REQUEST["access_key"]);
+
+ db_query($this->link, "BEGIN");
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_linked_instances
+ WHERE access_url = '$access_url'");
+
+ if (db_num_rows($result) == 0) {
+ db_query($this->link, "INSERT INTO ttrss_linked_instances
+ (access_url, access_key, last_connected, last_status_in, last_status_out)
+ VALUES
+ ('$access_url', '$access_key', '1970-01-01', -1, -1)");
+
+ }
+
+ db_query($this->link, "COMMIT");
+ }
+
+ function edit() {
+ $id = db_escape_string($_REQUEST["id"]);
+
+ $result = db_query($this->link, "SELECT * FROM ttrss_linked_instances WHERE
+ id = '$id'");
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$id\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-instances\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"editSave\">";
+
+ print "<div class=\"dlgSec\">".__("Instance")."</div>";
+
+ print "<div class=\"dlgSecCont\">";
+
+ /* URL */
+
+ $access_url = htmlspecialchars(db_fetch_result($result, 0, "access_url"));
+
+ print __("URL:") . " ";
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ placeHolder=\"".__("Instance URL")."\"
+ regExp='^(http|https)://.*'
+ style=\"font-size : 16px; width: 20em\" name=\"access_url\"
+ value=\"$access_url\">";
+
+ print "<hr/>";
+
+ $access_key = htmlspecialchars(db_fetch_result($result, 0, "access_key"));
+
+ /* Access key */
+
+ print __("Access key:") . " ";
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\" required=\"1\"
+ placeHolder=\"".__("Access key")."\" regExp='\w{40}'
+ style=\"width: 20em\" name=\"access_key\" id=\"instance_edit_key\"
+ value=\"$access_key\">";
+
+ print "<p class='insensitive'>" . __("Use one access key for both linked instances.");
+
+ print "</div>";
+
+ print "<div class=\"dlgButtons\">
+ <div style='float : left'>
+ <button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('instanceEditDlg').regenKey()\">".
+ __('Generate new key')."</button>
+ </div>
+ <button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('instanceEditDlg').execute()\">".
+ __('Save')."</button>
+ <button dojoType=\"dijit.form.Button\"
+ onclick=\"return dijit.byId('instanceEditDlg').hide()\"\">".
+ __('Cancel')."</button></div>";
+
+ }
+
+ function editSave() {
+ $id = db_escape_string($_REQUEST["id"]);
+ $access_url = db_escape_string($_REQUEST["access_url"]);
+ $access_key = db_escape_string($_REQUEST["access_key"]);
+
+ db_query($this->link, "UPDATE ttrss_linked_instances SET
+ access_key = '$access_key', access_url = '$access_url',
+ last_connected = '1970-01-01'
+ WHERE id = '$id'");
+
+ }
+
+ function index() {
+
+ if (!function_exists('curl_init')) {
+ print "<div style='padding : 1em'>";
+ print_error("This functionality requires CURL functions. Please enable CURL in your PHP configuration (you might also want to disable open_basedir in php.ini) and reload this page.");
+ print "</div>";
+ }
+
+ print "<div id=\"pref-instance-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
+ print "<div id=\"pref-instance-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
+
+ print "<div id=\"pref-instance-toolbar\" dojoType=\"dijit.Toolbar\">";
+
+ $sort = db_escape_string($_REQUEST["sort"]);
+
+ if (!$sort || $sort == "undefined") {
+ $sort = "access_url";
+ }
+
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Select')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"selectTableRows('prefInstanceList', 'all')\"
+ dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ print "<div onclick=\"selectTableRows('prefInstanceList', 'none')\"
+ dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ print "</div></div>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"addInstance()\">".__('Link instance')."</button>";
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"editSelectedInstance()\">".__('Edit')."</button>";
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedInstances()\">".__('Remove')."</button>";
+
+ print "</div>"; #toolbar
+
+ $result = db_query($this->link, "SELECT *,
+ (SELECT COUNT(*) FROM ttrss_linked_feeds
+ WHERE instance_id = ttrss_linked_instances.id) AS num_feeds
+ FROM ttrss_linked_instances
+ ORDER BY $sort");
+
+ print "<p class=\"insensitive\" style='margin-left : 1em;'>" . __("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:");
+
+ print " <a href=\"#\" onclick=\"alert('".htmlspecialchars(get_self_url_prefix())."')\">(display url)</a>";
+
+ print "<p><table width='100%' id='prefInstanceList' class='prefInstanceList' cellspacing='0'>";
+
+ print "<tr class=\"title\">
+ <td align='center' width=\"5%\">&nbsp;</td>
+ <td width=''><a href=\"#\" onclick=\"updateInstanceList('access_url')\">".__('Instance URL')."</a></td>
+ <td width='20%'><a href=\"#\" onclick=\"updateInstanceList('access_key')\">".__('Access key')."</a></td>
+ <td width='10%'><a href=\"#\" onclick=\"updateUsersList('last_connected')\">".__('Last connected')."</a></td>
+ <td width='10%'><a href=\"#\" onclick=\"updateUsersList('num_feeds')\">".__('Stored feeds')."</a></td>
+ </tr>";
+
+ $lnum = 0;
+
+ while ($line = db_fetch_assoc($result)) {
+ $class = ($lnum % 2) ? "even" : "odd";
+
+ $id = $line['id'];
+ $this_row_id = "id=\"LIRR-$id\"";
+
+ $line["last_connected"] = make_local_datetime($this->link, $line["last_connected"], false);
+
+ print "<tr class=\"$class\" $this_row_id>";
+
+ print "<td align='center'><input onclick='toggleSelectRow(this);'
+ type=\"checkbox\" id=\"LICHK-$id\"></td>";
+
+ $onclick = "onclick='editInstance($id, event)' title='".__('Click to edit')."'";
+
+ $access_key = mb_substr($line['access_key'], 0, 4) . '...' .
+ mb_substr($line['access_key'], -4);
+
+ print "<td $onclick>" . htmlspecialchars($line['access_url']) . "</td>";
+ print "<td $onclick>" . htmlspecialchars($access_key) . "</td>";
+ print "<td $onclick>" . htmlspecialchars($line['last_connected']) . "</td>";
+ print "<td $onclick>" . htmlspecialchars($line['num_feeds']) . "</td>";
+
+ print "</tr>";
+
+ ++$lnum;
+ }
+
+ print "</table>";
+
+ print "</div>"; #pane
+ print "</div>"; #container
+
+ }
+}
+?>
diff --git a/classes/pref/labels.php b/classes/pref/labels.php
new file mode 100644
index 000000000..317f92738
--- /dev/null
+++ b/classes/pref/labels.php
@@ -0,0 +1,326 @@
+<?php
+class Pref_Labels extends Handler_Protected {
+
+ function csrf_ignore($method) {
+ $csrf_ignored = array("index", "getlabeltree", "edit");
+
+ return array_search($method, $csrf_ignored) !== false;
+ }
+
+ function edit() {
+ $label_id = db_escape_string($_REQUEST['id']);
+
+ $result = db_query($this->link, "SELECT * FROM ttrss_labels2 WHERE
+ id = '$label_id' AND owner_uid = " . $_SESSION["uid"]);
+
+ $line = db_fetch_assoc($result);
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"id\" value=\"$label_id\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-labels\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"save\">";
+
+ print "<div class=\"dlgSec\">".__("Caption")."</div>";
+
+ print "<div class=\"dlgSecCont\">";
+
+ $fg_color = $line['fg_color'];
+ $bg_color = $line['bg_color'];
+
+ print "<span class=\"labelColorIndicator\" id=\"label-editor-indicator\" style='color : $fg_color; background-color : $bg_color; margin-bottom : 4px; margin-right : 4px'>&alpha;</span>";
+
+ print "<input style=\"font-size : 16px\" name=\"caption\"
+ dojoType=\"dijit.form.ValidationTextBox\"
+ required=\"true\"
+ value=\"".htmlspecialchars($line['caption'])."\">";
+
+ print "</div>";
+ print "<div class=\"dlgSec\">" . __("Colors") . "</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print "<table cellspacing=\"0\">";
+
+ print "<tr><td>".__("Foreground:")."</td><td>".__("Background:").
+ "</td></tr>";
+
+ print "<tr><td style='padding-right : 10px'>";
+
+ print "<input dojoType=\"dijit.form.TextBox\"
+ style=\"display : none\" id=\"labelEdit_fgColor\"
+ name=\"fg_color\" value=\"$fg_color\">";
+ print "<input dojoType=\"dijit.form.TextBox\"
+ style=\"display : none\" id=\"labelEdit_bgColor\"
+ name=\"bg_color\" value=\"$bg_color\">";
+
+ print "<div dojoType=\"dijit.ColorPalette\">
+ <script type=\"dojo/method\" event=\"onChange\" args=\"fg_color\">
+ dijit.byId(\"labelEdit_fgColor\").attr('value', fg_color);
+ $('label-editor-indicator').setStyle({color: fg_color});
+ </script>
+ </div>";
+ print "</div>";
+
+ print "</td><td>";
+
+ print "<div dojoType=\"dijit.ColorPalette\">
+ <script type=\"dojo/method\" event=\"onChange\" args=\"bg_color\">
+ dijit.byId(\"labelEdit_bgColor\").attr('value', bg_color);
+ $('label-editor-indicator').setStyle({backgroundColor: bg_color});
+ </script>
+ </div>";
+ print "</div>";
+
+ print "</td></tr></table>";
+ print "</div>";
+
+# print "</form>";
+
+ print "<div class=\"dlgButtons\">";
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').execute()\">".
+ __('Save')."</button>";
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('labelEditDlg').hide()\">".
+ __('Cancel')."</button>";
+ print "</div>";
+
+ return;
+ }
+
+ function getlabeltree() {
+ $root = array();
+ $root['id'] = 'root';
+ $root['name'] = __('Labels');
+ $root['items'] = array();
+
+ $result = db_query($this->link, "SELECT *
+ FROM ttrss_labels2
+ WHERE owner_uid = ".$_SESSION["uid"]."
+ ORDER BY caption");
+
+ while ($line = db_fetch_assoc($result)) {
+ $label = array();
+ $label['id'] = 'LABEL:' . $line['id'];
+ $label['bare_id'] = $line['id'];
+ $label['name'] = $line['caption'];
+ $label['fg_color'] = $line['fg_color'];
+ $label['bg_color'] = $line['bg_color'];
+ $label['type'] = 'label';
+ $label['checkbox'] = false;
+
+ array_push($root['items'], $label);
+ }
+
+ $fl = array();
+ $fl['identifier'] = 'id';
+ $fl['label'] = 'name';
+ $fl['items'] = array($root);
+
+ print json_encode($fl);
+ return;
+ }
+
+ function colorset() {
+ $kind = db_escape_string($_REQUEST["kind"]);
+ $ids = split(',', db_escape_string($_REQUEST["ids"]));
+ $color = db_escape_string($_REQUEST["color"]);
+ $fg = db_escape_string($_REQUEST["fg"]);
+ $bg = db_escape_string($_REQUEST["bg"]);
+
+ foreach ($ids as $id) {
+
+ if ($kind == "fg" || $kind == "bg") {
+ db_query($this->link, "UPDATE ttrss_labels2 SET
+ ${kind}_color = '$color' WHERE id = '$id'
+ AND owner_uid = " . $_SESSION["uid"]);
+ } else {
+ db_query($this->link, "UPDATE ttrss_labels2 SET
+ fg_color = '$fg', bg_color = '$bg' WHERE id = '$id'
+ AND owner_uid = " . $_SESSION["uid"]);
+ }
+
+ $caption = db_escape_string(label_find_caption($this->link, $id, $_SESSION["uid"]));
+
+ /* Remove cached data */
+
+ db_query($this->link, "UPDATE ttrss_user_entries SET label_cache = ''
+ WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]);
+
+ }
+
+ return;
+ }
+
+ function colorreset() {
+ $ids = split(',', db_escape_string($_REQUEST["ids"]));
+
+ foreach ($ids as $id) {
+ db_query($this->link, "UPDATE ttrss_labels2 SET
+ fg_color = '', bg_color = '' WHERE id = '$id'
+ AND owner_uid = " . $_SESSION["uid"]);
+
+ $caption = db_escape_string(label_find_caption($this->link, $id, $_SESSION["uid"]));
+
+ /* Remove cached data */
+
+ db_query($this->link, "UPDATE ttrss_user_entries SET label_cache = ''
+ WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $_SESSION["uid"]);
+ }
+
+ }
+
+ function save() {
+
+ $id = db_escape_string($_REQUEST["id"]);
+ $caption = db_escape_string(trim($_REQUEST["caption"]));
+
+ db_query($this->link, "BEGIN");
+
+ $result = db_query($this->link, "SELECT caption FROM ttrss_labels2
+ WHERE id = '$id' AND owner_uid = ". $_SESSION["uid"]);
+
+ if (db_num_rows($result) != 0) {
+ $old_caption = db_fetch_result($result, 0, "caption");
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_labels2
+ WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]);
+
+ if (db_num_rows($result) == 0) {
+ if ($caption) {
+ $result = db_query($this->link, "UPDATE ttrss_labels2 SET
+ caption = '$caption' WHERE id = '$id' AND
+ owner_uid = " . $_SESSION["uid"]);
+
+ /* Update filters that reference label being renamed */
+
+ $old_caption = db_escape_string($old_caption);
+
+ db_query($this->link, "UPDATE ttrss_filters SET
+ action_param = '$caption' WHERE action_param = '$old_caption'
+ AND action_id = 7
+ AND owner_uid = " . $_SESSION["uid"]);
+
+ print $_REQUEST["value"];
+ } else {
+ print $old_caption;
+ }
+ } else {
+ print $old_caption;
+ }
+ }
+
+ db_query($this->link, "COMMIT");
+
+ return;
+ }
+
+ function remove() {
+
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+
+ foreach ($ids as $id) {
+ label_remove($this->link, $id, $_SESSION["uid"]);
+ }
+
+ }
+
+ function add() {
+ $caption = db_escape_string($_REQUEST["caption"]);
+ $output = db_escape_string($_REQUEST["output"]);
+
+ if ($caption) {
+
+ if (label_create($this->link, $caption)) {
+ if (!$output) {
+ print T_sprintf("Created label <b>%s</b>", htmlspecialchars($caption));
+ }
+ }
+
+ if ($output == "select") {
+ header("Content-Type: text/xml");
+
+ print "<rpc-reply><payload>";
+
+ print_label_select($this->link, "select_label",
+ $caption, "");
+
+ print "</payload></rpc-reply>";
+ }
+ }
+
+ return;
+ }
+
+ function index() {
+
+ $sort = db_escape_string($_REQUEST["sort"]);
+
+ if (!$sort || $sort == "undefined") {
+ $sort = "caption";
+ }
+
+ $label_search = db_escape_string($_REQUEST["search"]);
+
+ if (array_key_exists("search", $_REQUEST)) {
+ $_SESSION["prefs_label_search"] = $label_search;
+ } else {
+ $label_search = $_SESSION["prefs_label_search"];
+ }
+
+ print "<div id=\"pref-label-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
+ print "<div id=\"pref-label-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
+ print "<div id=\"pref-label-toolbar\" dojoType=\"dijit.Toolbar\">";
+
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Select')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"dijit.byId('labelTree').model.setAllChecked(true)\"
+ dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ print "<div onclick=\"dijit.byId('labelTree').model.setAllChecked(false)\"
+ dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ print "</div></div>";
+
+ print"<button dojoType=\"dijit.form.Button\" onclick=\"return addLabel()\">".
+ __('Create label')."</button dojoType=\"dijit.form.Button\"> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"removeSelectedLabels()\">".
+ __('Remove')."</button dojoType=\"dijit.form.Button\"> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"labelColorReset()\">".
+ __('Clear colors')."</button dojoType=\"dijit.form.Button\">";
+
+
+ print "</div>"; #toolbar
+ print "</div>"; #pane
+ print "<div id=\"pref-label-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">";
+
+ print "<div id=\"labellistLoading\">
+ <img src='images/indicator_tiny.gif'>".
+ __("Loading, please wait...")."</div>";
+
+ print "<div dojoType=\"dojo.data.ItemFileWriteStore\" jsId=\"labelStore\"
+ url=\"backend.php?op=pref-labels&method=getlabeltree\">
+ </div>
+ <div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"labelModel\" store=\"labelStore\"
+ query=\"{id:'root'}\" rootId=\"root\"
+ childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
+ </div>
+ <div dojoType=\"fox.PrefLabelTree\" id=\"labelTree\"
+ model=\"labelModel\" openOnClick=\"true\">
+ <script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
+ Element.hide(\"labellistLoading\");
+ </script>
+ <script type=\"dojo/method\" event=\"onClick\" args=\"item\">
+ var id = String(item.id);
+ var bare_id = id.substr(id.indexOf(':')+1);
+
+ if (id.match('LABEL:')) {
+ editLabel(bare_id);
+ }
+ </script>
+ </div>";
+
+ print "</div>"; #pane
+ print "</div>"; #container
+
+ }
+}
+
+?>
diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php
new file mode 100644
index 000000000..ff8a17cdb
--- /dev/null
+++ b/classes/pref/prefs.php
@@ -0,0 +1,499 @@
+<?php
+class Pref_Prefs extends Handler_Protected {
+
+ function csrf_ignore($method) {
+ $csrf_ignored = array("index");
+
+ return array_search($method, $csrf_ignored) !== false;
+ }
+
+ function changepassword() {
+
+ $old_pw = $_POST["old_password"];
+ $new_pw = $_POST["new_password"];
+ $con_pw = $_POST["confirm_password"];
+
+ if ($old_pw == "") {
+ print "ERROR: ".__("Old password cannot be blank.");
+ return;
+ }
+
+ if ($new_pw == "") {
+ print "ERROR: ".__("New password cannot be blank.");
+ return;
+ }
+
+ if ($new_pw != $con_pw) {
+ print "ERROR: ".__("Entered passwords do not match.");
+ return;
+ }
+
+ $module_class = "auth_" . $_SESSION["auth_module"];
+ $authenticator = new $module_class($this->link);
+
+ if (method_exists($authenticator, "change_password")) {
+ print $authenticator->change_password($_SESSION["uid"], $old_pw, $new_pw);
+ } else {
+ print "ERROR: ".__("Function not supported by authentication module.");
+ }
+ }
+
+ function saveconfig() {
+
+ $_SESSION["prefs_cache"] = false;
+
+ $orig_theme = get_pref($this->link, "_THEME_ID");
+
+ foreach (array_keys($_POST) as $pref_name) {
+
+ $pref_name = db_escape_string($pref_name);
+ $value = db_escape_string($_POST[$pref_name]);
+
+ if ($pref_name == 'DIGEST_PREFERRED_TIME') {
+ if (get_pref($this->link, 'DIGEST_PREFERRED_TIME') != $value) {
+
+ db_query($this->link, "UPDATE ttrss_users SET
+ last_digest_sent = NULL WHERE id = " . $_SESSION['uid']);
+
+ }
+ }
+
+ set_pref($this->link, $pref_name, $value);
+
+ }
+
+ if ($orig_theme != get_pref($this->link, "_THEME_ID")) {
+ print "PREFS_THEME_CHANGED";
+ } else {
+ print __("The configuration was saved.");
+ }
+ }
+
+ function getHelp() {
+
+ $pref_name = db_escape_string($_REQUEST["pn"]);
+
+ $result = db_query($this->link, "SELECT help_text FROM ttrss_prefs
+ WHERE pref_name = '$pref_name'");
+
+ if (db_num_rows($result) > 0) {
+ $help_text = db_fetch_result($result, 0, "help_text");
+ print $help_text;
+ } else {
+ printf(__("Unknown option: %s"), $pref_name);
+ }
+ }
+
+ function changeemail() {
+
+ $email = db_escape_string($_POST["email"]);
+ $full_name = db_escape_string($_POST["full_name"]);
+
+ $active_uid = $_SESSION["uid"];
+
+ db_query($this->link, "UPDATE ttrss_users SET email = '$email',
+ full_name = '$full_name' WHERE id = '$active_uid'");
+
+ print __("Your personal data has been saved.");
+
+ return;
+ }
+
+ function resetconfig() {
+
+ $_SESSION["prefs_op_result"] = "reset-to-defaults";
+
+ if ($_SESSION["profile"]) {
+ $profile_qpart = "profile = '" . $_SESSION["profile"] . "'";
+ } else {
+ $profile_qpart = "profile IS NULL";
+ }
+
+ db_query($this->link, "DELETE FROM ttrss_user_prefs
+ WHERE $profile_qpart AND owner_uid = ".$_SESSION["uid"]);
+
+ initialize_user_prefs($this->link, $_SESSION["uid"], $_SESSION["profile"]);
+
+ print "PREFS_THEME_CHANGED";
+ }
+
+ function index() {
+
+ global $access_level_names;
+
+ $prefs_blacklist = array("HIDE_READ_FEEDS", "FEEDS_SORT_BY_UNREAD",
+ "STRIP_UNSAFE_TAGS");
+
+ $profile_blacklist = array("ALLOW_DUPLICATE_POSTS", "PURGE_OLD_DAYS",
+ "PURGE_UNREAD_ARTICLES", "DIGEST_ENABLE", "DIGEST_CATCHUP",
+ "BLACKLISTED_TAGS", "ENABLE_API_ACCESS", "UPDATE_POST_ON_CHECKSUM_CHANGE",
+ "DEFAULT_UPDATE_INTERVAL", "USER_TIMEZONE", "SORT_HEADLINES_BY_FEED_DATE",
+ "SSL_CERT_SERIAL", "DIGEST_PREFERRED_TIME");
+
+
+ $_SESSION["prefs_op_result"] = "";
+
+ print "<div dojoType=\"dijit.layout.AccordionContainer\" region=\"center\">";
+ print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Personal data / Authentication')."\">";
+
+ print "<form dojoType=\"dijit.form.Form\" id=\"changeUserdataForm\">";
+
+ print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
+ evt.preventDefault();
+ if (this.validate()) {
+ notify_progress('Saving data...', true);
+
+ new Ajax.Request('backend.php', {
+ parameters: dojo.objectToQuery(this.getValues()),
+ onComplete: function(transport) {
+ notify_callback2(transport);
+ } });
+
+ }
+ </script>";
+
+ print "<table width=\"100%\" class=\"prefPrefsList\">";
+
+ $result = db_query($this->link, "SELECT email,full_name,
+ access_level FROM ttrss_users
+ WHERE id = ".$_SESSION["uid"]);
+
+ $email = htmlspecialchars(db_fetch_result($result, 0, "email"));
+ $full_name = htmlspecialchars(db_fetch_result($result, 0, "full_name"));
+
+ print "<tr><td width=\"40%\">".__('Full name')."</td>";
+ print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" name=\"full_name\" required=\"1\"
+ value=\"$full_name\"></td></tr>";
+
+ print "<tr><td width=\"40%\">".__('E-mail')."</td>";
+ print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" name=\"email\" required=\"1\" value=\"$email\"></td></tr>";
+
+ if (!SINGLE_USER_MODE && !$_SESSION["hide_hello"]) {
+
+ $access_level = db_fetch_result($result, 0, "access_level");
+ print "<tr><td width=\"40%\">".__('Access level')."</td>";
+ print "<td>" . $access_level_names[$access_level] . "</td></tr>";
+ }
+
+ print "</table>";
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-prefs\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"changeemail\">";
+
+ print "<p><button dojoType=\"dijit.form.Button\" type=\"submit\">".
+ __("Save data")."</button>";
+
+ print "</form>";
+
+ if ($_SESSION["auth_module"]) {
+ $module_class = "auth_" . $_SESSION["auth_module"];
+ $authenticator = new $module_class($this->link);
+ } else {
+ $authenticator = false;
+ }
+
+ if ($authenticator && method_exists($authenticator, "change_password")) {
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_users
+ WHERE id = ".$_SESSION["uid"]." AND pwd_hash
+ = 'SHA1:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'");
+
+ if (db_num_rows($result) != 0) {
+ print format_warning(__("Your password is at default value, please change it."), "default_pass_warning");
+ }
+
+ print "<form dojoType=\"dijit.form.Form\">";
+
+ print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
+ evt.preventDefault();
+ if (this.validate()) {
+ notify_progress('Changing password...', true);
+
+ new Ajax.Request('backend.php', {
+ parameters: dojo.objectToQuery(this.getValues()),
+ onComplete: function(transport) {
+ notify('');
+ if (transport.responseText.indexOf('ERROR: ') == 0) {
+ notify_error(transport.responseText.replace('ERROR: ', ''));
+ } else {
+ notify_info(transport.responseText);
+ var warn = $('default_pass_warning');
+ if (warn) Element.hide(warn);
+ }
+ }});
+ this.reset();
+ }
+ </script>";
+
+ print "<table width=\"100%\" class=\"prefPrefsList\">";
+
+ print "<tr><td width=\"40%\">".__("Old password")."</td>";
+ print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" type=\"password\" required=\"1\" name=\"old_password\"></td></tr>";
+
+ print "<tr><td width=\"40%\">".__("New password")."</td>";
+
+ print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" type=\"password\" required=\"1\"
+ name=\"new_password\"></td></tr>";
+
+ print "<tr><td width=\"40%\">".__("Confirm password")."</td>";
+
+ print "<td class=\"prefValue\"><input dojoType=\"dijit.form.ValidationTextBox\" type=\"password\" required=\"1\" name=\"confirm_password\"></td></tr>";
+
+ print "</table>";
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-prefs\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"changepassword\">";
+
+ print "<p><button dojoType=\"dijit.form.Button\" type=\"submit\">".
+ __("Change password")."</button>";
+
+ print "</form>";
+
+ }
+
+ print "</div>"; #pane
+
+ print "<div dojoType=\"dijit.layout.AccordionPane\" selected=\"true\" title=\"".__('Preferences')."\">";
+
+ print "<form dojoType=\"dijit.form.Form\" id=\"changeSettingsForm\">";
+
+ print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
+ evt.preventDefault();
+ if (this.validate()) {
+ console.log(dojo.objectToQuery(this.getValues()));
+
+ new Ajax.Request('backend.php', {
+ parameters: dojo.objectToQuery(this.getValues()),
+ onComplete: function(transport) {
+ var msg = transport.responseText;
+ if (msg.match('PREFS_THEME_CHANGED')) {
+ window.location.reload();
+ } else {
+ notify_info(msg);
+ }
+ } });
+ }
+ </script>";
+
+ print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
+
+ print '<div dojoType="dijit.layout.ContentPane" region="center" style="overflow-y : auto">';
+
+ if ($_SESSION["profile"]) {
+ print_notice("Some preferences are only available in default profile.");
+ }
+
+ if ($_SESSION["profile"]) {
+ initialize_user_prefs($this->link, $_SESSION["uid"], $_SESSION["profile"]);
+ $profile_qpart = "profile = '" . $_SESSION["profile"] . "'";
+ } else {
+ initialize_user_prefs($this->link, $_SESSION["uid"]);
+ $profile_qpart = "profile IS NULL";
+ }
+
+ $result = db_query($this->link, "SELECT DISTINCT
+ ttrss_user_prefs.pref_name,short_desc,help_text,value,type_name,
+ section_name,def_value,section_id
+ FROM ttrss_prefs,ttrss_prefs_types,ttrss_prefs_sections,ttrss_user_prefs
+ WHERE type_id = ttrss_prefs_types.id AND
+ $profile_qpart AND
+ section_id = ttrss_prefs_sections.id AND
+ ttrss_user_prefs.pref_name = ttrss_prefs.pref_name AND
+ short_desc != '' AND
+ owner_uid = ".$_SESSION["uid"]."
+ ORDER BY section_id,short_desc");
+
+ $lnum = 0;
+
+ $active_section = "";
+
+ while ($line = db_fetch_assoc($result)) {
+
+ if (in_array($line["pref_name"], $prefs_blacklist)) {
+ continue;
+ }
+
+ if ($_SESSION["profile"] && in_array($line["pref_name"],
+ $profile_blacklist)) {
+ continue;
+ }
+
+ if ($active_section != $line["section_name"]) {
+
+ if ($active_section != "") {
+ print "</table>";
+ }
+
+ print "<table width=\"100%\" class=\"prefPrefsList\">";
+
+ $active_section = $line["section_name"];
+
+ print "<tr><td colspan=\"3\"><h3>".__($active_section)."</h3></td></tr>";
+
+ if ($line["section_id"] == 2) {
+ print "<tr><td width=\"40%\">".__("Select theme")."</td>";
+
+ $user_theme = get_pref($this->link, "_THEME_ID");
+ $themes = get_all_themes();
+
+ print "<td><select name=\"_THEME_ID\" dojoType=\"dijit.form.Select\">";
+ print "<option value='Default'>".__('Default')."</option>";
+ print "<option value='----------------' disabled=\"1\">--------</option>";
+
+ foreach ($themes as $t) {
+ $base = $t['base'];
+ $name = $t['name'];
+
+ if ($base == $user_theme) {
+ $selected = "selected=\"1\"";
+ } else {
+ $selected = "";
+ }
+
+ print "<option $selected value='$base'>$name</option>";
+
+ }
+
+ print "</select></td></tr>";
+ }
+ $lnum = 0;
+ }
+
+ print "<tr>";
+
+ $type_name = $line["type_name"];
+ $pref_name = $line["pref_name"];
+ $value = $line["value"];
+ $def_value = $line["def_value"];
+ $help_text = $line["help_text"];
+
+ print "<td width=\"40%\" class=\"prefName\" id=\"$pref_name\">" . __($line["short_desc"]);
+
+ if ($help_text) print "<div class=\"prefHelp\">".__($help_text)."</div>";
+
+ print "</td>";
+
+ print "<td class=\"prefValue\">";
+
+ if ($pref_name == "USER_TIMEZONE") {
+
+ $timezones = explode("\n", file_get_contents("lib/timezones.txt"));
+
+ print_select($pref_name, $value, $timezones, 'dojoType="dijit.form.FilteringSelect"');
+ } else if ($pref_name == "USER_STYLESHEET") {
+
+ print "<button dojoType=\"dijit.form.Button\"
+ onclick=\"customizeCSS()\">" . __('Customize') . "</button>";
+
+ } else if ($pref_name == "DEFAULT_ARTICLE_LIMIT") {
+
+ $limits = array(15, 30, 45, 60);
+
+ print_select($pref_name, $value, $limits,
+ 'dojoType="dijit.form.Select"');
+
+ } else if ($pref_name == "DEFAULT_UPDATE_INTERVAL") {
+
+ global $update_intervals_nodefault;
+
+ print_select_hash($pref_name, $value, $update_intervals_nodefault,
+ 'dojoType="dijit.form.Select"');
+
+ } else if ($type_name == "bool") {
+
+ if ($value == "true") {
+ $value = __("Yes");
+ } else {
+ $value = __("No");
+ }
+
+ if ($pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE != 0) {
+ $disabled = "disabled=\"1\"";
+ $value = __("Yes");
+ } else {
+ $disabled = "";
+ }
+
+ print_radio($pref_name, $value, __("Yes"), array(__("Yes"), __("No")),
+ $disabled);
+
+ } else if (array_search($pref_name, array('FRESH_ARTICLE_MAX_AGE', 'DEFAULT_ARTICLE_LIMIT',
+ 'PURGE_OLD_DAYS', 'LONG_DATE_FORMAT', 'SHORT_DATE_FORMAT')) !== false) {
+
+ $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : '';
+
+ if ($pref_name == "PURGE_OLD_DAYS" && FORCE_ARTICLE_PURGE != 0) {
+ $disabled = "disabled=\"1\"";
+ $value = FORCE_ARTICLE_PURGE;
+ } else {
+ $disabled = "";
+ }
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\"
+ required=\"1\" $regexp $disabled
+ name=\"$pref_name\" value=\"$value\">";
+
+ } else if ($pref_name == "SSL_CERT_SERIAL") {
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\"
+ id=\"SSL_CERT_SERIAL\" readonly=\"1\"
+ name=\"$pref_name\" value=\"$value\">";
+
+ $cert_serial = htmlspecialchars(get_ssl_certificate_id());
+ $has_serial = ($cert_serial) ? "false" : "true";
+
+ print " <button dojoType=\"dijit.form.Button\" disabled=\"$has_serial\"
+ onclick=\"insertSSLserial('$cert_serial')\">" .
+ __('Register') . "</button>";
+
+ print " <button dojoType=\"dijit.form.Button\"
+ onclick=\"insertSSLserial('')\">" .
+ __('Clear') . "</button>";
+
+ } else if ($pref_name == 'DIGEST_PREFERRED_TIME') {
+ print "<input dojoType=\"dijit.form.ValidationTextBox\"
+ id=\"$pref_name\" regexp=\"[012]?\d:\d\d\" placeHolder=\"12:00\"
+ name=\"$pref_name\" value=\"$value\"><div class=\"insensitive\">".
+ T_sprintf("Current server time: %s (UTC)", date("H:i")) . "</div>";
+ } else {
+ $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : '';
+
+ print "<input dojoType=\"dijit.form.ValidationTextBox\"
+ $regexp
+ name=\"$pref_name\" value=\"$value\">";
+ }
+
+ print "</td>";
+
+ print "</tr>";
+
+ $lnum++;
+ }
+
+ print "</table>";
+
+ print '</div>'; # inside pane
+ print '<div dojoType="dijit.layout.ContentPane" region="bottom">';
+
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pref-prefs\">";
+ print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"saveconfig\">";
+
+ print "<button dojoType=\"dijit.form.Button\" type=\"submit\">".
+ __('Save configuration')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return editProfiles()\">".
+ __('Manage profiles')."</button> ";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"return validatePrefsReset()\">".
+ __('Reset to defaults')."</button>";
+
+ print '</div>'; # inner pane
+ print '</div>'; # border container
+
+ print "</form>";
+
+ print "</div>"; #pane
+ print "</div>"; #container
+ }
+}
+?>
diff --git a/classes/pref/users.php b/classes/pref/users.php
new file mode 100644
index 000000000..e54f899d7
--- /dev/null
+++ b/classes/pref/users.php
@@ -0,0 +1,494 @@
+<?php
+class Pref_Users extends Handler_Protected {
+ function before($method) {
+ if (parent::before($method)) {
+ if ($_SESSION["access_level"] < 10) {
+ print __("Your access level is insufficient to open this tab.");
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ function csrf_ignore($method) {
+ $csrf_ignored = array("index");
+
+ return array_search($method, $csrf_ignored) !== false;
+ }
+
+ function userdetails() {
+
+ header("Content-Type: text/xml");
+ print "<dlg>";
+
+ $uid = sprintf("%d", $_REQUEST["id"]);
+
+ print "<title>".__('User details')."</title>";
+
+ print "<content><![CDATA[";
+
+ $result = db_query($this->link, "SELECT login,
+ ".SUBSTRING_FOR_DATE."(last_login,1,16) AS last_login,
+ access_level,
+ (SELECT COUNT(int_id) FROM ttrss_user_entries
+ WHERE owner_uid = id) AS stored_articles,
+ ".SUBSTRING_FOR_DATE."(created,1,16) AS created
+ FROM ttrss_users
+ WHERE id = '$uid'");
+
+ if (db_num_rows($result) == 0) {
+ print "<h1>".__('User not found')."</h1>";
+ return;
+ }
+
+ // print "<h1>User Details</h1>";
+
+ $login = db_fetch_result($result, 0, "login");
+
+ print "<table width='100%'>";
+
+ $last_login = make_local_datetime($this->link,
+ db_fetch_result($result, 0, "last_login"), true);
+
+ $created = make_local_datetime($this->link,
+ db_fetch_result($result, 0, "created"), true);
+
+ $access_level = db_fetch_result($result, 0, "access_level");
+ $stored_articles = db_fetch_result($result, 0, "stored_articles");
+
+ print "<tr><td>".__('Registered')."</td><td>$created</td></tr>";
+ print "<tr><td>".__('Last logged in')."</td><td>$last_login</td></tr>";
+
+ $result = db_query($this->link, "SELECT COUNT(id) as num_feeds FROM ttrss_feeds
+ WHERE owner_uid = '$uid'");
+
+ $num_feeds = db_fetch_result($result, 0, "num_feeds");
+
+ print "<tr><td>".__('Subscribed feeds count')."</td><td>$num_feeds</td></tr>";
+
+ print "</table>";
+
+ print "<h1>".__('Subscribed feeds')."</h1>";
+
+ $result = db_query($this->link, "SELECT id,title,site_url FROM ttrss_feeds
+ WHERE owner_uid = '$uid' ORDER BY title");
+
+ print "<ul class=\"userFeedList\">";
+
+ $row_class = "odd";
+
+ while ($line = db_fetch_assoc($result)) {
+
+ $icon_file = ICONS_URL."/".$line["id"].".ico";
+
+ if (file_exists($icon_file) && filesize($icon_file) > 0) {
+ $feed_icon = "<img class=\"tinyFeedIcon\" src=\"$icon_file\">";
+ } else {
+ $feed_icon = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">";
+ }
+
+ print "<li class=\"$row_class\">$feed_icon&nbsp;<a href=\"".$line["site_url"]."\">".$line["title"]."</a></li>";
+
+ $row_class = $row_class == "even" ? "odd" : "even";
+
+ }
+
+ if (db_num_rows($result) < $num_feeds) {
+ // FIXME - add link to show ALL subscribed feeds here somewhere
+ print "<li><img
+ class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\">&nbsp;...</li>";
+ }
+
+ print "</ul>";
+
+ print "<div align='center'>
+ <button onclick=\"closeInfoBox()\">".__("Close this window").
+ "</button></div>";
+
+ print "]]></content></dlg>";
+
+ return;
+ }
+
+ function edit() {
+ global $access_level_names;
+
+ header("Content-Type: text/xml");
+
+ $id = db_escape_string($_REQUEST["id"]);
+
+ print "<dlg id=\"$method\">";
+ print "<title>".__('User Editor')."</title>";
+ print "<content><![CDATA[";
+
+ print "<form id=\"user_edit_form\" onsubmit='return false'>";
+
+ print "<input type=\"hidden\" name=\"id\" value=\"$id\">";
+ print "<input type=\"hidden\" name=\"op\" value=\"pref-users\">";
+ print "<input type=\"hidden\" name=\"method\" value=\"editSave\">";
+
+ $result = db_query($this->link, "SELECT * FROM ttrss_users WHERE id = '$id'");
+
+ $login = db_fetch_result($result, 0, "login");
+ $access_level = db_fetch_result($result, 0, "access_level");
+ $email = db_fetch_result($result, 0, "email");
+
+ $sel_disabled = ($id == $_SESSION["uid"]) ? "disabled" : "";
+
+ print "<div class=\"dlgSec\">".__("User")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ if ($sel_disabled) {
+ print "<input type=\"hidden\" name=\"login\" value=\"$login\">";
+ print "<input size=\"30\" style=\"font-size : 16px\"
+ onkeypress=\"return filterCR(event, userEditSave)\" $sel_disabled
+ value=\"$login\">";
+ } else {
+ print "<input size=\"30\" style=\"font-size : 16px\"
+ onkeypress=\"return filterCR(event, userEditSave)\" $sel_disabled
+ name=\"login\" value=\"$login\">";
+ }
+
+ print "</div>";
+
+ print "<div class=\"dlgSec\">".__("Authentication")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print __('Access level: ') . " ";
+
+ if (!$sel_disabled) {
+ print_select_hash("access_level", $access_level, $access_level_names,
+ $sel_disabled);
+ } else {
+ print_select_hash("", $access_level, $access_level_names,
+ $sel_disabled);
+ print "<input type=\"hidden\" name=\"access_level\" value=\"$access_level\">";
+ }
+
+ print "<br/>";
+
+ print __('Change password to') .
+ " <input size=\"20\" onkeypress=\"return filterCR(event, userEditSave)\"
+ name=\"password\">";
+
+ print "</div>";
+
+ print "<div class=\"dlgSec\">".__("Options")."</div>";
+ print "<div class=\"dlgSecCont\">";
+
+ print __('E-mail: ').
+ " <input size=\"30\" name=\"email\" onkeypress=\"return filterCR(event, userEditSave)\"
+ value=\"$email\">";
+
+ print "</div>";
+
+ print "</table>";
+
+ print "</form>";
+
+ print "<div class=\"dlgButtons\">
+ <button onclick=\"return userEditSave()\">".
+ __('Save')."</button>
+ <button onclick=\"return userEditCancel()\">".
+ __('Cancel')."</button></div>";
+
+ print "]]></content></dlg>";
+
+ return;
+ }
+
+ function editSave() {
+ $login = db_escape_string(trim($_REQUEST["login"]));
+ $uid = db_escape_string($_REQUEST["id"]);
+ $access_level = (int) $_REQUEST["access_level"];
+ $email = db_escape_string(trim($_REQUEST["email"]));
+ $password = db_escape_string(trim($_REQUEST["password"]));
+
+ if ($password) {
+ $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
+ $pwd_hash = encrypt_password($password, $salt, true);
+ $pass_query_part = "pwd_hash = '$pwd_hash', salt = '$salt',";
+ } else {
+ $pass_query_part = "";
+ }
+
+ db_query($this->link, "UPDATE ttrss_users SET $pass_query_part login = '$login',
+ access_level = '$access_level', email = '$email' WHERE id = '$uid'");
+
+ }
+
+ function remove() {
+ $ids = split(",", db_escape_string($_REQUEST["ids"]));
+
+ foreach ($ids as $id) {
+ if ($id != $_SESSION["uid"] && $id != 1) {
+ db_query($this->link, "DELETE FROM ttrss_tags WHERE owner_uid = '$id'");
+ db_query($this->link, "DELETE FROM ttrss_feeds WHERE owner_uid = '$id'");
+ db_query($this->link, "DELETE FROM ttrss_users WHERE id = '$id'");
+ }
+ }
+ }
+
+ function add() {
+
+ $login = db_escape_string(trim($_REQUEST["login"]));
+ $tmp_user_pwd = make_password(8);
+ $salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
+ $pwd_hash = encrypt_password($tmp_user_pwd, $salt, true);
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE
+ login = '$login'");
+
+ if (db_num_rows($result) == 0) {
+
+ db_query($this->link, "INSERT INTO ttrss_users
+ (login,pwd_hash,access_level,last_login,created, salt)
+ VALUES ('$login', '$pwd_hash', 0, null, NOW(), '$salt')");
+
+
+ $result = db_query($this->link, "SELECT id FROM ttrss_users WHERE
+ login = '$login' AND pwd_hash = '$pwd_hash'");
+
+ if (db_num_rows($result) == 1) {
+
+ $new_uid = db_fetch_result($result, 0, "id");
+
+ print format_notice(T_sprintf("Added user <b>%s</b> with password <b>%s</b>",
+ $login, $tmp_user_pwd));
+
+ initialize_user($this->link, $new_uid);
+
+ } else {
+
+ print format_warning(T_sprintf("Could not create user <b>%s</b>", $login));
+
+ }
+ } else {
+ print format_warning(T_sprintf("User <b>%s</b> already exists.", $login));
+ }
+ }
+
+ function resetPass() {
+
+ $uid = db_escape_string($_REQUEST["id"]);
+
+ $result = db_query($this->link, "SELECT login,email
+ FROM ttrss_users WHERE id = '$uid'");
+
+ $login = db_fetch_result($result, 0, "login");
+ $email = db_fetch_result($result, 0, "email");
+ $salt = db_fetch_result($result, 0, "salt");
+
+ $new_salt = substr(bin2hex(get_random_bytes(125)), 0, 250);
+ $tmp_user_pwd = make_password(8);
+
+ $pwd_hash = encrypt_password($tmp_user_pwd, $new_salt, true);
+
+ db_query($this->link, "UPDATE ttrss_users SET pwd_hash = '$pwd_hash', salt = '$new_salt'
+ WHERE id = '$uid'");
+
+ print T_sprintf("Changed password of user <b>%s</b>
+ to <b>%s</b>", $login, $tmp_user_pwd);
+
+ require_once 'lib/phpmailer/class.phpmailer.php';
+
+ if ($email) {
+ print " ";
+ print T_sprintf("Notifying <b>%s</b>.", $email);
+
+ require_once "lib/MiniTemplator.class.php";
+
+ $tpl = new MiniTemplator;
+
+ $tpl->readTemplateFromFile("templates/resetpass_template.txt");
+
+ $tpl->setVariable('LOGIN', $login);
+ $tpl->setVariable('NEWPASS', $tmp_user_pwd);
+
+ $tpl->addBlock('message');
+
+ $message = "";
+
+ $tpl->generateOutputToString($message);
+
+ $mail = new PHPMailer();
+
+ $mail->PluginDir = "lib/phpmailer/";
+ $mail->SetLanguage("en", "lib/phpmailer/language/");
+
+ $mail->CharSet = "UTF-8";
+
+ $mail->From = SMTP_FROM_ADDRESS;
+ $mail->FromName = SMTP_FROM_NAME;
+ $mail->AddAddress($email, $login);
+
+ if (SMTP_HOST) {
+ $mail->Host = SMTP_HOST;
+ $mail->Mailer = "smtp";
+ $mail->SMTPAuth = SMTP_LOGIN != '';
+ $mail->Username = SMTP_LOGIN;
+ $mail->Password = SMTP_PASSWORD;
+ }
+
+ $mail->IsHTML(false);
+ $mail->Subject = __("[tt-rss] Password change notification");
+ $mail->Body = $message;
+
+ $rc = $mail->Send();
+
+ if (!$rc) print_error($mail->ErrorInfo);
+ }
+
+ print "</div>";
+ }
+
+ function index() {
+
+ global $access_level_names;
+
+ print "<div id=\"pref-user-wrap\" dojoType=\"dijit.layout.BorderContainer\" gutters=\"false\">";
+ print "<div id=\"pref-user-header\" dojoType=\"dijit.layout.ContentPane\" region=\"top\">";
+
+ print "<div id=\"pref-user-toolbar\" dojoType=\"dijit.Toolbar\">";
+
+ $user_search = db_escape_string($_REQUEST["search"]);
+
+ if (array_key_exists("search", $_REQUEST)) {
+ $_SESSION["prefs_user_search"] = $user_search;
+ } else {
+ $user_search = $_SESSION["prefs_user_search"];
+ }
+
+ print "<div style='float : right; padding-right : 4px;'>
+ <input dojoType=\"dijit.form.TextBox\" id=\"user_search\" size=\"20\" type=\"search\"
+ value=\"$user_search\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"javascript:updateUsersList()\">".
+ __('Search')."</button>
+ </div>";
+
+ $sort = db_escape_string($_REQUEST["sort"]);
+
+ if (!$sort || $sort == "undefined") {
+ $sort = "login";
+ }
+
+ print "<div dojoType=\"dijit.form.DropDownButton\">".
+ "<span>" . __('Select')."</span>";
+ print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
+ print "<div onclick=\"selectTableRows('prefUserList', 'all')\"
+ dojoType=\"dijit.MenuItem\">".__('All')."</div>";
+ print "<div onclick=\"selectTableRows('prefUserList', 'none')\"
+ dojoType=\"dijit.MenuItem\">".__('None')."</div>";
+ print "</div></div>";
+
+ print "<button dojoType=\"dijit.form.Button\" onclick=\"javascript:addUser()\">".__('Create user')."</button>";
+
+ print "
+ <button dojoType=\"dijit.form.Button\" onclick=\"javascript:selectedUserDetails()\">".
+ __('Details')."</button dojoType=\"dijit.form.Button\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"javascript:editSelectedUser()\">".
+ __('Edit')."</button dojoType=\"dijit.form.Button\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"javascript:removeSelectedUsers()\">".
+ __('Remove')."</button dojoType=\"dijit.form.Button\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"javascript:resetSelectedUserPass()\">".
+ __('Reset password')."</button dojoType=\"dijit.form.Button\">";
+
+ print "</div>"; #toolbar
+ print "</div>"; #pane
+ print "<div id=\"pref-user-content\" dojoType=\"dijit.layout.ContentPane\" region=\"center\">";
+
+ print "<div id=\"sticky-status-msg\"></div>";
+
+ if ($user_search) {
+
+ $user_search = split(" ", $user_search);
+ $tokens = array();
+
+ foreach ($user_search as $token) {
+ $token = trim($token);
+ array_push($tokens, "(UPPER(login) LIKE UPPER('%$token%'))");
+ }
+
+ $user_search_query = "(" . join($tokens, " AND ") . ") AND ";
+
+ } else {
+ $user_search_query = "";
+ }
+
+ $result = db_query($this->link, "SELECT
+ id,login,access_level,email,
+ ".SUBSTRING_FOR_DATE."(last_login,1,16) as last_login,
+ ".SUBSTRING_FOR_DATE."(created,1,16) as created
+ FROM
+ ttrss_users
+ WHERE
+ $user_search_query
+ id > 0
+ ORDER BY $sort");
+
+ if (db_num_rows($result) > 0) {
+
+ print "<p><table width=\"100%\" cellspacing=\"0\"
+ class=\"prefUserList\" id=\"prefUserList\">";
+
+ print "<tr class=\"title\">
+ <td align='center' width=\"5%\">&nbsp;</td>
+ <td width=''><a href=\"#\" onclick=\"updateUsersList('login')\">".__('Login')."</a></td>
+ <td width='20%'><a href=\"#\" onclick=\"updateUsersList('access_level')\">".__('Access Level')."</a></td>
+ <td width='20%'><a href=\"#\" onclick=\"updateUsersList('created')\">".__('Registered')."</a></td>
+ <td width='20%'><a href=\"#\" onclick=\"updateUsersList('last_login')\">".__('Last login')."</a></td></tr>";
+
+ $lnum = 0;
+
+ while ($line = db_fetch_assoc($result)) {
+
+ $class = ($lnum % 2) ? "even" : "odd";
+
+ $uid = $line["id"];
+
+ print "<tr class=\"$class\" id=\"UMRR-$uid\">";
+
+ $line["login"] = htmlspecialchars($line["login"]);
+
+ $line["created"] = make_local_datetime($this->link, $line["created"], false);
+ $line["last_login"] = make_local_datetime($this->link, $line["last_login"], false);
+
+ print "<td align='center'><input onclick='toggleSelectRow(this);'
+ type=\"checkbox\" id=\"UMCHK-$uid\"></td>";
+
+ $onclick = "onclick='editUser($uid, event)' title='".__('Click to edit')."'";
+
+ print "<td $onclick>" . $line["login"] . "</td>";
+
+ if (!$line["email"]) $line["email"] = "&nbsp;";
+
+ print "<td $onclick>" . $access_level_names[$line["access_level"]] . "</td>";
+ print "<td $onclick>" . $line["created"] . "</td>";
+ print "<td $onclick>" . $line["last_login"] . "</td>";
+
+ print "</tr>";
+
+ ++$lnum;
+ }
+
+ print "</table>";
+
+ } else {
+ print "<p>";
+ if (!$user_search) {
+ print_warning(__('No users defined.'));
+ } else {
+ print_warning(__('No matching users found.'));
+ }
+ print "</p>";
+
+ }
+
+ print "</div>"; #pane
+ print "</div>"; #container
+
+ }
+
+ }
+?>