*/ public static function get_ts_languages(): array { if (Config::get(Config::DB_TYPE) == 'pgsql') { return array_map('ucfirst', array_column(ORM::for_table('pg_ts_config')->select('cfgname')->find_array(), 'cfgname')); } return []; } function renameCat(): void { $cat = ORM::for_table("ttrss_feed_categories") ->where("owner_uid", $_SESSION["uid"]) ->find_one($_REQUEST['id']); $title = clean($_REQUEST['title']); if ($cat && $title) { $cat->title = $title; $cat->save(); } } /** * @return array> */ private function get_category_items(int $cat_id): array { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; else $search = ""; // first one is set by API $show_empty_cats = self::_param_to_bool($_REQUEST['force_show_empty'] ?? false) || (clean($_REQUEST['mode'] ?? 0) != 2 && !$search); $items = []; $feed_categories = ORM::for_table('ttrss_feed_categories') ->select_many('id', 'title') ->where(['owner_uid' => $_SESSION['uid'], 'parent_cat' => $cat_id]) ->order_by_asc('order_id') ->order_by_asc('title') ->find_many(); foreach ($feed_categories as $feed_category) { $cat = [ 'id' => 'CAT:' . $feed_category->id, 'bare_id' => (int)$feed_category->id, 'name' => $feed_category->title, 'items' => $this->get_category_items($feed_category->id), 'checkbox' => false, 'type' => 'category', 'unread' => -1, 'child_unread' => -1, 'auxcounter' => -1, 'parent_id' => $cat_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); } $feeds_obj = ORM::for_table('ttrss_feeds') ->select_many('id', 'title', 'last_error', 'update_interval') ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated') ->where(['cat_id' => $cat_id, 'owner_uid' => $_SESSION['uid']]) ->order_by_asc('order_id') ->order_by_asc('title'); if ($search) { $feeds_obj->where_raw('(LOWER(title) LIKE ? OR LOWER(feed_url) LIKE LOWER(?))', ["%$search%", "%$search%"]); } foreach ($feeds_obj->find_many() as $feed) { array_push($items, [ 'id' => 'FEED:' . $feed->id, 'bare_id' => (int) $feed->id, 'auxcounter' => -1, 'name' => $feed->title, 'checkbox' => false, 'unread' => -1, 'error' => $feed->last_error, 'icon' => Feeds::_get_icon($feed->id), 'param' => TimeHelper::make_local_datetime($feed->last_updated, true), 'updates_disabled' => (int)($feed->update_interval < 0), ]); } return $items; } function getfeedtree(): void { print json_encode($this->_makefeedtree()); } /** * @return array|string> */ function _makefeedtree(): array { 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(Prefs::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 { array_push($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(Prefs::ENABLE_FEED_CATS)) { $cat = $this->feedlist_init_cat(-2); } else { $cat['items'] = []; } $labels = ORM::for_table('ttrss_labels2') ->where('owner_uid', $_SESSION['uid']) ->order_by_asc('caption') ->find_many(); if (count($labels)) { foreach ($labels as $label) { $label_id = Labels::label_to_feed_id($label->id); $feed = $this->feedlist_init_feed($label_id, null, false); $feed['fg_color'] = $label->fg_color; $feed['bg_color'] = $label->bg_color; array_push($cat['items'], $feed); } if ($enable_cats) { array_push($root['items'], $cat); } else { array_push($root['items'], ...$cat['items']); } } } if ($enable_cats) { $show_empty_cats = self::_param_to_bool($_REQUEST['force_show_empty'] ?? false) || (clean($_REQUEST['mode'] ?? 0) != 2 && !$search); $feed_categories = ORM::for_table('ttrss_feed_categories') ->select_many('id', 'title') ->where('owner_uid', $_SESSION['uid']) ->where_null('parent_cat') ->order_by_asc('order_id') ->order_by_asc('title') ->find_many(); foreach ($feed_categories as $feed_category) { $cat = [ 'id' => 'CAT:' . $feed_category->id, 'bare_id' => (int) $feed_category->id, 'auxcounter' => -1, 'name' => $feed_category->title, 'items' => $this->get_category_items($feed_category->id), 'checkbox' => false, 'type' => 'category', 'unread' => -1, 'child_unread' => -1, ]; $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 = [ 'id' => 'CAT:0', 'bare_id' => 0, 'auxcounter' => -1, 'name' => __('Uncategorized'), 'items' => [], 'type' => 'category', 'checkbox' => false, 'unread' => -1, 'child_unread' => -1, ]; $feeds_obj = ORM::for_table('ttrss_feeds') ->select_many('id', 'title', 'last_error', 'update_interval') ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated') ->where('owner_uid', $_SESSION['uid']) ->where_null('cat_id') ->order_by_asc('order_id') ->order_by_asc('title'); if ($search) { $feeds_obj->where_raw('(LOWER(title) LIKE ? OR LOWER(feed_url) LIKE LOWER(?))', ["%$search%", "%$search%"]); } foreach ($feeds_obj->find_many() as $feed) { array_push($cat['items'], [ 'id' => 'FEED:' . $feed->id, 'bare_id' => (int) $feed->id, 'auxcounter' => -1, 'name' => $feed->title, 'checkbox' => false, 'error' => $feed->last_error, 'icon' => Feeds::_get_icon($feed->id), 'param' => TimeHelper::make_local_datetime($feed->last_updated, true), 'unread' => -1, 'type' => 'feed', 'updates_disabled' => (int)($feed->update_interval < 0), ]); } $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 { $feeds_obj = ORM::for_table('ttrss_feeds') ->select_many('id', 'title', 'last_error', 'update_interval') ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated') ->where('owner_uid', $_SESSION['uid']) ->order_by_asc('order_id') ->order_by_asc('title'); if ($search) { $feeds_obj->where_raw('(LOWER(title) LIKE ? OR LOWER(feed_url) LIKE LOWER(?))', ["%$search%", "%$search%"]); } foreach ($feeds_obj->find_many() as $feed) { array_push($root['items'], [ 'id' => 'FEED:' . $feed->id, 'bare_id' => (int) $feed->id, 'auxcounter' => -1, 'name' => $feed->title, 'checkbox' => false, 'error' => $feed->last_error, 'icon' => Feeds::_get_icon($feed->id), 'param' => TimeHelper::make_local_datetime($feed->last_updated, true), 'unread' => -1, 'type' => 'feed', 'updates_disabled' => (int)($feed->update_interval < 0), ]); } $root['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', count($root['items'])), count($root['items'])); } return [ 'identifier' => 'id', 'label' => 'name', 'items' => clean($_REQUEST['mode'] ?? 0) != 2 ? [$root] : $root['items'], ]; } function catsortreset(): void { $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } function feedsortreset(): void { $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } /** * @param array $data_map */ private function process_category_order(array &$data_map, string $item_id = '', string $parent_id = '', int $nest_level = 0): void { $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; } $feed_category = ORM::for_table('ttrss_feed_categories') ->where('owner_uid', $_SESSION['uid']) ->find_one($bare_item_id); if ($feed_category) { $feed_category->parent_cat = $parent_qpart; $feed_category->save(); } } $order_id = 1; $cat = ($data_map[$item_id] ?? false); 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) { $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $_SESSION['uid']) ->find_one($bare_id); if ($feed) { $feed->order_id = $order_id; $feed->cat_id = ($item_id != "root" && $bare_item_id) ? $bare_item_id : null; $feed->save(); } } else if (strpos($id, "CAT:") === 0) { $this->process_category_order($data_map, $item['_reference'], $item_id, $nest_level+1); $feed_category = ORM::for_table('ttrss_feed_categories') ->where('owner_uid', $_SESSION['uid']) ->find_one($bare_id); if ($feed_category) { $feed_category->order_id = $order_id; $feed_category->save(); } } } ++$order_id; } } } function savefeedorder(): void { $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; /** @var array */ $data_map = array(); $root_item = ''; foreach ($data['items'] as $item) { # if ($item['id'] != 'root') { if (is_array($item['items'] ?? false)) { 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(): void { $feed_id = (int) $_REQUEST["feed_id"]; $cache = DiskCache::instance('feed-icons'); $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $_SESSION['uid']) ->find_one($feed_id); if ($feed && $cache->exists((string)$feed_id)) { if ($cache->remove((string)$feed_id)) { $feed->set([ 'favicon_avg_color' => null, 'favicon_last_checked' => '1970-01-01', 'favicon_is_custom' => false, ]); $feed->save(); } } } function uploadIcon(): void { $feed_id = (int) $_REQUEST['feed_id']; $tmp_file = tempnam(Config::get(Config::CACHE_DIR) . '/upload', 'icon'); // default value $rc = self::E_ICON_UPLOAD_FAILED; $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $_SESSION['uid']) ->find_one($feed_id); if ($feed && $tmp_file && move_uploaded_file($_FILES['icon_file']['tmp_name'], $tmp_file)) { if (filesize($tmp_file) < Config::get(Config::MAX_FAVICON_FILE_SIZE)) { $cache = DiskCache::instance('feed-icons'); if ($cache->put((string)$feed_id, file_get_contents($tmp_file))) { $feed->set([ 'favicon_avg_color' => null, 'favicon_is_custom' => true, ]); if ($feed->save()) { $rc = self::E_ICON_UPLOAD_SUCCESS; } } else { $rc = self::E_ICON_RENAME_FAILED; } @unlink($tmp_file); } else { $rc = self::E_ICON_FILE_TOO_LARGE; } } if (file_exists($tmp_file)) unlink($tmp_file); print json_encode(['rc' => $rc, 'icon_url' => Feeds::_get_icon($feed_id) . "?ts=" . time() ]); } function editfeed(): void { global $purge_intervals; global $update_intervals; $feed_id = (int)clean($_REQUEST["id"]); $row = ORM::for_table('ttrss_feeds') ->where("owner_uid", $_SESSION["uid"]) ->find_one($feed_id)->as_array(); if ($row) { 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(Prefs::DEFAULT_UPDATE_INTERVAL)]); if (Config::get(Config::FORCE_ARTICLE_PURGE) == 0) { $local_purge_intervals = $purge_intervals; $default_purge_interval = get_pref(Prefs::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 = Config::get(Config::FORCE_ARTICLE_PURGE); $local_purge_intervals = [ T_nsprintf('%d day', '%d days', $purge_interval, $purge_interval) ]; } $user = ORM::for_table("ttrss_users")->find_one($_SESSION["uid"]); print json_encode([ "feed" => $row, "cats" => [ "enabled" => get_pref(Prefs::ENABLE_FEED_CATS), "select" => \Controls\select_feeds_cats("cat_id", $row["cat_id"]), ], "plugin_data" => $plugin_data, "force_purge" => (int)Config::get(Config::FORCE_ARTICLE_PURGE), "intervals" => [ "update" => $local_update_intervals, "purge" => $local_purge_intervals, ], "user" => [ "access_level" => $user->access_level ], "lang" => [ "enabled" => Config::get(Config::DB_TYPE) == "pgsql", "default" => get_pref(Prefs::DEFAULT_SEARCH_LANGUAGE), "all" => $this::get_ts_languages(), ] ]); } } private function _batch_toggle_checkbox(string $name): string { return \Controls\checkbox_tag("", false, "", ["data-control-for" => $name, "title" => __("Check to enable field"), "onchange" => "App.dialogOf(this).toggleField(this)"]); } function editfeeds(): void { global $purge_intervals; global $update_intervals; $feed_ids = clean($_REQUEST["ids"]); $local_update_intervals = $update_intervals; $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref(Prefs::DEFAULT_UPDATE_INTERVAL)]); $local_purge_intervals = $purge_intervals; $default_purge_interval = get_pref(Prefs::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")); $options = [ "include_in_digest" => __('Include in e-mail digest'), "always_display_enclosures" => __('Always display image attachments'), "hide_images" => __('Do not embed media'), "cache_images" => __('Cache media'), "mark_unread_on_update" => __('Mark updated articles as unread') ]; print_notice("Enable the options you wish to apply using checkboxes on the right."); ?>
'1']) ?> _batch_toggle_checkbox("cat_id") ?>
1]) ?> _batch_toggle_checkbox("feed_language") ?>

1]) ?> _batch_toggle_checkbox("update_interval") ?>
1]) ?> _batch_toggle_checkbox("purge_interval") ?>
_batch_toggle_checkbox("auth_login") ?>
_batch_toggle_checkbox("auth_pass") ?>
$caption) { ?>
editsaveops(true); } function editSave(): void { $this->editsaveops(false); } private function editsaveops(bool $batch): void { $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"] ?? 0); $auth_login = clean($_POST["auth_login"] ?? ""); $auth_pass = clean($_POST["auth_pass"] ?? ""); $private = clean($_POST["private"] ?? "") == "on"; $include_in_digest = clean($_POST["include_in_digest"] ?? "") == "on"; $cache_images = clean($_POST["cache_images"] ?? "") == "on"; $hide_images = clean($_POST["hide_images"] ?? "") == "on"; $always_display_enclosures = clean($_POST["always_display_enclosures"] ?? "") == "on"; $mark_unread_on_update = clean($_POST["mark_unread_on_update"] ?? "") == "on"; $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; */ $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $_SESSION['uid']) ->find_one($feed_id); if ($feed) { $feed->title = $feed_title; $feed->cat_id = $cat_id ? $cat_id : null; $feed->feed_url = $feed_url; $feed->site_url = $site_url; $feed->update_interval = $upd_intl; $feed->purge_interval = $purge_intl; $feed->auth_login = $auth_login; $feed->auth_pass = $auth_pass; $feed->private = (int)$private; $feed->cache_images = (int)$cache_images; $feed->hide_images = (int)$hide_images; $feed->feed_language = $feed_language; $feed->include_in_digest = (int)$include_in_digest; $feed->always_display_enclosures = (int)$always_display_enclosures; $feed->mark_unread_on_update = (int)$mark_unread_on_update; $feed->save(); 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 = ""; $qparams = []; switch ($k) { case "title": $qpart = "title = ?"; $qparams = [$feed_title]; break; case "feed_url": $qpart = "feed_url = ?"; $qparams = [$this->pdo->quote($feed_url)]; break; case "update_interval": $qpart = "update_interval = ?"; $qparams = [$upd_intl]; break; case "purge_interval": $qpart = "purge_interval = ?"; $qparams = [$purge_intl]; break; case "auth_login": $qpart = "auth_login = ?"; $qparams = [$auth_login]; break; case "auth_pass": $qpart = "auth_pass = ?, auth_pass_encrypted = false"; $qparams = [$auth_pass]; break; case "private": $qpart = "private = ?"; $qparams = [$private]; break; case "include_in_digest": $qpart = "include_in_digest = ?"; $qparams = [$include_in_digest]; break; case "always_display_enclosures": $qpart = "always_display_enclosures = ?"; $qparams = [$always_display_enclosures]; break; case "mark_unread_on_update": $qpart = "mark_unread_on_update = ?"; $qparams = [$mark_unread_on_update]; break; case "cache_images": $qpart = "cache_images = ?"; $qparams = [$cache_images]; break; case "hide_images": $qpart = "hide_images = ?"; $qparams = [$hide_images]; break; case "cat_id": if (get_pref(Prefs::ENABLE_FEED_CATS)) { $qpart = "cat_id = ?"; $qparams = $cat_id ? [$cat_id] : [null]; } break; case "feed_language": $qpart = "feed_language = ?"; $qparams = [$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([...$qparams, ...$feed_ids, $_SESSION['uid']]); } } $this->pdo->commit(); } } function remove(): void { /** @var array */ $ids = array_map('intval', explode(",", clean($_REQUEST["ids"]))); foreach ($ids as $id) { self::remove_feed($id, $_SESSION["uid"]); } } function removeCat(): void { $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { Feeds::_remove_cat((int)$id, $_SESSION["uid"]); } } function addCat(): void { $feed_cat = clean($_REQUEST["cat"]); Feeds::_add_cat($feed_cat, $_SESSION['uid']); } function importOpml(): void { $opml = new OPML($_REQUEST); $opml->opml_import($_SESSION["uid"]); } private function index_feeds(): void { $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(): void { ?> run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsPublishedGenerated"); } function index(): void { ?>
index_feeds() ?>
index_opml() ?>
index_shared() ?>
run_hooks(PluginHost::HOOK_PREFS_TAB, "prefFeeds"); $plugin_data = trim((string)ob_get_contents()); ob_end_clean(); ?>
*/ private function feedlist_init_cat(int $cat_id): array { return [ 'id' => 'CAT:' . $cat_id, 'items' => array(), 'name' => Feeds::_get_cat_title($cat_id), 'type' => 'category', 'unread' => -1, //(int) Feeds::_get_cat_unread($cat_id); 'bare_id' => $cat_id, ]; } /** * @return array */ private function feedlist_init_feed(int $feed_id, ?string $title = null, bool $unread = false, string $error = '', string $updated = ''): array { if (!$title) $title = Feeds::_get_title($feed_id, false); if ($unread === false) $unread = Feeds::_get_counters($feed_id, false, true); return [ 'id' => 'FEED:' . $feed_id, 'name' => $title, 'unread' => (int) $unread, 'type' => 'feed', 'error' => $error, 'updated' => $updated, 'icon' => Feeds::_get_icon($feed_id), 'bare_id' => $feed_id, 'auxcounter' => 0, ]; } function inactiveFeeds(): void { if (Config::get(Config::DB_TYPE) == "pgsql") { $interval_qpart = "NOW() - INTERVAL '3 months'"; } else { $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; } $inactive_feeds = ORM::for_table('ttrss_feeds') ->table_alias('f') ->select_many('f.id', 'f.title', 'f.site_url', 'f.feed_url') ->select_expr('MAX(e.updated)', 'last_article') ->join('ttrss_user_entries', [ 'ue.feed_id', '=', 'f.id'], 'ue') ->join('ttrss_entries', ['e.id', '=', 'ue.ref_id'], 'e') ->where('f.owner_uid', $_SESSION['uid']) ->where_raw( "(SELECT MAX(ttrss_entries.updated) FROM ttrss_entries JOIN ttrss_user_entries ON ttrss_entries.id = ttrss_user_entries.ref_id WHERE ttrss_user_entries.feed_id = f.id) < $interval_qpart") ->group_by('f.title') ->group_by('f.id') ->group_by('f.site_url') ->group_by('f.feed_url') ->order_by_asc('last_article') ->find_array(); foreach ($inactive_feeds as $inactive_feed) { $inactive_feed['last_article'] = TimeHelper::make_local_datetime($inactive_feed['last_article'], false); } print json_encode($inactive_feeds); } function feedsWithErrors(): void { print json_encode(ORM::for_table('ttrss_feeds') ->select_many('id', 'title', 'feed_url', 'last_error', 'site_url') ->where_not_equal('last_error', '') ->where('owner_uid', $_SESSION['uid']) ->where_gte('update_interval', 0) ->find_array()); } static function remove_feed(int $id, int $owner_uid): void { 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(); $favicon_cache = DiskCache::instance('feed-icons'); if ($favicon_cache->exists((string)$id)) $favicon_cache->remove((string)$id); } else { Labels::remove(Labels::feed_to_label_id($id), $owner_uid); } } function batchSubscribe(): void { print json_encode([ "enable_cats" => (int)get_pref(Prefs::ENABLE_FEED_CATS), "cat_select" => \Controls\select_feeds_cats("cat") ]); } function batchAddFeeds(): void { $cat_id = clean($_REQUEST['cat']); $feeds = explode("\n", clean($_REQUEST['feeds'])); $login = clean($_REQUEST['login']); $pass = clean($_REQUEST['pass']); $user = ORM::for_table('ttrss_users')->find_one($_SESSION["uid"]); // TODO: we should return some kind of error code to frontend here if ($user->access_level == UserHelper::ACCESS_LEVEL_READONLY) { return; } $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 clearKeys(): void { Feeds::_clear_access_keys($_SESSION['uid']); } function regenFeedKey(): void { $feed_id = clean($_REQUEST['id']); $is_cat = self::_param_to_bool($_REQUEST['is_cat'] ?? false); $new_key = Feeds::_update_access_key($feed_id, $is_cat, $_SESSION["uid"]); print json_encode(["link" => $new_key]); } function getSharedURL(): void { $feed_id = clean($_REQUEST['id']); $is_cat = self::_param_to_bool($_REQUEST['is_cat'] ?? false); $search = clean($_REQUEST['search']); $link = Config::get_self_url() . "/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 ]); } /** * @param array $cat */ private function calculate_children_count(array $cat): int { $c = 0; foreach ($cat['items'] ?? [] as $child) { if (($child['type'] ?? '') == 'category') { $c += $this->calculate_children_count($child); } else { $c += 1; } } return $c; } }