query("SELECT cfgname FROM pg_ts_config"); while ($row = $res->fetch()) { array_push($rv, ucfirst($row['cfgname'])); } } return $rv; } function batch_edit_cbox($elem, $label = false) { print ""; } function renamecat() { $title = clean($_REQUEST['title']); $id = clean($_REQUEST['id']); if ($title) { $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET title = ? WHERE id = ? AND owner_uid = ?"); $sth->execute([$title, $id, $_SESSION['uid']]); } } private function get_category_items($cat_id) { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; else $search = ""; // first one is set by API $show_empty_cats = clean($_REQUEST['force_show_empty'] ?? false) || (clean($_REQUEST['mode'] ?? 0) != 2 && !$search); $items = array(); $sth = $this->pdo->prepare("SELECT id, title FROM ttrss_feed_categories WHERE owner_uid = ? AND parent_cat = ? ORDER BY order_id, title"); $sth->execute([$_SESSION['uid'], $cat_id]); while ($line = $sth->fetch()) { $cat = array(); $cat['id'] = 'CAT:' . $line['id']; $cat['bare_id'] = (int)$line['id']; $cat['name'] = $line['title']; $cat['items'] = array(); $cat['checkbox'] = false; $cat['type'] = 'category'; $cat['unread'] = -1; $cat['child_unread'] = -1; $cat['auxcounter'] = -1; $cat['parent_id'] = $cat_id; $cat['items'] = $this->get_category_items($line['id']); $num_children = $this->calculate_children_count($cat); $cat['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children); if ($num_children > 0 || $show_empty_cats) array_push($items, $cat); } $fsth = $this->pdo->prepare("SELECT id, title, last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval FROM ttrss_feeds WHERE cat_id = :cat AND owner_uid = :uid AND (:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search)) ORDER BY order_id, title"); $fsth->execute([":cat" => $cat_id, ":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]); while ($feed_line = $fsth->fetch()) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; $feed['auxcounter'] = -1; $feed['name'] = $feed_line['title']; $feed['checkbox'] = false; $feed['unread'] = -1; $feed['error'] = $feed_line['last_error']; $feed['icon'] = Feeds::_get_icon($feed_line['id']); $feed['param'] = TimeHelper::make_local_datetime( $feed_line['last_updated'], true); $feed['updates_disabled'] = (int)($feed_line['update_interval'] < 0); array_push($items, $feed); } return $items; } function getfeedtree() { print json_encode($this->_makefeedtree()); } function _makefeedtree() { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; else $search = ""; $root = array(); $root['id'] = 'root'; $root['name'] = __('Feeds'); $root['items'] = array(); $root['param'] = 0; $root['type'] = 'category'; $enable_cats = get_pref('ENABLE_FEED_CATS'); if (clean($_REQUEST['mode'] ?? 0) == 2) { if ($enable_cats) { $cat = $this->feedlist_init_cat(-1); } else { $cat['items'] = array(); } foreach (array(-4, -3, -1, -2, 0, -6) as $i) { array_push($cat['items'], $this->feedlist_init_feed($i)); } /* Plugin feeds for -1 */ $feeds = PluginHost::getInstance()->get_feeds(-1); if ($feeds) { foreach ($feeds as $feed) { $feed_id = PluginHost::pfeed_to_feed_id($feed['id']); $item = array(); $item['id'] = 'FEED:' . $feed_id; $item['bare_id'] = (int)$feed_id; $item['auxcounter'] = -1; $item['name'] = $feed['title']; $item['checkbox'] = false; $item['error'] = ''; $item['icon'] = $feed['icon']; $item['param'] = ''; $item['unread'] = -1; $item['type'] = 'feed'; array_push($cat['items'], $item); } } if ($enable_cats) { array_push($root['items'], $cat); } else { $root['items'] = array_merge($root['items'], $cat['items']); } $sth = $this->pdo->prepare("SELECT * FROM ttrss_labels2 WHERE owner_uid = ? ORDER by caption"); $sth->execute([$_SESSION['uid']]); if (get_pref('ENABLE_FEED_CATS')) { $cat = $this->feedlist_init_cat(-2); } else { $cat['items'] = array(); } $num_labels = 0; while ($line = $sth->fetch()) { ++$num_labels; $label_id = Labels::label_to_feed_id($line['id']); $feed = $this->feedlist_init_feed($label_id, false, 0); $feed['fg_color'] = $line['fg_color']; $feed['bg_color'] = $line['bg_color']; array_push($cat['items'], $feed); } if ($num_labels) { if ($enable_cats) { array_push($root['items'], $cat); } else { $root['items'] = array_merge($root['items'], $cat['items']); } } } if ($enable_cats) { $show_empty_cats = clean($_REQUEST['force_show_empty'] ?? false) || (clean($_REQUEST['mode'] ?? 0) != 2 && !$search); $sth = $this->pdo->prepare("SELECT id, title FROM ttrss_feed_categories WHERE owner_uid = ? AND parent_cat IS NULL ORDER BY order_id, title"); $sth->execute([$_SESSION['uid']]); while ($line = $sth->fetch()) { $cat = array(); $cat['id'] = 'CAT:' . $line['id']; $cat['bare_id'] = (int)$line['id']; $cat['auxcounter'] = -1; $cat['name'] = $line['title']; $cat['items'] = array(); $cat['checkbox'] = false; $cat['type'] = 'category'; $cat['unread'] = -1; $cat['child_unread'] = -1; $cat['items'] = $this->get_category_items($line['id']); $num_children = $this->calculate_children_count($cat); $cat['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children); if ($num_children > 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['auxcounter'] = -1; $cat['name'] = __("Uncategorized"); $cat['items'] = array(); $cat['type'] = 'category'; $cat['checkbox'] = false; $cat['unread'] = -1; $cat['child_unread'] = -1; $fsth = $this->pdo->prepare("SELECT id, title,last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval FROM ttrss_feeds WHERE cat_id IS NULL AND owner_uid = :uid AND (:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search)) ORDER BY order_id, title"); $fsth->execute([":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]); while ($feed_line = $fsth->fetch()) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; $feed['auxcounter'] = -1; $feed['name'] = $feed_line['title']; $feed['checkbox'] = false; $feed['error'] = $feed_line['last_error']; $feed['icon'] = Feeds::_get_icon($feed_line['id']); $feed['param'] = TimeHelper::make_local_datetime( $feed_line['last_updated'], true); $feed['unread'] = -1; $feed['type'] = 'feed'; $feed['updates_disabled'] = (int)($feed_line['update_interval'] < 0); array_push($cat['items'], $feed); } $cat['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items'])); if (count($cat['items']) > 0 || $show_empty_cats) array_push($root['items'], $cat); $num_children = $this->calculate_children_count($root); $root['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children); } else { $fsth = $this->pdo->prepare("SELECT id, title, last_error, ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval FROM ttrss_feeds WHERE owner_uid = :uid AND (:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search)) ORDER BY order_id, title"); $fsth->execute([":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]); while ($feed_line = $fsth->fetch()) { $feed = array(); $feed['id'] = 'FEED:' . $feed_line['id']; $feed['bare_id'] = (int)$feed_line['id']; $feed['auxcounter'] = -1; $feed['name'] = $feed_line['title']; $feed['checkbox'] = false; $feed['error'] = $feed_line['last_error']; $feed['icon'] = Feeds::_get_icon($feed_line['id']); $feed['param'] = TimeHelper::make_local_datetime( $feed_line['last_updated'], true); $feed['unread'] = -1; $feed['type'] = 'feed'; $feed['updates_disabled'] = (int)($feed_line['update_interval'] < 0); array_push($root['items'], $feed); } $root['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', count($root['items'])), count($root['items'])); } $fl = array(); $fl['identifier'] = 'id'; $fl['label'] = 'name'; if (clean($_REQUEST['mode'] ?? 0) != 2) { $fl['items'] = array($root); } else { $fl['items'] = $root['items']; } return $fl; } function catsortreset() { $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } function feedsortreset() { $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) { $prefix = ""; for ($i = 0; $i < $nest_level; $i++) $prefix .= " "; Debug::log("$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 = $parent_bare_id; } else { $parent_qpart = null; } $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET parent_cat = ? WHERE id = ? AND owner_uid = ?"); $sth->execute([$parent_qpart, $bare_item_id, $_SESSION['uid']]); } $order_id = 1; $cat = $data_map[$item_id]; if ($cat && is_array($cat)) { foreach ($cat as $item) { $id = $item['_reference']; $bare_id = substr($id, strpos($id, ':')+1); Debug::log("$prefix [$order_id] $id/$bare_id"); if ($item['_reference']) { if (strpos($id, "FEED") === 0) { $cat_id = ($item_id != "root") ? $bare_item_id : null; $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET order_id = ?, cat_id = ? WHERE id = ? AND owner_uid = ?"); $sth->execute([$order_id, $cat_id ? $cat_id : null, $bare_id, $_SESSION['uid']]); } else if (strpos($id, "CAT:") === 0) { $this->process_category_order($data_map, $item['_reference'], $item_id, $nest_level+1); $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET order_id = ? WHERE id = ? AND owner_uid = ?"); $sth->execute([$order_id, $bare_id, $_SESSION['uid']]); } } ++$order_id; } } } function savefeedorder() { $data = json_decode($_POST['payload'], true); #file_put_contents("/tmp/saveorder.json", clean($_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); } } function removeicon() { $feed_id = clean($_REQUEST["feed_id"]); $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); $sth->execute([$feed_id, $_SESSION['uid']]); if ($row = $sth->fetch()) { @unlink(ICONS_DIR . "/$feed_id.ico"); $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = NULL, favicon_last_checked = '1970-01-01' where id = ?"); $sth->execute([$feed_id]); } } function uploadicon() { header("Content-type: text/html"); if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) { $tmp_file = tempnam(CACHE_DIR . '/upload', 'icon'); if (!$tmp_file) return; $result = move_uploaded_file($_FILES['icon_file']['tmp_name'], $tmp_file); if (!$result) { return; } } else { return; } $icon_file = $tmp_file; $feed_id = clean($_REQUEST["feed_id"]); $rc = 2; // failed if ($icon_file && is_file($icon_file) && $feed_id) { if (filesize($icon_file) < 65535) { $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); $sth->execute([$feed_id, $_SESSION['uid']]); if ($row = $sth->fetch()) { $new_filename = ICONS_DIR . "/$feed_id.ico"; if (file_exists($new_filename)) unlink($new_filename); if (rename($icon_file, $new_filename)) { chmod($new_filename, 644); $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = '' WHERE id = ?"); $sth->execute([$feed_id]); $rc = Feeds::_get_icon($feed_id); } } } else { $rc = 1; } } if ($icon_file && is_file($icon_file)) { unlink($icon_file); } print $rc; return; } function editfeed() { global $purge_intervals; global $update_intervals; $feed_id = (int)clean($_REQUEST["id"]); $sth = $this->pdo->prepare("SELECT * FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); $sth->execute([$feed_id, $_SESSION['uid']]); if ($row = $sth->fetch(PDO::FETCH_ASSOC)) { ob_start(); PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED, $feed_id); $plugin_data = trim((string)ob_get_contents()); ob_end_clean(); $row["icon"] = Feeds::_get_icon($feed_id); $local_update_intervals = $update_intervals; $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]); if (FORCE_ARTICLE_PURGE == 0) { $local_purge_intervals = $purge_intervals; $default_purge_interval = get_pref("PURGE_OLD_DAYS"); if ($default_purge_interval > 0) $local_purge_intervals[0] .= " " . T_nsprintf('(%d day)', '(%d days)', $default_purge_interval, $default_purge_interval); else $local_purge_intervals[0] .= " " . sprintf("(%s)", __("Disabled")); } else { $purge_interval = FORCE_ARTICLE_PURGE; $local_purge_intervals = [ T_nsprintf('%d day', '%d days', $purge_interval, $purge_interval) ]; } print json_encode([ "feed" => $row, "cats" => [ "enabled" => get_pref('ENABLE_FEED_CATS'), "select" => \Controls\select_feeds_cats("cat_id", $row["cat_id"]), ], "plugin_data" => $plugin_data, "force_purge" => (int)FORCE_ARTICLE_PURGE, "intervals" => [ "update" => $local_update_intervals, "purge" => $local_purge_intervals, ], "lang" => [ "enabled" => DB_TYPE == "pgsql", "default" => get_pref('DEFAULT_SEARCH_LANGUAGE'), "all" => $this::get_ts_languages(), ] ]); } else { print json_encode(["error" => "FEED_NOT_FOUND"]); } } function editfeeds() { global $purge_intervals; global $update_intervals; $feed_ids = clean($_REQUEST["ids"]); print_notice("Enable the options you wish to apply using checkboxes on the right:"); print \Controls\hidden_tag("ids", "$feed_ids"); print \Controls\hidden_tag("op", "pref-feeds"); print \Controls\hidden_tag("method", "batchEditSave"); print "
".__("Feed")."
"; print "
"; /* Category */ if (get_pref('ENABLE_FEED_CATS')) { print "
"; print " "; print \Controls\select_feeds_cats("cat_id", null, ['disabled' => '1']); $this->batch_edit_cbox("cat_id"); print "
"; } /* FTS Stemming Language */ if (DB_TYPE == "pgsql") { print "
"; print " "; print \Controls\select_tag("feed_language", "", $this::get_ts_languages(), ["disabled"=> 1]); $this->batch_edit_cbox("feed_language"); print "
"; } print "
"; print "
".__("Update")."
"; print "
"; /* Update Interval */ print "
"; print " "; $local_update_intervals = $update_intervals; $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]); print \Controls\select_hash("update_interval", "", $local_update_intervals, ["disabled" => 1]); $this->batch_edit_cbox("update_interval"); print "
"; /* Purge intl */ if (FORCE_ARTICLE_PURGE == 0) { print "
"; print " "; $local_purge_intervals = $purge_intervals; $default_purge_interval = get_pref("PURGE_OLD_DAYS"); if ($default_purge_interval > 0) $local_purge_intervals[0] .= " " . T_sprintf("(%d days)", $default_purge_interval); else $local_purge_intervals[0] .= " " . sprintf("(%s)", __("Disabled")); print \Controls\select_hash("purge_interval", "", $local_purge_intervals, ["disabled" => 1]); $this->batch_edit_cbox("purge_interval"); print "
"; } print "
"; print "
".__("Authentication")."
"; print "
"; print "
"; print ""; $this->batch_edit_cbox("auth_login"); print ""; $this->batch_edit_cbox("auth_pass"); print "
"; print "
"; print "
".__("Options")."
"; print "
"; print "
"; print ""; print " "; $this->batch_edit_cbox("include_in_digest", "include_in_digest_l"); print "
"; print ""; print " "; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l"); print "
"; print ""; print " "; $this->batch_edit_cbox("hide_images", "hide_images_l"); print "
"; print ""; print " "; $this->batch_edit_cbox("cache_images", "cache_images_l"); print "
"; print ""; print " "; $this->batch_edit_cbox("mark_unread_on_update", "mark_unread_on_update_l"); print "
"; print "
"; print ""; return; } function batchEditSave() { return $this->editsaveops(true); } function editSave() { return $this->editsaveops(false); } private function editsaveops($batch) { $feed_title = clean($_POST["title"]); $feed_url = clean($_POST["feed_url"]); $site_url = clean($_POST["site_url"]); $upd_intl = (int) clean($_POST["update_interval"] ?? 0); $purge_intl = (int) clean($_POST["purge_interval"] ?? 0); $feed_id = (int) clean($_POST["id"] ?? 0); /* editSave */ $feed_ids = explode(",", clean($_POST["ids"] ?? "")); /* batchEditSave */ $cat_id = (int) clean($_POST["cat_id"]); $auth_login = clean($_POST["auth_login"]); $auth_pass = clean($_POST["auth_pass"]); $private = checkbox_to_sql_bool(clean($_POST["private"] ?? "")); $include_in_digest = checkbox_to_sql_bool( clean($_POST["include_in_digest"] ?? "")); $cache_images = checkbox_to_sql_bool( clean($_POST["cache_images"] ?? "")); $hide_images = checkbox_to_sql_bool( clean($_POST["hide_images"] ?? "")); $always_display_enclosures = checkbox_to_sql_bool( clean($_POST["always_display_enclosures"] ?? "")); $mark_unread_on_update = checkbox_to_sql_bool( clean($_POST["mark_unread_on_update"] ?? "")); $feed_language = clean($_POST["feed_language"]); if (!$batch) { /* $sth = $this->pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ?"); $sth->execute([$feed_id]); $row = $sth->fetch();$orig_feed_url = $row["feed_url"]; $reset_basic_info = $orig_feed_url != $feed_url; */ $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET cat_id = :cat_id, title = :title, feed_url = :feed_url, site_url = :site_url, update_interval = :upd_intl, purge_interval = :purge_intl, auth_login = :auth_login, auth_pass = :auth_pass, auth_pass_encrypted = false, private = :private, cache_images = :cache_images, hide_images = :hide_images, include_in_digest = :include_in_digest, always_display_enclosures = :always_display_enclosures, mark_unread_on_update = :mark_unread_on_update, feed_language = :feed_language WHERE id = :id AND owner_uid = :uid"); $sth->execute([":title" => $feed_title, ":cat_id" => $cat_id ? $cat_id : null, ":feed_url" => $feed_url, ":site_url" => $site_url, ":upd_intl" => $upd_intl, ":purge_intl" => $purge_intl, ":auth_login" => $auth_login, ":auth_pass" => $auth_pass, ":private" => (int)$private, ":cache_images" => (int)$cache_images, ":hide_images" => (int)$hide_images, ":include_in_digest" => (int)$include_in_digest, ":always_display_enclosures" => (int)$always_display_enclosures, ":mark_unread_on_update" => (int)$mark_unread_on_update, ":feed_language" => $feed_language, ":id" => $feed_id, ":uid" => $_SESSION['uid']]); /* if ($reset_basic_info) { RSSUtils::set_basic_feed_info($feed_id); } */ PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_SAVE_FEED, $feed_id); } else { $feed_data = array(); foreach (array_keys($_POST) as $k) { if ($k != "op" && $k != "method" && $k != "ids") { $feed_data[$k] = clean($_POST[$k]); } } $this->pdo->beginTransaction(); $feed_ids_qmarks = arr_qmarks($feed_ids); foreach (array_keys($feed_data) as $k) { $qpart = ""; switch ($k) { case "title": $qpart = "title = " . $this->pdo->quote($feed_title); break; case "feed_url": $qpart = "feed_url = " . $this->pdo->quote($feed_url); break; case "update_interval": $qpart = "update_interval = " . $this->pdo->quote($upd_intl); break; case "purge_interval": $qpart = "purge_interval =" . $this->pdo->quote($purge_intl); break; case "auth_login": $qpart = "auth_login = " . $this->pdo->quote($auth_login); break; case "auth_pass": $qpart = "auth_pass =" . $this->pdo->quote($auth_pass). ", auth_pass_encrypted = false"; break; case "private": $qpart = "private = " . $this->pdo->quote($private); break; case "include_in_digest": $qpart = "include_in_digest = " . $this->pdo->quote($include_in_digest); break; case "always_display_enclosures": $qpart = "always_display_enclosures = " . $this->pdo->quote($always_display_enclosures); break; case "mark_unread_on_update": $qpart = "mark_unread_on_update = " . $this->pdo->quote($mark_unread_on_update); break; case "cache_images": $qpart = "cache_images = " . $this->pdo->quote($cache_images); break; case "hide_images": $qpart = "hide_images = " . $this->pdo->quote($hide_images); break; case "cat_id": if (get_pref('ENABLE_FEED_CATS')) { if ($cat_id) { $qpart = "cat_id = " . $this->pdo->quote($cat_id); } else { $qpart = 'cat_id = NULL'; } } else { $qpart = ""; } break; case "feed_language": $qpart = "feed_language = " . $this->pdo->quote($feed_language); break; } if ($qpart) { $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids_qmarks) AND owner_uid = ?"); $sth->execute(array_merge($feed_ids, [$_SESSION['uid']])); } } $this->pdo->commit(); } return; } function remove() { $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { self::remove_feed($id, $_SESSION["uid"]); } return; } function removeCat() { $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { $this->remove_feed_category($id, $_SESSION["uid"]); } } function addCat() { $feed_cat = clean($_REQUEST["cat"]); Feeds::_add_cat($feed_cat); } function importOpml() { $opml = new OPML($_REQUEST); $opml->opml_import($_SESSION["uid"]); } private function index_feeds() { $error_button = ""; $inactive_button = ""; $feed_search = clean($_REQUEST["search"] ?? ""); if (array_key_exists("search", $_REQUEST)) { $_SESSION["prefs_feed_search"] = $feed_search; } else { $feed_search = $_SESSION["prefs_feed_search"] ?? ""; } ?>
" persist="true" model="feedModel" openOnClick="false">



run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsOPML"); } private function index_shared() { ?>

run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsPublishedGenerated"); } function index() { ?>
index_feeds() ?>
index_opml() ?>
index_shared() ?>
run_hooks(PluginHost::HOOK_PREFS_TAB, "prefFeeds"); $plugin_data = trim((string)ob_get_contents()); ob_end_clean(); ?>
pdo->prepare("SELECT ttrss_feeds.title, ttrss_feeds.site_url, ttrss_feeds.feed_url, ttrss_feeds.id, MAX(updated) AS last_article FROM ttrss_feeds, ttrss_entries, ttrss_user_entries 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 = ? AND ttrss_user_entries.feed_id = ttrss_feeds.id AND ttrss_entries.id = ref_id GROUP BY ttrss_feeds.title, ttrss_feeds.id, ttrss_feeds.site_url, ttrss_feeds.feed_url ORDER BY last_article"); $sth->execute([$_SESSION['uid']]); $rv = []; while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { $row['last_article'] = TimeHelper::make_local_datetime($row['last_article'], false); array_push($rv, $row); } print json_encode($rv); } function feedsWithErrors() { $sth = $this->pdo->prepare("SELECT id,title,feed_url,last_error,site_url FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); $sth->execute([$_SESSION['uid']]); $rv = []; while ($row = $sth->fetch()) { array_push($rv, $row); } print json_encode($rv); } private function remove_feed_category($id, $owner_uid) { $sth = $this->pdo->prepare("DELETE FROM ttrss_feed_categories WHERE id = ? AND owner_uid = ?"); $sth->execute([$id, $owner_uid]); } static function remove_feed($id, $owner_uid) { if (PluginHost::getInstance()->run_hooks_until(PluginHost::HOOK_UNSUBSCRIBE_FEED, true, $id, $owner_uid)) return; $pdo = Db::pdo(); if ($id > 0) { $pdo->beginTransaction(); /* save starred articles in Archived feed */ $sth = $pdo->prepare("UPDATE ttrss_user_entries SET feed_id = NULL, orig_feed_id = NULL WHERE feed_id = ? AND marked = true AND owner_uid = ?"); $sth->execute([$id, $owner_uid]); /* Remove access key for the feed */ $sth = $pdo->prepare("DELETE FROM ttrss_access_keys WHERE feed_id = ? AND owner_uid = ?"); $sth->execute([$id, $owner_uid]); /* remove the feed */ $sth = $pdo->prepare("DELETE FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); $sth->execute([$id, $owner_uid]); $pdo->commit(); if (file_exists(ICONS_DIR . "/$id.ico")) { unlink(ICONS_DIR . "/$id.ico"); } } else { Labels::remove(Labels::feed_to_label_id($id), $owner_uid); } } function batchSubscribe() { print json_encode([ "enable_cats" => (int)get_pref('ENABLE_FEED_CATS'), "cat_select" => \Controls\select_feeds_cats("cat") ]); } function batchAddFeeds() { $cat_id = clean($_REQUEST['cat']); $feeds = explode("\n", clean($_REQUEST['feeds'])); $login = clean($_REQUEST['login']); $pass = clean($_REQUEST['pass']); $csth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ? AND owner_uid = ?"); $isth = $this->pdo->prepare("INSERT INTO ttrss_feeds (owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted) VALUES (?, ?, '[Unknown]', ?, ?, ?, 0, false)"); foreach ($feeds as $feed) { $feed = trim($feed); if (UrlHelper::validate($feed)) { $this->pdo->beginTransaction(); $csth->execute([$feed, $_SESSION['uid']]); if (!$csth->fetch()) { $isth->execute([$_SESSION['uid'], $feed, $cat_id ? $cat_id : null, $login, $pass]); } $this->pdo->commit(); } } } function getOPMLKey() { print json_encode(["link" => OPML::opml_publish_url()]); } function regenOPMLKey() { $this->update_feed_access_key('OPML:Publish', false, $_SESSION["uid"]); print json_encode(["link" => OPML::opml_publish_url()]); } function regenFeedKey() { $feed_id = clean($_REQUEST['id']); $is_cat = clean($_REQUEST['is_cat']); $new_key = $this->update_feed_access_key($feed_id, $is_cat, $_SESSION["uid"]); print json_encode(["link" => $new_key]); } function getsharedurl() { $feed_id = clean($_REQUEST['id']); $is_cat = clean($_REQUEST['is_cat']) == "true"; $search = clean($_REQUEST['search']); $link = get_self_url_prefix() . "/public.php?" . http_build_query([ 'op' => 'rss', 'id' => $feed_id, 'is_cat' => (int)$is_cat, 'q' => $search, 'key' => Feeds::_get_access_key($feed_id, $is_cat, $_SESSION["uid"]) ]); print json_encode([ "title" => Feeds::_get_title($feed_id, $is_cat), "link" => $link ]); } private function update_feed_access_key($feed_id, $is_cat, $owner_uid) { // clear old value and generate new one $sth = $this->pdo->prepare("DELETE FROM ttrss_access_keys WHERE feed_id = ? AND is_cat = ? AND owner_uid = ?"); $sth->execute([$feed_id, bool_to_sql_bool($is_cat), $owner_uid]); return Feeds::_get_access_key($feed_id, $is_cat, $owner_uid); } // Silent function clearKeys() { $sth = $this->pdo->prepare("DELETE FROM ttrss_access_keys WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } private function calculate_children_count($cat) { $c = 0; foreach ($cat['items'] ?? [] as $child) { if ($child['type'] ?? '' == 'category') { $c += $this->calculate_children_count($child); } else { $c += 1; } } return $c; } }