select('cfgname')->find_array(), 'cfgname')); } return []; } 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 = []; $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() { 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)); } /* 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'] = []; } $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, false, 0); $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 { $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); $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() { $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; } $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]; 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() { $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") ?>