diff options
Diffstat (limited to 'classes/pref')
-rwxr-xr-x | classes/pref/feeds.php | 607 | ||||
-rwxr-xr-x | classes/pref/filters.php | 132 | ||||
-rw-r--r-- | classes/pref/labels.php | 25 | ||||
-rw-r--r-- | classes/pref/prefs.php | 257 | ||||
-rw-r--r-- | classes/pref/system.php | 21 | ||||
-rw-r--r-- | classes/pref/users.php | 18 |
6 files changed, 575 insertions, 485 deletions
diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 5f7635736..b90ce49b4 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -5,29 +5,25 @@ class Pref_Feeds extends Handler_Protected { const E_ICON_UPLOAD_FAILED = 'E_ICON_UPLOAD_FAILED'; const E_ICON_UPLOAD_SUCCESS = 'E_ICON_UPLOAD_SUCCESS'; - function csrf_ignore($method) { + function csrf_ignore(string $method): bool { $csrf_ignored = array("index", "getfeedtree", "savefeedorder"); return array_search($method, $csrf_ignored) !== false; } - public static function get_ts_languages() { - $rv = []; - - if (Config::get(Config::DB_TYPE) == "pgsql") { - $dbh = Db::pdo(); - - $res = $dbh->query("SELECT cfgname FROM pg_ts_config"); - - while ($row = $res->fetch()) { - array_push($rv, ucfirst($row['cfgname'])); - } + /** + * @return array<int, string> + */ + 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 $rv; + return []; } - function renameCat() { + function renameCat(): void { $cat = ORM::for_table("ttrss_feed_categories") ->where("owner_uid", $_SESSION["uid"]) ->find_one($_REQUEST['id']); @@ -40,7 +36,10 @@ class Pref_Feeds extends Handler_Protected { } } - private function get_category_items($cat_id) { + /** + * @return array<int, array<string, bool|int|string>> + */ + private function get_category_items(int $cat_id): array { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; @@ -48,74 +47,76 @@ class Pref_Feeds extends Handler_Protected { $search = ""; // first one is set by API - $show_empty_cats = clean($_REQUEST['force_show_empty'] ?? false) || + $show_empty_cats = self::_param_to_bool($_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']); + $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%"]); } - $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); + 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() { + function getfeedtree(): void { print json_encode($this->_makefeedtree()); } - function _makefeedtree() { + /** + * @return array<string, array<int|string, mixed>|string> + */ + function _makefeedtree(): array { if (clean($_REQUEST['mode'] ?? 0) != 2) $search = $_SESSION["prefs_feed_search"] ?? ""; @@ -181,24 +182,23 @@ class Pref_Feeds extends Handler_Protected { if (get_pref(Prefs::ENABLE_FEED_CATS)) { $cat = $this->feedlist_init_cat(-2); } else { - $cat['items'] = array(); + $cat['items'] = []; } - $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); - } + $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 ($num_labels) { if ($enable_cats) { array_push($root['items'], $cat); } else { @@ -208,26 +208,29 @@ class Pref_Feeds extends Handler_Protected { } if ($enable_cats) { - $show_empty_cats = clean($_REQUEST['force_show_empty'] ?? false) || + $show_empty_cats = self::_param_to_bool($_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']); + $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); @@ -235,47 +238,48 @@ class Pref_Feeds extends Handler_Protected { if ($num_children > 0 || $show_empty_cats) array_push($root['items'], $cat); - $root['param'] += count($cat['items']); + //$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'); - $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); + 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'])); @@ -287,61 +291,59 @@ class Pref_Feeds extends Handler_Protected { $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); - } + $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'); - $root['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', count($root['items'])), count($root['items'])); - } + if ($search) { + $feeds_obj->where_raw('(LOWER(title) LIKE ? OR LOWER(feed_url) LIKE LOWER(?))', ["%$search%", "%$search%"]); + } - $fl = array(); - $fl['identifier'] = 'id'; - $fl['label'] = 'name'; + 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), + ]); + } - if (clean($_REQUEST['mode'] ?? 0) != 2) { - $fl['items'] = array($root); - } else { - $fl['items'] = $root['items']; + $root['param'] = sprintf(_ngettext('(%d feed)', '(%d feeds)', count($root['items'])), count($root['items'])); } - return $fl; + return [ + 'identifier' => 'id', + 'label' => 'name', + 'items' => clean($_REQUEST['mode'] ?? 0) != 2 ? [$root] : $root['items'], + ]; } - function catsortreset() { + function catsortreset(): void { $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); } - function feedsortreset() { + function feedsortreset(): void { $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) { + /** + * @param array<string, mixed> $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++) @@ -359,10 +361,14 @@ class Pref_Feeds extends Handler_Protected { $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']]); + $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; @@ -380,22 +386,27 @@ class Pref_Feeds extends Handler_Protected { 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']]); + $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); - $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories - SET order_id = ? WHERE id = ? AND - owner_uid = ?"); - $sth->execute([$order_id, $bare_id, $_SESSION['uid']]); + $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(); + } } } @@ -404,7 +415,7 @@ class Pref_Feeds extends Handler_Protected { } } - function savefeedorder() { + function savefeedorder(): void { $data = json_decode($_POST['payload'], true); #file_put_contents("/tmp/saveorder.json", clean($_POST['payload'])); @@ -418,8 +429,9 @@ class Pref_Feeds extends Handler_Protected { if (is_array($data) && is_array($data['items'])) { # $cat_order_id = 0; + /** @var array<int, mixed> */ $data_map = array(); - $root_item = false; + $root_item = ''; foreach ($data['items'] as $item) { @@ -440,7 +452,7 @@ class Pref_Feeds extends Handler_Protected { } } - function removeIcon() { + function removeIcon(): void { $feed_id = (int) $_REQUEST["feed_id"]; $icon_file = Config::get(Config::ICONS_DIR) . "/$feed_id.ico"; @@ -460,7 +472,7 @@ class Pref_Feeds extends Handler_Protected { } } - function uploadIcon() { + function uploadIcon(): void { $feed_id = (int) $_REQUEST['feed_id']; $tmp_file = tempnam(Config::get(Config::CACHE_DIR) . '/upload', 'icon'); @@ -503,7 +515,7 @@ class Pref_Feeds extends Handler_Protected { print json_encode(['rc' => $rc, 'icon_url' => Feeds::_get_icon($feed_id)]); } - function editfeed() { + function editfeed(): void { global $purge_intervals; global $update_intervals; @@ -539,6 +551,8 @@ class Pref_Feeds extends Handler_Protected { $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" => [ @@ -551,6 +565,9 @@ class Pref_Feeds extends Handler_Protected { "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), @@ -560,12 +577,12 @@ class Pref_Feeds extends Handler_Protected { } } - private function _batch_toggle_checkbox($name) { + 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() { + function editfeeds(): void { global $purge_intervals; global $update_intervals; @@ -673,15 +690,15 @@ class Pref_Feeds extends Handler_Protected { <?php } - function batchEditSave() { - return $this->editsaveops(true); + function batchEditSave(): void { + $this->editsaveops(true); } - function editSave() { - return $this->editsaveops(false); + function editSave(): void { + $this->editsaveops(false); } - private function editsaveops($batch) { + private function editsaveops(bool $batch): void { $feed_title = clean($_POST["title"]); $feed_url = clean($_POST["feed_url"]); @@ -770,11 +787,11 @@ class Pref_Feeds extends Handler_Protected { break; case "update_interval": - $qpart = "update_interval = " . $this->pdo->quote($upd_intl); + $qpart = "update_interval = " . $upd_intl; // made int above break; case "purge_interval": - $qpart = "purge_interval =" . $this->pdo->quote($purge_intl); + $qpart = "purge_interval = " . $purge_intl; // made int above break; case "auth_login": @@ -786,33 +803,33 @@ class Pref_Feeds extends Handler_Protected { break; case "private": - $qpart = "private = " . $this->pdo->quote($private); + $qpart = "private = " . $private; // made int above break; case "include_in_digest": - $qpart = "include_in_digest = " . $this->pdo->quote($include_in_digest); + $qpart = "include_in_digest = " . $include_in_digest; // made int above break; case "always_display_enclosures": - $qpart = "always_display_enclosures = " . $this->pdo->quote($always_display_enclosures); + $qpart = "always_display_enclosures = " . $always_display_enclosures; // made int above break; case "mark_unread_on_update": - $qpart = "mark_unread_on_update = " . $this->pdo->quote($mark_unread_on_update); + $qpart = "mark_unread_on_update = " . $mark_unread_on_update; // made int above break; case "cache_images": - $qpart = "cache_images = " . $this->pdo->quote($cache_images); + $qpart = "cache_images = " . $cache_images; // made int above break; case "hide_images": - $qpart = "hide_images = " . $this->pdo->quote($hide_images); + $qpart = "hide_images = " . $hide_images; // made int above break; case "cat_id": if (get_pref(Prefs::ENABLE_FEED_CATS)) { if ($cat_id) { - $qpart = "cat_id = " . $this->pdo->quote($cat_id); + $qpart = "cat_id = " . $cat_id; // made int above } else { $qpart = 'cat_id = NULL'; } @@ -837,39 +854,36 @@ class Pref_Feeds extends Handler_Protected { $this->pdo->commit(); } - return; } - function remove() { - - $ids = explode(",", clean($_REQUEST["ids"])); + function remove(): void { + /** @var array<int, int> */ + $ids = array_map('intval', explode(",", clean($_REQUEST["ids"]))); foreach ($ids as $id) { self::remove_feed($id, $_SESSION["uid"]); } - - return; } - function removeCat() { + function removeCat(): void { $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { Feeds::_remove_cat((int)$id, $_SESSION["uid"]); } } - function addCat() { + function addCat(): void { $feed_cat = clean($_REQUEST["cat"]); Feeds::_add_cat($feed_cat, $_SESSION['uid']); } - function importOpml() { + function importOpml(): void { $opml = new OPML($_REQUEST); $opml->opml_import($_SESSION["uid"]); } - private function index_feeds() { + private function index_feeds(): void { $error_button = "<button dojoType='dijit.form.Button' id='pref_feeds_errors_btn' style='display : none' onclick='CommonDialogs.showFeedsWithErrors()'>". @@ -980,7 +994,7 @@ class Pref_Feeds extends Handler_Protected { } - private function index_opml() { + private function index_opml(): void { ?> <form id='opml_import_form' method='post' enctype='multipart/form-data'> @@ -1016,7 +1030,7 @@ class Pref_Feeds extends Handler_Protected { PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsOPML"); } - private function index_shared() { + private function index_shared(): void { ?> <?= format_notice('Published articles can be subscribed by anyone who knows the following URL:') ?></h3> @@ -1036,7 +1050,7 @@ class Pref_Feeds extends Handler_Protected { PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsPublishedGenerated"); } - function index() { + function index(): void { ?> <div dojoType='dijit.layout.TabContainer' tabPosition='left-h'> @@ -1075,44 +1089,44 @@ class Pref_Feeds extends Handler_Protected { <?php } - private function feedlist_init_cat($cat_id) { - $obj = array(); - $cat_id = (int) $cat_id; - - $obj['id'] = 'CAT:' . $cat_id; - $obj['items'] = array(); - $obj['name'] = Feeds::_get_cat_title($cat_id); - $obj['type'] = 'category'; - $obj['unread'] = -1; //(int) Feeds::_get_cat_unread($cat_id); - $obj['bare_id'] = $cat_id; - - return $obj; + /** + * @return array<string, mixed> + */ + 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, + ]; } - private function feedlist_init_feed($feed_id, $title = false, $unread = false, $error = '', $updated = '') { - $obj = array(); - $feed_id = (int) $feed_id; - + /** + * @return array<string, mixed> + */ + 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 = getFeedUnread($feed_id, false); - - $obj['id'] = 'FEED:' . $feed_id; - $obj['name'] = $title; - $obj['unread'] = (int) $unread; - $obj['type'] = 'feed'; - $obj['error'] = $error; - $obj['updated'] = $updated; - $obj['icon'] = Feeds::_get_icon($feed_id); - $obj['bare_id'] = $feed_id; - $obj['auxcounter'] = 0; - - return $obj; + $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() { + function inactiveFeeds(): void { if (Config::get(Config::DB_TYPE) == "pgsql") { $interval_qpart = "NOW() - INTERVAL '3 months'"; @@ -1120,44 +1134,41 @@ class Pref_Feeds extends Handler_Protected { $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)"; } - $sth = $this->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); + $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($rv); + print json_encode($inactive_feeds); } - 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); + 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']) + ->find_array()); } - static function remove_feed($id, $owner_uid) { + 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; @@ -1198,19 +1209,26 @@ class Pref_Feeds extends Handler_Protected { } } - function batchSubscribe() { + function batchSubscribe(): void { print json_encode([ "enable_cats" => (int)get_pref(Prefs::ENABLE_FEED_CATS), "cat_select" => \Controls\select_feeds_cats("cat") ]); } - function batchAddFeeds() { + 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 = ?"); @@ -1236,22 +1254,22 @@ class Pref_Feeds extends Handler_Protected { } } - function clearKeys() { - return Feeds::_clear_access_keys($_SESSION['uid']); + function clearKeys(): void { + Feeds::_clear_access_keys($_SESSION['uid']); } - function regenFeedKey() { + function regenFeedKey(): void { $feed_id = clean($_REQUEST['id']); - $is_cat = clean($_REQUEST['is_cat']); + $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() { + function getSharedURL(): void { $feed_id = clean($_REQUEST['id']); - $is_cat = clean($_REQUEST['is_cat']) == "true"; + $is_cat = self::_param_to_bool($_REQUEST['is_cat'] ?? false); $search = clean($_REQUEST['search']); $link = Config::get_self_url() . "/public.php?" . http_build_query([ @@ -1268,11 +1286,14 @@ class Pref_Feeds extends Handler_Protected { ]); } - private function calculate_children_count($cat) { + /** + * @param array<string, mixed> $cat + */ + private function calculate_children_count(array $cat): int { $c = 0; foreach ($cat['items'] ?? [] as $child) { - if ($child['type'] ?? '' == 'category') { + if (($child['type'] ?? '') == 'category') { $c += $this->calculate_children_count($child); } else { $c += 1; diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 29d309dbb..04178f1a6 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -1,20 +1,28 @@ <?php class Pref_Filters extends Handler_Protected { - function csrf_ignore($method) { + const ACTION_TAG = 4; + const ACTION_SCORE = 6; + const ACTION_LABEL = 7; + const ACTION_PLUGIN = 9; + const ACTION_REMOVE_TAG = 10; + + const PARAM_ACTIONS = [self::ACTION_TAG, self::ACTION_SCORE, + self::ACTION_LABEL, self::ACTION_PLUGIN, self::ACTION_REMOVE_TAG]; + + function csrf_ignore(string $method): bool { $csrf_ignored = array("index", "getfiltertree", "savefilterorder"); return array_search($method, $csrf_ignored) !== false; } - function filtersortreset() { + function filtersortreset(): void { $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET order_id = 0 WHERE owner_uid = ?"); $sth->execute([$_SESSION['uid']]); - return; } - function savefilterorder() { + function savefilterorder(): void { $data = json_decode($_POST['payload'], true); #file_put_contents("/tmp/saveorder.json", clean($_POST['payload'])); @@ -40,11 +48,9 @@ class Pref_Filters extends Handler_Protected { } } } - - return; } - function testFilterDo() { + function testFilterDo(): void { $offset = (int) clean($_REQUEST["offset"]); $limit = (int) clean($_REQUEST["limit"]); @@ -59,7 +65,9 @@ class Pref_Filters extends Handler_Protected { $res = $this->pdo->query("SELECT id,name FROM ttrss_filter_types"); - $filter_types = array(); + /** @var array<int, string> */ + $filter_types = []; + while ($line = $res->fetch()) { $filter_types[$line["id"]] = $line["name"]; } @@ -67,7 +75,10 @@ class Pref_Filters extends Handler_Protected { $scope_qparts = array(); $rctr = 0; + + /** @var string $r */ foreach (clean($_REQUEST["rule"]) AS $r) { + /** @var array{'reg_exp': string, 'filter_type': int, 'feed_id': array<int, int|string>, 'name': string}|null */ $rule = json_decode($r, true); if ($rule && $rctr < 5) { @@ -75,19 +86,21 @@ class Pref_Filters extends Handler_Protected { unset($rule["filter_type"]); $scope_inner_qparts = []; + + /** @var int|string $feed_id may be a category string (e.g. 'CAT:7') or feed ID int */ foreach ($rule["feed_id"] as $feed_id) { - if (strpos($feed_id, "CAT:") === 0) { - $cat_id = (int) substr($feed_id, 4); - array_push($scope_inner_qparts, "cat_id = " . $this->pdo->quote($cat_id)); - } else if ($feed_id > 0) { - array_push($scope_inner_qparts, "feed_id = " . $this->pdo->quote($feed_id)); - } - } + if (strpos("$feed_id", "CAT:") === 0) { + $cat_id = (int) substr("$feed_id", 4); + array_push($scope_inner_qparts, "cat_id = " . $cat_id); + } else if (is_numeric($feed_id) && $feed_id > 0) { + array_push($scope_inner_qparts, "feed_id = " . (int)$feed_id); + } + } - if (count($scope_inner_qparts) > 0) { - array_push($scope_qparts, "(" . implode(" OR ", $scope_inner_qparts) . ")"); - } + if (count($scope_inner_qparts) > 0) { + array_push($scope_qparts, "(" . implode(" OR ", $scope_inner_qparts) . ")"); + } array_push($filter["rules"], $rule); @@ -162,7 +175,7 @@ class Pref_Filters extends Handler_Protected { print json_encode($rv); } - private function _get_rules_list($filter_id) { + private function _get_rules_list(int $filter_id): string { $sth = $this->pdo->prepare("SELECT reg_exp, inverse, match_on, @@ -203,7 +216,7 @@ class Pref_Filters extends Handler_Protected { } else { $where = $line["cat_filter"] ? - Feeds::_get_cat_title($line["cat_id"]) : + Feeds::_get_cat_title($line["cat_id"] ?? 0) : ($line["feed_id"] ? Feeds::_get_title($line["feed_id"]) : __("All feeds")); } @@ -222,7 +235,7 @@ class Pref_Filters extends Handler_Protected { return $rv; } - function getfiltertree() { + function getfiltertree(): void { $root = array(); $root['id'] = 'root'; $root['name'] = __('Filters'); @@ -270,7 +283,7 @@ class Pref_Filters extends Handler_Protected { } } - if ($line['action_id'] == 7) { + if ($line['action_id'] == self::ACTION_LABEL) { $label_sth = $this->pdo->prepare("SELECT fg_color, bg_color FROM ttrss_labels2 WHERE caption = ? AND owner_uid = ?"); @@ -307,10 +320,9 @@ class Pref_Filters extends Handler_Protected { $fl['items'] = array($root); print json_encode($fl); - return; } - function edit() { + function edit(): void { $filter_id = (int) clean($_REQUEST["id"] ?? 0); @@ -406,7 +418,10 @@ class Pref_Filters extends Handler_Protected { } } - private function _get_rule_name($rule) { + /** + * @param array<string, mixed>|null $rule + */ + private function _get_rule_name(?array $rule = null): string { if (!$rule) $rule = json_decode(clean($_REQUEST["rule"]), true); $feeds = $rule["feed_id"]; @@ -446,11 +461,18 @@ class Pref_Filters extends Handler_Protected { "<span class='field'>$filter_type</span>", "<span class='feed'>$feed</span>", isset($rule["inverse"]) ? __("(inverse)") : "") . "</span>"; } - function printRuleName() { + function printRuleName(): void { print $this->_get_rule_name(json_decode(clean($_REQUEST["rule"]), true)); } - private function _get_action_name($action) { + /** + * @param array<string, mixed>|null $action + */ + private function _get_action_name(?array $action = null): string { + if (!$action) { + return ""; + } + $sth = $this->pdo->prepare("SELECT description FROM ttrss_filter_actions WHERE id = ?"); $sth->execute([(int)$action["action_id"]]); @@ -461,11 +483,7 @@ class Pref_Filters extends Handler_Protected { $title = __($row["description"]); - if ($action["action_id"] == 4 || $action["action_id"] == 6 || - $action["action_id"] == 7) - $title .= ": " . $action["action_param"]; - - if ($action["action_id"] == 9) { + if ($action["action_id"] == self::ACTION_PLUGIN) { list ($pfclass, $pfaction) = explode(":", $action["action_param"]); $filter_actions = PluginHost::getInstance()->get_filter_actions(); @@ -478,18 +496,20 @@ class Pref_Filters extends Handler_Protected { } } } + } else if (in_array($action["action_id"], self::PARAM_ACTIONS)) { + $title .= ": " . $action["action_param"]; } } return $title; } - function printActionName() { - print $this->_get_action_name(json_decode(clean($_REQUEST["action"]), true)); + function printActionName(): void { + print $this->_get_action_name(json_decode(clean($_REQUEST["action"] ?? ""), true)); } - function editSave() { - $filter_id = clean($_REQUEST["id"]); + function editSave(): void { + $filter_id = (int) clean($_REQUEST["id"]); $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"] ?? false)); $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"] ?? false)); $inverse = checkbox_to_sql_bool(clean($_REQUEST["inverse"] ?? false)); @@ -510,7 +530,7 @@ class Pref_Filters extends Handler_Protected { $this->pdo->commit(); } - function remove() { + function remove(): void { $ids = explode(",", clean($_REQUEST["ids"])); $ids_qmarks = arr_qmarks($ids); @@ -520,7 +540,7 @@ class Pref_Filters extends Handler_Protected { $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - private function _save_rules_and_actions($filter_id) { + private function _save_rules_and_actions(int $filter_id): void { $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_rules WHERE filter_id = ?"); $sth->execute([$filter_id]); @@ -583,21 +603,26 @@ class Pref_Filters extends Handler_Protected { $action_param = $action["action_param"]; $action_param_label = $action["action_param_label"]; - if ($action_id == 7) { + if ($action_id == self::ACTION_LABEL) { $action_param = $action_param_label; } - if ($action_id == 6) { + if ($action_id == self::ACTION_SCORE) { $action_param = (int)str_replace("+", "", $action_param); } + if (in_array($action_id, [self::ACTION_TAG, self::ACTION_REMOVE_TAG])) { + $action_param = implode(", ", FeedItem_Common::normalize_categories( + explode(",", $action_param))); + } + $asth->execute([$filter_id, $action_id, $action_param]); } } } } - function add () { + function add(): void { $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"] ?? false)); $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"] ?? false)); $title = clean($_REQUEST["title"]); @@ -625,7 +650,7 @@ class Pref_Filters extends Handler_Protected { $this->pdo->commit(); } - function index() { + function index(): void { if (array_key_exists("search", $_REQUEST)) { $filter_search = clean($_REQUEST["search"]); $_SESSION["prefs_filter_search"] = $filter_search; @@ -691,7 +716,8 @@ class Pref_Filters extends Handler_Protected { <?php } - function editrule() { + function editrule(): void { + /** @var array<int, int|string> */ $feed_ids = explode(",", clean($_REQUEST["ids"])); print json_encode([ @@ -699,7 +725,10 @@ class Pref_Filters extends Handler_Protected { ]); } - private function _get_name($id) { + /** + * @return array<int, string> + */ + private function _get_name(int $id): array { $sth = $this->pdo->prepare( "SELECT title,match_any_rule,f.inverse AS inverse,COUNT(DISTINCT r.id) AS num_rules,COUNT(DISTINCT a.id) AS num_actions @@ -745,8 +774,9 @@ class Pref_Filters extends Handler_Protected { return []; } - function join() { - $ids = explode(",", clean($_REQUEST["ids"])); + function join(): void { + /** @var array<int, int> */ + $ids = array_map("intval", explode(",", clean($_REQUEST["ids"]))); if (count($ids) > 1) { $base_id = array_shift($ids); @@ -775,7 +805,7 @@ class Pref_Filters extends Handler_Protected { } } - private function _optimize($id) { + private function _optimize(int $id): void { $this->pdo->beginTransaction(); @@ -830,9 +860,11 @@ class Pref_Filters extends Handler_Protected { $this->pdo->commit(); } - private function _feed_multi_select($id, $default_ids = [], - $attributes = "", $include_all_feeds = true, - $root_id = null, $nest_level = 0) { + /** + * @param array<int, int|string> $default_ids + */ + private function _feed_multi_select(string $id, array $default_ids = [], string $attributes = "", + bool $include_all_feeds = true, ?int $root_id = null, int $nest_level = 0): string { $pdo = Db::pdo(); diff --git a/classes/pref/labels.php b/classes/pref/labels.php index 2cdb919ce..a50a85a66 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -1,13 +1,13 @@ <?php class Pref_Labels extends Handler_Protected { - function csrf_ignore($method) { + function csrf_ignore(string $method): bool { $csrf_ignored = array("index", "getlabeltree"); return array_search($method, $csrf_ignored) !== false; } - function edit() { + function edit(): void { $label = ORM::for_table('ttrss_labels2') ->where('owner_uid', $_SESSION['uid']) ->find_one($_REQUEST['id']); @@ -17,7 +17,7 @@ class Pref_Labels extends Handler_Protected { } } - function getlabeltree() { + function getlabeltree(): void { $root = array(); $root['id'] = 'root'; $root['name'] = __('Labels'); @@ -48,10 +48,9 @@ class Pref_Labels extends Handler_Protected { $fl['items'] = array($root); print json_encode($fl); - return; } - function colorset() { + function colorset(): void { $kind = clean($_REQUEST["kind"]); $ids = explode(',', clean($_REQUEST["ids"])); $color = clean($_REQUEST["color"]); @@ -84,7 +83,7 @@ class Pref_Labels extends Handler_Protected { } } - function colorreset() { + function colorreset(): void { $ids = explode(',', clean($_REQUEST["ids"])); foreach ($ids as $id) { @@ -101,7 +100,7 @@ class Pref_Labels extends Handler_Protected { } } - function save() { + function save(): void { $id = clean($_REQUEST["id"]); $caption = clean($_REQUEST["caption"]); @@ -148,9 +147,9 @@ class Pref_Labels extends Handler_Protected { } - function remove() { - - $ids = explode(",", clean($_REQUEST["ids"])); + function remove(): void { + /** @var array<int, int> */ + $ids = array_map("intval", explode(",", clean($_REQUEST["ids"]))); foreach ($ids as $id) { Labels::remove($id, $_SESSION["uid"]); @@ -158,9 +157,9 @@ class Pref_Labels extends Handler_Protected { } - function add() { + function add(): void { $caption = clean($_REQUEST["caption"]); - $output = clean($_REQUEST["output"]); + $output = clean($_REQUEST["output"] ?? false); if ($caption) { if (Labels::create($caption)) { @@ -171,7 +170,7 @@ class Pref_Labels extends Handler_Protected { } } - function index() { + function index(): void { ?> <div dojoType='dijit.layout.BorderContainer' gutters='false'> <div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='top'> diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index cb666e945..2d72a7732 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -2,12 +2,21 @@ use chillerlan\QRCode; class Pref_Prefs extends Handler_Protected { - + // TODO: class properties can be switched to PHP typing if/when the minimum PHP_VERSION is raised to 7.4.0+ + /** @var array<Prefs::*, array<int, string>> */ private $pref_help = []; + + /** @var array<string, array<int, string>> pref items are Prefs::*|Pref_Prefs::BLOCK_SEPARATOR (PHPStan was complaining) */ private $pref_item_map = []; + + /** @var array<string, string> */ private $pref_help_bottom = []; + + /** @var array<int, string> */ private $pref_blacklist = []; + private const BLOCK_SEPARATOR = 'BLOCK_SEPARATOR'; + const PI_RES_ALREADY_INSTALLED = "PI_RES_ALREADY_INSTALLED"; const PI_RES_SUCCESS = "PI_RES_SUCCESS"; const PI_ERR_NO_CLASS = "PI_ERR_NO_CLASS"; @@ -17,7 +26,8 @@ class Pref_Prefs extends Handler_Protected { const PI_ERR_PLUGIN_NOT_FOUND = "PI_ERR_PLUGIN_NOT_FOUND"; const PI_ERR_NO_WORKDIR = "PI_ERR_NO_WORKDIR"; - function csrf_ignore($method) { + /** @param string $method */ + function csrf_ignore(string $method) : bool { $csrf_ignored = array("index", "updateself", "otpqrcode"); return array_search($method, $csrf_ignored) !== false; @@ -30,35 +40,35 @@ class Pref_Prefs extends Handler_Protected { __('General') => [ Prefs::USER_LANGUAGE, Prefs::USER_TIMEZONE, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::USER_CSS_THEME, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::ENABLE_API_ACCESS, ], __('Feeds') => [ Prefs::DEFAULT_UPDATE_INTERVAL, Prefs::FRESH_ARTICLE_MAX_AGE, Prefs::DEFAULT_SEARCH_LANGUAGE, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::ENABLE_FEED_CATS, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::CONFIRM_FEED_CATCHUP, Prefs::ON_CATCHUP_SHOW_NEXT_FEED, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::HIDE_READ_FEEDS, Prefs::HIDE_READ_SHOWS_SPECIAL, ], __('Articles') => [ Prefs::PURGE_OLD_DAYS, Prefs::PURGE_UNREAD_ARTICLES, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::COMBINED_DISPLAY_MODE, Prefs::CDM_EXPANDED, Prefs::CDM_ENABLE_GRID, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::CDM_AUTO_CATCHUP, Prefs::VFEED_GROUP_BY_FEED, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::SHOW_CONTENT_PREVIEW, Prefs::STRIP_IMAGES, ], @@ -69,12 +79,12 @@ class Pref_Prefs extends Handler_Protected { ], __('Advanced') => [ Prefs::BLACKLISTED_TAGS, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::LONG_DATE_FORMAT, Prefs::SHORT_DATE_FORMAT, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::SSL_CERT_SERIAL, - 'BLOCK_SEPARATOR', + self::BLOCK_SEPARATOR, Prefs::DISABLE_CONDITIONAL_COUNTERS, Prefs::HEADLINES_NO_DISTINCT, ], @@ -127,7 +137,7 @@ class Pref_Prefs extends Handler_Protected { ]; } - function changepassword() { + function changepassword(): void { if (Config::get(Config::FORBID_PASSWORD_CHANGES)) { print "ERROR: ".format_error("Access forbidden."); @@ -173,7 +183,7 @@ class Pref_Prefs extends Handler_Protected { } } - function saveconfig() { + function saveconfig(): void { $boolean_prefs = explode(",", clean($_POST["boolean_prefs"])); foreach ($boolean_prefs as $pref) { @@ -223,7 +233,7 @@ class Pref_Prefs extends Handler_Protected { } } - function changePersonalData() { + function changePersonalData(): void { $user = ORM::for_table('ttrss_users')->find_one($_SESSION['uid']); $new_email = clean($_POST['email']); @@ -264,13 +274,13 @@ class Pref_Prefs extends Handler_Protected { print __("Your personal data has been saved."); } - function resetconfig() { + function resetconfig(): void { Prefs::reset($_SESSION["uid"], $_SESSION["profile"]); print "PREFS_NEED_RELOAD"; } - private function index_auth_personal() { + private function index_auth_personal(): void { $user = ORM::for_table('ttrss_users')->find_one($_SESSION['uid']); @@ -310,7 +320,7 @@ class Pref_Prefs extends Handler_Protected { <?php } - private function index_auth_password() { + private function index_auth_password(): void { if ($_SESSION["auth_module"]) { $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); } else { @@ -385,7 +395,7 @@ class Pref_Prefs extends Handler_Protected { } } - private function index_auth_app_passwords() { + private function index_auth_app_passwords(): void { print_notice("Separate passwords used for API clients. Required if you enable OTP."); ?> @@ -409,7 +419,7 @@ class Pref_Prefs extends Handler_Protected { <?php } - private function index_auth_2fa() { + private function index_auth_2fa(): void { $otp_enabled = UserHelper::is_otp_enabled($_SESSION["uid"]); if ($_SESSION["auth_module"] == "auth_internal") { @@ -515,7 +525,7 @@ class Pref_Prefs extends Handler_Protected { } } - function index_auth() { + function index_auth(): void { ?> <div dojoType='dijit.layout.TabContainer'> <div dojoType='dijit.layout.ContentPane' title="<?= __('Personal data') ?>"> @@ -534,35 +544,38 @@ class Pref_Prefs extends Handler_Protected { <?php } - private function index_prefs_list() { + private function index_prefs_list(): void { $profile = $_SESSION["profile"] ?? null; if ($profile) { print_notice(__("Some preferences are only available in default profile.")); } + /** @var array<string, array{'type_hint': Config::T_*, 'value': bool|int|string, 'help_text': string, 'short_desc': string}> */ $prefs_available = []; + + /** @var array<int, string> */ $listed_boolean_prefs = []; - foreach (Prefs::get_all($_SESSION["uid"], $profile) as $line) { + foreach (Prefs::get_all($_SESSION["uid"], $profile) as $pref) { - if (in_array($line["pref_name"], $this->pref_blacklist)) { + if (in_array($pref["pref_name"], $this->pref_blacklist)) { continue; } - if ($profile && in_array($line["pref_name"], Prefs::_PROFILE_BLACKLIST)) { + if ($profile && in_array($pref["pref_name"], Prefs::_PROFILE_BLACKLIST)) { continue; } - $pref_name = $line["pref_name"]; + $pref_name = $pref["pref_name"]; $short_desc = $this->_get_short_desc($pref_name); if (!$short_desc) continue; $prefs_available[$pref_name] = [ - 'type_hint' => $line['type_hint'], - 'value' => $line['value'], + 'type_hint' => $pref['type_hint'], + 'value' => $pref['value'], 'help_text' => $this->_get_help_text($pref_name), 'short_desc' => $short_desc ]; @@ -574,12 +587,12 @@ class Pref_Prefs extends Handler_Protected { foreach ($this->pref_item_map[$section] as $pref_name) { - if ($pref_name == 'BLOCK_SEPARATOR' && !$profile) { + if ($pref_name == self::BLOCK_SEPARATOR && !$profile) { print "<hr/>"; continue; } - if ($pref_name == "DEFAULT_SEARCH_LANGUAGE" && Config::get(Config::DB_TYPE) != "pgsql") { + if ($pref_name == Prefs::DEFAULT_SEARCH_LANGUAGE && Config::get(Config::DB_TYPE) != "pgsql") { continue; } @@ -596,17 +609,17 @@ class Pref_Prefs extends Handler_Protected { $value = $item['value']; $type_hint = $item['type_hint']; - if ($pref_name == "USER_LANGUAGE") { + if ($pref_name == Prefs::USER_LANGUAGE) { print \Controls\select_hash($pref_name, $value, get_translations(), ["style" => 'width : 220px; margin : 0px']); - } else if ($pref_name == "USER_TIMEZONE") { + } else if ($pref_name == Prefs::USER_TIMEZONE) { $timezones = explode("\n", file_get_contents("lib/timezones.txt")); print \Controls\select_tag($pref_name, $value, $timezones, ["dojoType" => "dijit.form.FilteringSelect"]); - } else if ($pref_name == "BLACKLISTED_TAGS") { # TODO: other possible <textarea> prefs go here + } else if ($pref_name == Prefs::BLACKLISTED_TAGS) { # TODO: other possible <textarea> prefs go here print "<div>"; @@ -618,7 +631,7 @@ class Pref_Prefs extends Handler_Protected { print "</div>"; - } else if ($pref_name == "USER_CSS_THEME") { + } else if ($pref_name == Prefs::USER_CSS_THEME) { $theme_files = array_map("basename", array_merge(glob("themes/*.php"), @@ -642,13 +655,13 @@ class Pref_Prefs extends Handler_Protected { <?php - } else if ($pref_name == "DEFAULT_UPDATE_INTERVAL") { + } else if ($pref_name == Prefs::DEFAULT_UPDATE_INTERVAL) { global $update_intervals_nodefault; print \Controls\select_hash($pref_name, $value, $update_intervals_nodefault); - } else if ($pref_name == "DEFAULT_SEARCH_LANGUAGE") { + } else if ($pref_name == Prefs::DEFAULT_SEARCH_LANGUAGE) { print \Controls\select_tag($pref_name, $value, Pref_Feeds::get_ts_languages()); @@ -656,7 +669,7 @@ class Pref_Prefs extends Handler_Protected { array_push($listed_boolean_prefs, $pref_name); - if ($pref_name == "PURGE_UNREAD_ARTICLES" && Config::get(Config::FORCE_ARTICLE_PURGE) != 0) { + if ($pref_name == Prefs::PURGE_UNREAD_ARTICLES && Config::get(Config::FORCE_ARTICLE_PURGE) != 0) { $is_disabled = true; $is_checked = true; } else { @@ -672,10 +685,10 @@ class Pref_Prefs extends Handler_Protected { ['onclick' => 'Helpers.Digest.preview()', 'style' => 'margin-left : 10px']); } - } else if (in_array($pref_name, ['FRESH_ARTICLE_MAX_AGE', - 'PURGE_OLD_DAYS', 'LONG_DATE_FORMAT', 'SHORT_DATE_FORMAT'])) { + } else if (in_array($pref_name, [Prefs::FRESH_ARTICLE_MAX_AGE, + Prefs::PURGE_OLD_DAYS, Prefs::LONG_DATE_FORMAT, Prefs::SHORT_DATE_FORMAT])) { - if ($pref_name == "PURGE_OLD_DAYS" && Config::get(Config::FORCE_ARTICLE_PURGE) != 0) { + if ($pref_name == Prefs::PURGE_OLD_DAYS && Config::get(Config::FORCE_ARTICLE_PURGE) != 0) { $attributes = ["disabled" => true, "required" => true]; $value = Config::get(Config::FORCE_ARTICLE_PURGE); } else { @@ -687,7 +700,7 @@ class Pref_Prefs extends Handler_Protected { else print \Controls\input_tag($pref_name, $value, "text", $attributes); - } else if ($pref_name == "SSL_CERT_SERIAL") { + } else if ($pref_name == Prefs::SSL_CERT_SERIAL) { print \Controls\input_tag($pref_name, $value, "text", ["readonly" => true], "SSL_CERT_SERIAL"); @@ -727,7 +740,7 @@ class Pref_Prefs extends Handler_Protected { print \Controls\hidden_tag("boolean_prefs", htmlspecialchars(join(",", $listed_boolean_prefs))); } - private function index_prefs() { + private function index_prefs(): void { ?> <form dojoType='dijit.form.Form' id='changeSettingsForm'> <?= \Controls\hidden_tag("op", "pref-prefs") ?> @@ -783,7 +796,7 @@ class Pref_Prefs extends Handler_Protected { <?php } - function getPluginsList() { + function getPluginsList(): void { $system_enabled = array_map("trim", explode(",", (string)Config::get(Config::PLUGINS))); $user_enabled = array_map("trim", explode(",", get_pref(Prefs::_ENABLED_PLUGINS))); @@ -813,10 +826,10 @@ class Pref_Prefs extends Handler_Protected { usort($rv, function($a, $b) { return strcmp($a["name"], $b["name"]); }); - print json_encode(['plugins' => $rv, 'is_admin' => $_SESSION['access_level'] >= 10]); + print json_encode(['plugins' => $rv, 'is_admin' => $_SESSION['access_level'] >= UserHelper::ACCESS_LEVEL_ADMIN]); } - function index_plugins() { + function index_plugins(): void { ?> <form dojoType="dijit.form.Form" id="changePluginsForm"> @@ -890,7 +903,7 @@ class Pref_Prefs extends Handler_Protected { <?= \Controls\button_tag(\Controls\icon("refresh"), "", ["title" => __("Reload"), "onclick" => "Helpers.Plugins.reload()"]) ?> - <?php if ($_SESSION["access_level"] >= 10) { ?> + <?php if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN) { ?> <?php if (Config::get(Config::CHECK_FOR_UPDATES) && Config::get(Config::CHECK_FOR_PLUGIN_UPDATES)) { ?> <button class='alt-warning' dojoType='dijit.form.Button' onclick="Helpers.Plugins.update()"> @@ -912,7 +925,7 @@ class Pref_Prefs extends Handler_Protected { <?php } - function index() { + function index(): void { ?> <div dojoType='dijit.layout.AccordionContainer' region='center'> <div dojoType='dijit.layout.AccordionPane' title="<i class='material-icons'>person</i> <?= __('Personal data / Authentication')?>"> @@ -937,7 +950,7 @@ class Pref_Prefs extends Handler_Protected { <?php } - function _get_otp_qrcode_img() { + function _get_otp_qrcode_img(): ?string { $secret = UserHelper::get_otp_secret($_SESSION["uid"]); $login = UserHelper::get_login_by_id($_SESSION["uid"]); @@ -949,15 +962,16 @@ class Pref_Prefs extends Handler_Protected { return $qrcode->render($otpurl); } - return false; + return null; } - function otpenable() { + function otpenable(): void { $password = clean($_REQUEST["password"]); $otp_check = clean($_REQUEST["otp"]); $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); + /** @var Auth_Internal|false $authenticator -- this is only here to make check_password() visible to static analyzer */ if ($authenticator->check_password($_SESSION["uid"], $password)) { if (UserHelper::enable_otp($_SESSION["uid"], $otp_check)) { print "OK"; @@ -969,9 +983,10 @@ class Pref_Prefs extends Handler_Protected { } } - function otpdisable() { + function otpdisable(): void { $password = clean($_REQUEST["password"]); + /** @var Auth_Internal|false $authenticator -- this is only here to make check_password() visible to static analyzer */ $authenticator = PluginHost::getInstance()->get_plugin($_SESSION["auth_module"]); if ($authenticator->check_password($_SESSION["uid"], $password)) { @@ -1008,38 +1023,42 @@ class Pref_Prefs extends Handler_Protected { } - function setplugins() { + function setplugins(): void { $plugins = array_filter($_REQUEST["plugins"], 'clean') ?? []; set_pref(Prefs::_ENABLED_PLUGINS, implode(",", $plugins)); } - function _get_plugin_version(Plugin $plugin) { + function _get_plugin_version(Plugin $plugin): string { $about = $plugin->about(); if (!empty($about[0])) { return T_sprintf("v%.2f, by %s", $about[0], $about[2]); - } else { - $ref = new ReflectionClass(get_class($plugin)); + } - $plugin_dir = dirname($ref->getFileName()); + $ref = new ReflectionClass(get_class($plugin)); - if (basename($plugin_dir) == "plugins") { - return ""; - } + $plugin_dir = dirname($ref->getFileName()); - if (is_dir("$plugin_dir/.git")) { - $ver = Config::get_version_from_git($plugin_dir); + if (basename($plugin_dir) == "plugins") { + return ""; + } - return $ver["status"] == 0 ? T_sprintf("v%s, by %s", $ver["version"], $about[2]) : $ver["version"]; - } + if (is_dir("$plugin_dir/.git")) { + $ver = Config::get_version_from_git($plugin_dir); + + return $ver["status"] == 0 ? T_sprintf("v%s, by %s", $ver["version"], $about[2]) : $ver["version"]; } + + return ""; } - static function _get_updated_plugins() { + /** + * @return array<int, array{'plugin': string, 'rv': array{'stdout': false|string, 'stderr': false|string, 'git_status': int, 'need_update': bool}|null}> + */ + static function _get_updated_plugins(): array { $root_dir = dirname(dirname(__DIR__)); # we're in classes/pref/ $plugin_dirs = array_filter(glob("$root_dir/plugins.local/*"), "is_dir"); - $rv = []; foreach ($plugin_dirs as $dir) { @@ -1057,7 +1076,10 @@ class Pref_Prefs extends Handler_Protected { return $rv; } - private static function _plugin_needs_update($root_dir, $plugin_name) { + /** + * @return array{'stdout': false|string, 'stderr': false|string, 'git_status': int, 'need_update': bool}|null + */ + private static function _plugin_needs_update(string $root_dir, string $plugin_name): ?array { $plugin_dir = "$root_dir/plugins.local/" . basename($plugin_name); $rv = null; @@ -1086,7 +1108,10 @@ class Pref_Prefs extends Handler_Protected { } - private function _update_plugin($root_dir, $plugin_name) { + /** + * @return array{'stdout': false|string, 'stderr': false|string, 'git_status': int} + */ + private function _update_plugin(string $root_dir, string $plugin_name): array { $plugin_dir = "$root_dir/plugins.local/" . basename($plugin_name); $rv = []; @@ -1112,7 +1137,7 @@ class Pref_Prefs extends Handler_Protected { } // https://gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2#gistcomment-2036828 - private function _recursive_rmdir(string $dir, bool $keep_root = false) { + private function _recursive_rmdir(string $dir, bool $keep_root = false): bool { // Handle bad arguments. if (empty($dir) || !file_exists($dir)) { return true; // No such file/dir$dir exists. @@ -1137,7 +1162,7 @@ class Pref_Prefs extends Handler_Protected { } // https://stackoverflow.com/questions/7153000/get-class-name-from-file - private function _get_class_name_from_file($file) { + private function _get_class_name_from_file(string $file): string { $tokens = token_get_all(file_get_contents($file)); for ($i = 0; $i < count($tokens); $i++) { @@ -1149,10 +1174,12 @@ class Pref_Prefs extends Handler_Protected { } } } + + return ""; } - function uninstallPlugin() { - if ($_SESSION["access_level"] >= 10) { + function uninstallPlugin(): void { + if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN) { $plugin_name = basename(clean($_REQUEST['plugin'])); $status = 0; @@ -1166,8 +1193,8 @@ class Pref_Prefs extends Handler_Protected { } } - function installPlugin() { - if ($_SESSION["access_level"] >= 10 && Config::get(Config::ENABLE_PLUGIN_INSTALLER)) { + function installPlugin(): void { + if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN && Config::get(Config::ENABLE_PLUGIN_INSTALLER)) { $plugin_name = basename(clean($_REQUEST['plugin'])); $all_plugins = $this->_get_available_plugins(); $plugin_dir = dirname(dirname(__DIR__)) . "/plugins.local"; @@ -1251,47 +1278,59 @@ class Pref_Prefs extends Handler_Protected { } } - private function _get_available_plugins() { - if ($_SESSION["access_level"] >= 10 && Config::get(Config::ENABLE_PLUGIN_INSTALLER)) { - return json_decode(UrlHelper::fetch(['url' => 'https://tt-rss.org/plugins.json']), true); + /** + * @return array<int, array{'name': string, 'description': string, 'topics': array<int, string>, 'html_url': string, 'clone_url': string, 'last_update': string}> + */ + private function _get_available_plugins(): array { + if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN && Config::get(Config::ENABLE_PLUGIN_INSTALLER)) { + $content = json_decode(UrlHelper::fetch(['url' => 'https://tt-rss.org/plugins.json']), true); + + if ($content) { + return $content; + } } + + return []; } - function getAvailablePlugins() { - if ($_SESSION["access_level"] >= 10) { + + function getAvailablePlugins(): void { + if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN) { print json_encode($this->_get_available_plugins()); + } else { + print "[]"; } } - function checkForPluginUpdates() { - if ($_SESSION["access_level"] >= 10 && Config::get(Config::CHECK_FOR_UPDATES) && Config::get(Config::CHECK_FOR_PLUGIN_UPDATES)) { + function checkForPluginUpdates(): void { + if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN && Config::get(Config::CHECK_FOR_UPDATES) && Config::get(Config::CHECK_FOR_PLUGIN_UPDATES)) { $plugin_name = $_REQUEST["name"] ?? ""; - $root_dir = dirname(dirname(__DIR__)); # we're in classes/pref/ - if (!empty($plugin_name)) { - $rv = [["plugin" => $plugin_name, "rv" => self::_plugin_needs_update($root_dir, $plugin_name)]]; - } else { - $rv = self::_get_updated_plugins(); - } + $rv = empty($plugin_name) ? self::_get_updated_plugins() : [ + ["plugin" => $plugin_name, "rv" => self::_plugin_needs_update($root_dir, $plugin_name)], + ]; print json_encode($rv); } } - function updateLocalPlugins() { - if ($_SESSION["access_level"] >= 10) { + function updateLocalPlugins(): void { + if ($_SESSION["access_level"] >= UserHelper::ACCESS_LEVEL_ADMIN) { $plugins = explode(",", $_REQUEST["plugins"] ?? ""); + if ($plugins !== false) { + $plugins = array_filter($plugins, 'strlen'); + } + # we're in classes/pref/ $root_dir = dirname(dirname(__DIR__)); $rv = []; - if (count($plugins) > 0) { + if ($plugins) { foreach ($plugins as $plugin_name) { array_push($rv, ["plugin" => $plugin_name, "rv" => $this->_update_plugin($root_dir, $plugin_name)]); } - // @phpstan-ignore-next-line } else { $plugin_dirs = array_filter(glob("$root_dir/plugins.local/*"), "is_dir"); @@ -1301,7 +1340,7 @@ class Pref_Prefs extends Handler_Protected { $test = self::_plugin_needs_update($root_dir, $plugin_name); - if (!empty($test["o"])) + if (!empty($test["stdout"])) array_push($rv, ["plugin" => $plugin_name, "rv" => $this->_update_plugin($root_dir, $plugin_name)]); } } @@ -1311,21 +1350,21 @@ class Pref_Prefs extends Handler_Protected { } } - function clearplugindata() { + function clearplugindata(): void { $name = clean($_REQUEST["name"]); PluginHost::getInstance()->clear_data(PluginHost::getInstance()->get_plugin($name)); } - function customizeCSS() { + function customizeCSS(): void { $value = get_pref(Prefs::USER_STYLESHEET); $value = str_replace("<br/>", "\n", $value); print json_encode(["value" => $value]); } - function activateprofile() { - $id = (int) $_REQUEST['id'] ?? 0; + function activateprofile(): void { + $id = (int) ($_REQUEST['id'] ?? 0); $profile = ORM::for_table('ttrss_settings_profiles') ->where('owner_uid', $_SESSION['uid']) @@ -1338,7 +1377,7 @@ class Pref_Prefs extends Handler_Protected { } } - function cloneprofile() { + function cloneprofile(): void { $old_profile = $_REQUEST["old_profile"] ?? 0; $new_title = clean($_REQUEST["new_title"]); @@ -1367,7 +1406,7 @@ class Pref_Prefs extends Handler_Protected { } } - function remprofiles() { + function remprofiles(): void { $ids = $_REQUEST["ids"] ?? []; ORM::for_table('ttrss_settings_profiles') @@ -1377,7 +1416,7 @@ class Pref_Prefs extends Handler_Protected { ->delete_many(); } - function addprofile() { + function addprofile(): void { $title = clean($_REQUEST["title"]); if ($title) { @@ -1396,7 +1435,7 @@ class Pref_Prefs extends Handler_Protected { } } - function saveprofile() { + function saveprofile(): void { $id = (int)$_REQUEST["id"]; $title = clean($_REQUEST["value"]); @@ -1413,7 +1452,7 @@ class Pref_Prefs extends Handler_Protected { } // TODO: this maybe needs to be unified with Public::getProfiles() - function getProfiles() { + function getProfiles(): void { $rv = []; $profiles = ORM::for_table('ttrss_settings_profiles') @@ -1442,21 +1481,21 @@ class Pref_Prefs extends Handler_Protected { print json_encode($rv); } - private function _get_short_desc($pref_name) { + private function _get_short_desc(string $pref_name): string { if (isset($this->pref_help[$pref_name][0])) { return $this->pref_help[$pref_name][0]; } return ""; } - private function _get_help_text($pref_name) { + private function _get_help_text(string $pref_name): string { if (isset($this->pref_help[$pref_name][1])) { return $this->pref_help[$pref_name][1]; } return ""; } - private function appPasswordList() { + private function appPasswordList(): void { ?> <div dojoType='fox.Toolbar'> <div dojoType='fox.form.DropDownButton'> @@ -1506,7 +1545,7 @@ class Pref_Prefs extends Handler_Protected { <?php } - function deleteAppPasswords() { + function deleteAppPasswords(): void { $passwords = ORM::for_table('ttrss_app_passwords') ->where('owner_uid', $_SESSION['uid']) ->where_in('id', $_REQUEST['ids'] ?? []) @@ -1515,7 +1554,7 @@ class Pref_Prefs extends Handler_Protected { $this->appPasswordList(); } - function generateAppPassword() { + function generateAppPassword(): void { $title = clean($_REQUEST['title']); $new_password = make_password(16); $new_salt = UserHelper::get_salt(); @@ -1536,11 +1575,11 @@ class Pref_Prefs extends Handler_Protected { $this->appPasswordList(); } - function previewDigest() { + function previewDigest(): void { print json_encode(Digest::prepare_headlines_digest($_SESSION["uid"], 1, 16)); } - static function _get_ssl_certificate_id() { + static function _get_ssl_certificate_id(): string { if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] ?? false) { return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] . $_SERVER["REDIRECT_SSL_CLIENT_V_START"] . @@ -1556,7 +1595,7 @@ class Pref_Prefs extends Handler_Protected { return ""; } - private function format_otp_secret($secret) { + private function format_otp_secret(string $secret): string { return implode(" ", str_split($secret, 4)); } } diff --git a/classes/pref/system.php b/classes/pref/system.php index 8bebcc7ce..10f196b55 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -2,19 +2,19 @@ class Pref_System extends Handler_Administrative { - private $log_page_limit = 15; + private const LOG_PAGE_LIMIT = 15; - function csrf_ignore($method) { + function csrf_ignore(string $method): bool { $csrf_ignored = array("index"); return array_search($method, $csrf_ignored) !== false; } - function clearLog() { + function clearLog(): void { $this->pdo->query("DELETE FROM ttrss_error_log"); } - function sendTestEmail() { + function sendTestEmail(): void { $mail_address = clean($_REQUEST["mail_address"]); $mailer = new Mailer(); @@ -28,7 +28,7 @@ class Pref_System extends Handler_Administrative { print json_encode(['rc' => $rc, 'error' => $mailer->error()]); } - function getphpinfo() { + function getphpinfo(): void { ob_start(); phpinfo(); $info = ob_get_contents(); @@ -37,7 +37,7 @@ class Pref_System extends Handler_Administrative { print preg_replace( '%^.*<body>(.*)</body>.*$%ms','$1', (string)$info); } - private function _log_viewer(int $page, int $severity) { + private function _log_viewer(int $page, int $severity): void { $errno_values = []; switch ($severity) { @@ -56,8 +56,7 @@ class Pref_System extends Handler_Administrative { $errno_filter_qpart = "true"; } - $limit = $this->log_page_limit; - $offset = $limit * $page; + $offset = self::LOG_PAGE_LIMIT * $page; $sth = $this->pdo->prepare("SELECT COUNT(id) AS total_pages @@ -69,7 +68,7 @@ class Pref_System extends Handler_Administrative { $sth->execute($errno_values); if ($res = $sth->fetch()) { - $total_pages = (int)($res["total_pages"] / $limit); + $total_pages = (int)($res["total_pages"] / self::LOG_PAGE_LIMIT); } else { $total_pages = 0; } @@ -134,7 +133,7 @@ class Pref_System extends Handler_Administrative { $errno_filter_qpart ORDER BY ttrss_error_log.id DESC - LIMIT $limit OFFSET $offset"); + LIMIT ". self::LOG_PAGE_LIMIT ." OFFSET $offset"); $sth->execute($errno_values); @@ -159,7 +158,7 @@ class Pref_System extends Handler_Administrative { <?php } - function index() { + function index(): void { $severity = (int) ($_REQUEST["severity"] ?? E_USER_WARNING); $page = (int) ($_REQUEST["page"] ?? 0); diff --git a/classes/pref/users.php b/classes/pref/users.php index 76a879efd..c48619614 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -1,10 +1,10 @@ <?php class Pref_Users extends Handler_Administrative { - function csrf_ignore($method) { + function csrf_ignore(string $method): bool { return $method == "index"; } - function edit() { + function edit(): void { $user = ORM::for_table('ttrss_users') ->select_expr("id,login,access_level,email,full_name,otp_enabled") ->find_one((int)$_REQUEST["id"]) @@ -20,7 +20,7 @@ class Pref_Users extends Handler_Administrative { } } - function userdetails() { + function userdetails(): void { $id = (int) clean($_REQUEST["id"]); $sth = $this->pdo->prepare("SELECT login, @@ -103,7 +103,7 @@ class Pref_Users extends Handler_Administrative { } - function editSave() { + function editSave(): void { $id = (int)$_REQUEST['id']; $password = clean($_REQUEST["password"]); $user = ORM::for_table('ttrss_users')->find_one($id); @@ -132,7 +132,7 @@ class Pref_Users extends Handler_Administrative { } } - function remove() { + function remove(): void { $ids = explode(",", clean($_REQUEST["ids"])); foreach ($ids as $id) { @@ -149,7 +149,7 @@ class Pref_Users extends Handler_Administrative { } } - function add() { + function add(): void { $login = clean($_REQUEST["login"]); if (!$login) return; // no blank usernames @@ -167,7 +167,7 @@ class Pref_Users extends Handler_Administrative { $user->created = Db::NOW(); $user->save(); - if ($new_uid = UserHelper::find_user_by_login($login)) { + if (!is_null(UserHelper::find_user_by_login($login))) { print T_sprintf("Added user %s with password %s", $login, $new_password); } else { @@ -178,11 +178,11 @@ class Pref_Users extends Handler_Administrative { } } - function resetPass() { + function resetPass(): void { UserHelper::reset_password(clean($_REQUEST["id"])); } - function index() { + function index(): void { global $access_level_names; |