+ 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]);
+
+ CCache::remove($id, $owner_uid, true);
+ }
+
+ static function remove_feed($id, $owner_uid) {
+ foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_UNSUBSCRIBE_FEED) as $p) {
+ if (! $p->hook_unsubscribe_feed($id, $owner_uid)) {
+ user_error("Feed $id (owner: $owner_uid) not removed due to plugin error (HOOK_UNSUBSCRIBE_FEED).", E_USER_WARNING);
+ return;
+ }
+ }
+
+ $pdo = Db::pdo();
+
+ if ($id > 0) {
+ $pdo->beginTransaction();
+
+ /* save starred articles in Archived feed */
+
+ /* prepare feed if necessary */
+
+ $sth = $pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ?
+ AND owner_uid = ?");
+ $sth->execute([$id, $owner_uid]);
+
+ if ($row = $sth->fetch()) {
+ $feed_url = $row["feed_url"];
+
+ $sth = $pdo->prepare("SELECT id FROM ttrss_archived_feeds
+ WHERE feed_url = ? AND owner_uid = ?");
+ $sth->execute([$feed_url, $owner_uid]);
+
+ if ($row = $sth->fetch()) {
+ $archive_id = $row["id"];
+ } else {
+ $res = $pdo->query("SELECT MAX(id) AS id FROM ttrss_archived_feeds");
+ $row = $res->fetch();
+
+ $new_feed_id = (int)$row['id'] + 1;
+
+ $sth = $pdo->prepare("INSERT INTO ttrss_archived_feeds
+ (id, owner_uid, title, feed_url, site_url)
+ SELECT ?, owner_uid, title, feed_url, site_url from ttrss_feeds
+ WHERE id = ?");
+ $sth->execute([$new_feed_id, $id]);
+
+ $archive_id = $new_feed_id;
+ }
+
+ $sth = $pdo->prepare("UPDATE ttrss_user_entries SET feed_id = NULL,
+ orig_feed_id = ? WHERE feed_id = ? AND
+ marked = true AND owner_uid = ?");
+
+ $sth->execute([$archive_id, $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");
+ }
+
+ CCache::remove($id, $owner_uid);
+
+ } else {
+ Labels::remove(Labels::feed_to_label_id($id), $owner_uid);
+ //CCache::remove($id, $owner_uid); don't think labels are cached
+ }
+ }
+
+ function batchSubscribe() {
+ print_hidden("op", "pref-feeds");
+ print_hidden("method", "batchaddfeeds");
+
+ print "<table width='100%'><tr><td>
+ ".__("Add one valid RSS feed per line (no feed detection is done)")."
+ </td><td align='right'>";
+ if (get_pref('ENABLE_FEED_CATS')) {
+ print __('Place in category:') . " ";
+ print_feed_cat_select("cat", false, 'dojoType="dijit.form.Select"');
+ }
+ print "</td></tr><tr><td colspan='2'>";
+ print "<textarea
+ style='font-size : 12px; width : 98%; height: 200px;'
+ placeHolder=\"".__("Feeds to subscribe, One per line")."\"
+ dojoType=\"dijit.form.SimpleTextarea\" required=\"1\" name=\"feeds\"></textarea>";
+
+ print "</td></tr><tr><td colspan='2'>";
+
+ print "<div id='feedDlg_loginContainer' style='display : none'>
+ " .
+ " <input dojoType=\"dijit.form.TextBox\" name='login'\"
+ placeHolder=\"".__("Login")."\"
+ style=\"width : 10em;\"> ".
+ " <input
+ placeHolder=\"".__("Password")."\"
+ dojoType=\"dijit.form.TextBox\" type='password'
+ autocomplete=\"new-password\"
+ style=\"width : 10em;\" name='pass'\">".
+ "</div>";
+
+ print "</td></tr><tr><td colspan='2'>";
+
+ print "<div style=\"clear : both\">
+ <input type=\"checkbox\" name=\"need_auth\" dojoType=\"dijit.form.CheckBox\" id=\"feedDlg_loginCheck\"
+ onclick='checkboxToggleElement(this, \"feedDlg_loginContainer\")'>
+ <label for=\"feedDlg_loginCheck\">".
+ __('Feeds require authentication.')."</div>";
+
+ print "</form>";
+
+ print "</td></tr></table>";
+
+ print "<div class=\"dlgButtons\">
+ <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').execute()\">".__('Subscribe')."</button>
+ <button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button>
+ </div>";
+ }
+
+ function batchAddFeeds() {
+ $cat_id = clean($_REQUEST['cat']);
+ $feeds = explode("\n", clean($_REQUEST['feeds']));
+ $login = clean($_REQUEST['login']);
+ $pass = trim(clean($_REQUEST['pass']));
+
+ foreach ($feeds as $feed) {
+ $feed = trim($feed);
+
+ if (validate_feed_url($feed)) {
+
+ $this->pdo->beginTransaction();
+
+ $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
+ WHERE feed_url = ? AND owner_uid = ?");
+ $sth->execute([$feed, $_SESSION['uid']]);
+
+ if (!$sth->fetch()) {
+ $sth = $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)");
+
+ $sth->execute([$_SESSION['uid'], $feed, $cat_id ? $cat_id : null, $login, $pass]);
+ }
+
+ $this->pdo->commit();
+ }
+ }
+ }
+
+ function regenOPMLKey() {
+ $this->update_feed_access_key('OPML:Publish',
+ false, $_SESSION["uid"]);
+
+ $new_link = Opml::opml_publish_url();
+
+ print json_encode(array("link" => $new_link));
+ }
+
+ function regenFeedKey() {
+ $feed_id = clean($_REQUEST['id']);
+ $is_cat = clean($_REQUEST['is_cat']);
+
+ $new_key = $this->update_feed_access_key($feed_id, $is_cat);
+
+ print json_encode(["link" => $new_key]);
+ }
+
+
+ private function update_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
+ if (!$owner_uid) $owner_uid = $_SESSION["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 get_feed_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;
+ }
+
+ function getinactivefeeds() {
+ if (DB_TYPE == "pgsql") {
+ $interval_qpart = "NOW() - INTERVAL '3 months'";
+ } else {
+ $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
+ }
+
+ $sth = $this->pdo->prepare("SELECT COUNT(id) 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 = ?");
+ $sth->execute([$_SESSION['uid']]);
+
+ if ($row = $sth->fetch()) {
+ print (int)$row["num_inactive"];
+ }
+ }
+
+ static function subscribe_to_feed_url() {
+ $url_path = get_self_url_prefix() .
+ "/public.php?op=subscribe&feed_url=%s";
+ return $url_path;
+ }
+