query("SELECT cfgname FROM pg_ts_config"); while ($row = $res->fetch()) { array_push($rv, ucfirst($row['cfgname'])); } } return $rv; } function renameCat() { $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(); } } 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(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, false, 0)); } /* 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(Prefs::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 = (int) $_REQUEST["feed_id"]; $icon_file = Config::get(Config::ICONS_DIR) . "/$feed_id.ico"; $feed = ORM::for_table('ttrss_feeds') ->where('owner_uid', $_SESSION['uid']) ->find_one($feed_id); if ($feed && file_exists($icon_file)) { if (unlink($icon_file)) { $feed->set([ 'favicon_avg_color' => null, 'favicon_last_checked' => '1970-01-01', 'favicon_is_custom' => false, ]); $feed->save(); } } } function uploadIcon() { $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)) { $new_filename = Config::get(Config::ICONS_DIR) . "/$feed_id.ico"; if (file_exists($new_filename)) unlink($new_filename); if (rename($tmp_file, $new_filename)) { chmod($new_filename, 0644); $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; } } 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)]); } function editfeed() { 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) ]; } 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, ], "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($name) { return \Controls\checkbox_tag("", false, "", ["data-control-for" => $name, "title" => __("Check to enable field"), "onchange" => "App.dialogOf(this).toggleField(this)"]); } function editfeeds() { 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."); ?> = \Controls\hidden_tag("ids", $feed_ids) ?> = \Controls\hidden_tag("op", "pref-feeds") ?> = \Controls\hidden_tag("method", "batchEditSave") ?>