diff options
| author | 2023-12-18 17:59:16 +0100 | |
|---|---|---|
| committer | 2023-12-18 17:59:16 +0100 | |
| commit | a80a5f48a16e7d232168a7aaa68e9a1804235ce1 (patch) | |
| tree | a515b88592629dea7e83b96e26e2452d3f98a98e | |
| parent | 6bb45a87268157aab961a6a4a728d9a9bbe043b0 (diff) | |
Pass PHPStan level 8 (#5946)
* Pass PHPStan level 8
And prepare for PHPStan level 9 https://phpstan.org/user-guide/rule-levels
* Revert wrong replace in comment
* Fix PHPStan level 8
* Update PHPStan and other dev dependencies
* Remove obsolete comment
* noVariableVariables and towards bleedingEdge
https://github.com/phpstan/phpstan-strict-rules
https://phpstan.org/blog/what-is-bleeding-edge
* More bleedingEdge
* A bit more PHPStan level 9
* More PHPStan level 9
* Prepare for booleansInConditions
Ignore int and null
* Revert wrong line
* More fixes
* Fix keep_max_n_unread
* Stricter attribute functions
* Stricter callHooks and more PHPStan level 9
* More typing
* A tiny more
119 files changed, 1347 insertions, 1173 deletions
diff --git a/app/Controllers/apiController.php b/app/Controllers/apiController.php index 7568f9831..2d9fad535 100644 --- a/app/Controllers/apiController.php +++ b/app/Controllers/apiController.php @@ -13,18 +13,20 @@ class FreshRSS_api_Controller extends FreshRSS_ActionController { */ public static function updatePassword(string $apiPasswordPlain) { $username = Minz_User::name(); - $userConfig = FreshRSS_Context::$user_conf; + if ($username == null) { + return _t('feedback.api.password.failed'); + } $apiPasswordHash = FreshRSS_password_Util::hash($apiPasswordPlain); - $userConfig->apiPasswordHash = $apiPasswordHash; + FreshRSS_Context::userConf()->apiPasswordHash = $apiPasswordHash; $feverKey = FreshRSS_fever_Util::updateKey($username, $apiPasswordPlain); if (!$feverKey) { return _t('feedback.api.password.failed'); } - $userConfig->feverKey = $feverKey; - if ($userConfig->save()) { + FreshRSS_Context::userConf()->feverKey = $feverKey; + if (FreshRSS_Context::userConf()->save()) { return false; } else { return _t('feedback.api.password.failed'); diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 06eca7d9e..85a722761 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -28,23 +28,26 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { $anon = Minz_Request::paramBoolean('anon_access'); $anon_refresh = Minz_Request::paramBoolean('anon_refresh'); - $auth_type = Minz_Request::paramString('auth_type') ?: 'none'; + $auth_type = Minz_Request::paramString('auth_type') ?: 'form'; $unsafe_autologin = Minz_Request::paramBoolean('unsafe_autologin'); $api_enabled = Minz_Request::paramBoolean('api_enabled'); - if ($anon !== FreshRSS_Context::$system_conf->allow_anonymous || - $auth_type !== FreshRSS_Context::$system_conf->auth_type || - $anon_refresh !== FreshRSS_Context::$system_conf->allow_anonymous_refresh || - $unsafe_autologin !== FreshRSS_Context::$system_conf->unsafe_autologin_enabled || - $api_enabled !== FreshRSS_Context::$system_conf->api_enabled) { - - // TODO: test values from form - FreshRSS_Context::$system_conf->auth_type = $auth_type; - FreshRSS_Context::$system_conf->allow_anonymous = $anon; - FreshRSS_Context::$system_conf->allow_anonymous_refresh = $anon_refresh; - FreshRSS_Context::$system_conf->unsafe_autologin_enabled = $unsafe_autologin; - FreshRSS_Context::$system_conf->api_enabled = $api_enabled; - - $ok &= FreshRSS_Context::$system_conf->save(); + if ($anon !== FreshRSS_Context::systemConf()->allow_anonymous || + $auth_type !== FreshRSS_Context::systemConf()->auth_type || + $anon_refresh !== FreshRSS_Context::systemConf()->allow_anonymous_refresh || + $unsafe_autologin !== FreshRSS_Context::systemConf()->unsafe_autologin_enabled || + $api_enabled !== FreshRSS_Context::systemConf()->api_enabled) { + + if (in_array($auth_type, ['form', 'http_auth', 'none'], true)) { + FreshRSS_Context::systemConf()->auth_type = $auth_type; + } else { + FreshRSS_Context::systemConf()->auth_type = 'form'; + } + FreshRSS_Context::systemConf()->allow_anonymous = $anon; + FreshRSS_Context::systemConf()->allow_anonymous_refresh = $anon_refresh; + FreshRSS_Context::systemConf()->unsafe_autologin_enabled = $unsafe_autologin; + FreshRSS_Context::systemConf()->api_enabled = $api_enabled; + + $ok &= FreshRSS_Context::systemConf()->save(); } invalidateHttpCache(); @@ -69,7 +72,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { Minz_Request::forward(['c' => 'index', 'a' => 'index'], true); } - $auth_type = FreshRSS_Context::$system_conf->auth_type; + $auth_type = FreshRSS_Context::systemConf()->auth_type; FreshRSS_Context::initUser(Minz_User::INTERNAL_USER, false); switch ($auth_type) { case 'form': @@ -114,7 +117,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { FreshRSS_View::prependTitle(_t('gen.auth.login') . ' · '); FreshRSS_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $this->view->cookie_days = (int)round($limits['cookie_duration'] / 86400, 1); $isPOST = Minz_Request::isPost() && !Minz_Session::paramBoolean('POST_to_GET'); @@ -128,39 +131,39 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { usleep(random_int(100, 10000)); //Primitive mitigation of timing attacks, in μs FreshRSS_Context::initUser($username); - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { // Initialise the default user to be able to display the error page - FreshRSS_Context::initUser(FreshRSS_Context::$system_conf->default_user); + FreshRSS_Context::initUser(FreshRSS_Context::systemConf()->default_user); Minz_Error::error(403, _t('feedback.auth.login.invalid'), false); return; } - if (!FreshRSS_Context::$user_conf->enabled || FreshRSS_Context::$user_conf->passwordHash == '') { + if (!FreshRSS_Context::userConf()->enabled || FreshRSS_Context::userConf()->passwordHash == '') { usleep(random_int(100, 5000)); //Primitive mitigation of timing attacks, in μs Minz_Error::error(403, _t('feedback.auth.login.invalid'), false); return; } $ok = FreshRSS_FormAuth::checkCredentials( - $username, FreshRSS_Context::$user_conf->passwordHash, $nonce, $challenge + $username, FreshRSS_Context::userConf()->passwordHash, $nonce, $challenge ); if ($ok) { // Set session parameter to give access to the user. Minz_Session::_params([ Minz_User::CURRENT_USER => $username, - 'passwordHash' => FreshRSS_Context::$user_conf->passwordHash, + 'passwordHash' => FreshRSS_Context::userConf()->passwordHash, 'csrf' => false, ]); FreshRSS_Auth::giveAccess(); // Set cookie parameter if needed. if (Minz_Request::paramBoolean('keep_logged_in')) { - FreshRSS_FormAuth::makeCookie($username, FreshRSS_Context::$user_conf->passwordHash); + FreshRSS_FormAuth::makeCookie($username, FreshRSS_Context::userConf()->passwordHash); } else { FreshRSS_FormAuth::deleteCookie(); } - Minz_Translate::init(FreshRSS_Context::$user_conf->language); + Minz_Translate::init(FreshRSS_Context::userConf()->language); // All is good, go back to the original request or the index. $url = Minz_Url::unserialize(Minz_Request::paramString('original_request')); @@ -176,7 +179,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { Minz_Request::setBadNotification(_t('feedback.auth.login.invalid')); Minz_Request::forward(['c' => 'auth', 'a' => 'login'], false); } - } elseif (FreshRSS_Context::$system_conf->unsafe_autologin_enabled) { + } elseif (FreshRSS_Context::systemConf()->unsafe_autologin_enabled) { $username = Minz_Request::paramString('u'); $password = Minz_Request::paramString('p'); Minz_Request::_param('p'); @@ -188,11 +191,11 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { FreshRSS_FormAuth::deleteCookie(); FreshRSS_Context::initUser($username); - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { return; } - $s = FreshRSS_Context::$user_conf->passwordHash; + $s = FreshRSS_Context::userConf()->passwordHash; $ok = password_verify($password, $s); unset($password); if ($ok) { @@ -203,7 +206,7 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { ]); FreshRSS_Auth::giveAccess(); - Minz_Translate::init(FreshRSS_Context::$user_conf->language); + Minz_Translate::init(FreshRSS_Context::userConf()->language); Minz_Request::good(_t('feedback.auth.login.success'), ['c' => 'index', 'a' => 'index']); } else { @@ -242,8 +245,8 @@ class FreshRSS_auth_Controller extends FreshRSS_ActionController { } $this->view->show_tos_checkbox = file_exists(TOS_FILENAME); - $this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation; - $this->view->preferred_language = Minz_Translate::getLanguage(null, Minz_Request::getPreferredLanguages(), FreshRSS_Context::$system_conf->language); + $this->view->show_email_field = FreshRSS_Context::systemConf()->force_email_validation; + $this->view->preferred_language = Minz_Translate::getLanguage(null, Minz_Request::getPreferredLanguages(), FreshRSS_Context::systemConf()->language); FreshRSS_View::prependTitle(_t('gen.auth.registration.title') . ' · '); } diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index daee1666a..780f03f02 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -33,7 +33,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { $url_redirect = ['c' => 'subscription', 'a' => 'add']; - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $this->view->categories = $catDAO->listCategories(false) ?: []; if (count($this->view->categories) >= $limits['max_categories']) { @@ -61,10 +61,10 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { $opml_url = checkUrl(Minz_Request::paramString('opml_url')); if ($opml_url != '') { $cat->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML); - $cat->_attributes('opml_url', $opml_url); + $cat->_attribute('opml_url', $opml_url); } else { $cat->_kind(FreshRSS_Category::KIND_NORMAL); - $cat->_attributes('opml_url', null); + $cat->_attribute('opml_url', null); } if ($catDAO->addCategoryObject($cat)) { @@ -102,7 +102,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { $category->_filtersAction('read', Minz_Request::paramTextToArray('filteractions_read')); if (Minz_Request::paramBoolean('use_default_purge_options')) { - $category->_attributes('archiving', null); + $category->_attribute('archiving', null); } else { if (!Minz_Request::paramBoolean('enable_keep_max')) { $keepMax = false; @@ -117,7 +117,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { } else { $keepPeriod = false; } - $category->_attributes('archiving', [ + $category->_attribute('archiving', [ 'keep_period' => $keepPeriod, 'keep_max' => $keepMax, 'keep_min' => Minz_Request::paramInt('keep_min'), @@ -128,15 +128,15 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { } $position = Minz_Request::paramInt('position') ?: null; - $category->_attributes('position', $position); + $category->_attribute('position', $position); $opml_url = checkUrl(Minz_Request::paramString('opml_url')); if ($opml_url != '') { $category->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML); - $category->_attributes('opml_url', $opml_url); + $category->_attribute('opml_url', $opml_url); } else { $category->_kind(FreshRSS_Category::KIND_NORMAL); - $category->_attributes('opml_url', null); + $category->_attribute('opml_url', null); } $values = [ @@ -190,9 +190,10 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { } // Remove related queries. - FreshRSS_Context::$user_conf->queries = remove_query_by_get( - 'c_' . $id, FreshRSS_Context::$user_conf->queries); - FreshRSS_Context::$user_conf->save(); + /** @var array<array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string}> $queries */ + $queries = remove_query_by_get('c_' . $id, FreshRSS_Context::userConf()->queries); + FreshRSS_Context::userConf()->queries = $queries; + FreshRSS_Context::userConf()->save(); Minz_Request::good(_t('feedback.sub.category.deleted'), $url_redirect); } @@ -230,10 +231,11 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { // Remove related queries foreach ($feeds as $feed) { - FreshRSS_Context::$user_conf->queries = remove_query_by_get( - 'f_' . $feed->id(), FreshRSS_Context::$user_conf->queries); + /** @var array<array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string}> */ + $queries = remove_query_by_get('f_' . $feed->id(), FreshRSS_Context::userConf()->queries); + FreshRSS_Context::userConf()->queries = $queries; } - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->save(); Minz_Request::good(_t('feedback.sub.category.emptied'), $url_redirect); } else { @@ -258,11 +260,13 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { $id = Minz_Request::paramInt('id'); if ($id === 0) { Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect); + return; } $category = $catDAO->searchById($id); if ($category === null) { Minz_Request::bad(_t('feedback.sub.category.not_exist'), $url_redirect); + return; } invalidateHttpCache(); @@ -288,7 +292,7 @@ class FreshRSS_category_Controller extends FreshRSS_ActionController { $successes = 0; $errors = 0; $catDAO = FreshRSS_Factory::createCategoryDao(); - $categories = $catDAO->listCategoriesOrderUpdate(FreshRSS_Context::$user_conf->dynamic_opml_ttl_default ?? 86400); + $categories = $catDAO->listCategoriesOrderUpdate(FreshRSS_Context::userConf()->dynamic_opml_ttl_default ?? 86400); foreach ($categories as $category) { if ($category->refreshDynamicOpml()) { $successes++; diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index d7c087620..c52a5d23c 100644 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -44,32 +44,32 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { */ public function displayAction(): void { if (Minz_Request::isPost()) { - FreshRSS_Context::$user_conf->language = Minz_Request::paramString('language') ?: 'en'; - FreshRSS_Context::$user_conf->timezone = Minz_Request::paramString('timezone'); - FreshRSS_Context::$user_conf->theme = Minz_Request::paramString('theme') ?: FreshRSS_Themes::$defaultTheme; - FreshRSS_Context::$user_conf->darkMode = Minz_Request::paramString('darkMode') ?: 'no'; - FreshRSS_Context::$user_conf->content_width = Minz_Request::paramString('content_width') ?: 'thin'; - FreshRSS_Context::$user_conf->topline_read = Minz_Request::paramBoolean('topline_read'); - FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::paramBoolean('topline_favorite'); - FreshRSS_Context::$user_conf->topline_date = Minz_Request::paramBoolean('topline_date'); - FreshRSS_Context::$user_conf->topline_link = Minz_Request::paramBoolean('topline_link'); - FreshRSS_Context::$user_conf->topline_website = Minz_Request::paramString('topline_website'); - FreshRSS_Context::$user_conf->topline_thumbnail = Minz_Request::paramString('topline_thumbnail'); - FreshRSS_Context::$user_conf->topline_summary = Minz_Request::paramBoolean('topline_summary'); - FreshRSS_Context::$user_conf->topline_display_authors = Minz_Request::paramBoolean('topline_display_authors'); - FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::paramBoolean('bottomline_read'); - FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::paramBoolean('bottomline_favorite'); - FreshRSS_Context::$user_conf->bottomline_sharing = Minz_Request::paramBoolean('bottomline_sharing'); - FreshRSS_Context::$user_conf->bottomline_tags = Minz_Request::paramBoolean('bottomline_tags'); - FreshRSS_Context::$user_conf->bottomline_myLabels = Minz_Request::paramBoolean('bottomline_myLabels'); - FreshRSS_Context::$user_conf->bottomline_date = Minz_Request::paramBoolean('bottomline_date'); - FreshRSS_Context::$user_conf->bottomline_link = Minz_Request::paramBoolean('bottomline_link'); - FreshRSS_Context::$user_conf->show_nav_buttons = Minz_Request::paramBoolean('show_nav_buttons'); - FreshRSS_Context::$user_conf->html5_notif_timeout = Minz_Request::paramInt('html5_notif_timeout'); - FreshRSS_Context::$user_conf->save(); - - Minz_Session::_param('language', FreshRSS_Context::$user_conf->language); - Minz_Translate::reset(FreshRSS_Context::$user_conf->language); + FreshRSS_Context::userConf()->language = Minz_Request::paramString('language') ?: 'en'; + FreshRSS_Context::userConf()->timezone = Minz_Request::paramString('timezone'); + FreshRSS_Context::userConf()->theme = Minz_Request::paramString('theme') ?: FreshRSS_Themes::$defaultTheme; + FreshRSS_Context::userConf()->darkMode = Minz_Request::paramString('darkMode') ?: 'no'; + FreshRSS_Context::userConf()->content_width = Minz_Request::paramString('content_width') ?: 'thin'; + FreshRSS_Context::userConf()->topline_read = Minz_Request::paramBoolean('topline_read'); + FreshRSS_Context::userConf()->topline_favorite = Minz_Request::paramBoolean('topline_favorite'); + FreshRSS_Context::userConf()->topline_date = Minz_Request::paramBoolean('topline_date'); + FreshRSS_Context::userConf()->topline_link = Minz_Request::paramBoolean('topline_link'); + FreshRSS_Context::userConf()->topline_website = Minz_Request::paramString('topline_website'); + FreshRSS_Context::userConf()->topline_thumbnail = Minz_Request::paramString('topline_thumbnail'); + FreshRSS_Context::userConf()->topline_summary = Minz_Request::paramBoolean('topline_summary'); + FreshRSS_Context::userConf()->topline_display_authors = Minz_Request::paramBoolean('topline_display_authors'); + FreshRSS_Context::userConf()->bottomline_read = Minz_Request::paramBoolean('bottomline_read'); + FreshRSS_Context::userConf()->bottomline_favorite = Minz_Request::paramBoolean('bottomline_favorite'); + FreshRSS_Context::userConf()->bottomline_sharing = Minz_Request::paramBoolean('bottomline_sharing'); + FreshRSS_Context::userConf()->bottomline_tags = Minz_Request::paramBoolean('bottomline_tags'); + FreshRSS_Context::userConf()->bottomline_myLabels = Minz_Request::paramBoolean('bottomline_myLabels'); + FreshRSS_Context::userConf()->bottomline_date = Minz_Request::paramBoolean('bottomline_date'); + FreshRSS_Context::userConf()->bottomline_link = Minz_Request::paramBoolean('bottomline_link'); + FreshRSS_Context::userConf()->show_nav_buttons = Minz_Request::paramBoolean('show_nav_buttons'); + FreshRSS_Context::userConf()->html5_notif_timeout = Minz_Request::paramInt('html5_notif_timeout'); + FreshRSS_Context::userConf()->save(); + + Minz_Session::_param('language', FreshRSS_Context::userConf()->language); + Minz_Translate::reset(FreshRSS_Context::userConf()->language); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'display' ]); @@ -111,27 +111,31 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { */ public function readingAction(): void { if (Minz_Request::isPost()) { - FreshRSS_Context::$user_conf->posts_per_page = Minz_Request::paramInt('posts_per_page') ?: 10; - FreshRSS_Context::$user_conf->view_mode = Minz_Request::paramString('view_mode', true) ?: 'normal'; - FreshRSS_Context::$user_conf->default_view = Minz_Request::paramString('default_view') ?: 'adaptive'; - FreshRSS_Context::$user_conf->show_fav_unread = Minz_Request::paramBoolean('show_fav_unread'); - FreshRSS_Context::$user_conf->auto_load_more = Minz_Request::paramBoolean('auto_load_more'); - FreshRSS_Context::$user_conf->display_posts = Minz_Request::paramBoolean('display_posts'); - FreshRSS_Context::$user_conf->display_categories = Minz_Request::paramString('display_categories') ?: 'active'; - FreshRSS_Context::$user_conf->show_tags = Minz_Request::paramString('show_tags') ?: '0'; - FreshRSS_Context::$user_conf->show_tags_max = Minz_Request::paramInt('show_tags_max'); - FreshRSS_Context::$user_conf->show_author_date = Minz_Request::paramString('show_author_date') ?: '0'; - FreshRSS_Context::$user_conf->show_feed_name = Minz_Request::paramString('show_feed_name') ?: 't'; - FreshRSS_Context::$user_conf->hide_read_feeds = Minz_Request::paramBoolean('hide_read_feeds'); - FreshRSS_Context::$user_conf->onread_jump_next = Minz_Request::paramBoolean('onread_jump_next'); - FreshRSS_Context::$user_conf->lazyload = Minz_Request::paramBoolean('lazyload'); - FreshRSS_Context::$user_conf->sides_close_article = Minz_Request::paramBoolean('sides_close_article'); - FreshRSS_Context::$user_conf->sticky_post = Minz_Request::paramBoolean('sticky_post'); - FreshRSS_Context::$user_conf->reading_confirm = Minz_Request::paramBoolean('reading_confirm'); - FreshRSS_Context::$user_conf->auto_remove_article = Minz_Request::paramBoolean('auto_remove_article'); - FreshRSS_Context::$user_conf->mark_updated_article_unread = Minz_Request::paramBoolean('mark_updated_article_unread'); - FreshRSS_Context::$user_conf->sort_order = Minz_Request::paramString('sort_order') ?: 'DESC'; - FreshRSS_Context::$user_conf->mark_when = [ + FreshRSS_Context::userConf()->posts_per_page = Minz_Request::paramInt('posts_per_page') ?: 10; + FreshRSS_Context::userConf()->view_mode = Minz_Request::paramString('view_mode', true) ?: 'normal'; + FreshRSS_Context::userConf()->default_view = Minz_Request::paramString('default_view') ?: 'adaptive'; + FreshRSS_Context::userConf()->show_fav_unread = Minz_Request::paramBoolean('show_fav_unread'); + FreshRSS_Context::userConf()->auto_load_more = Minz_Request::paramBoolean('auto_load_more'); + FreshRSS_Context::userConf()->display_posts = Minz_Request::paramBoolean('display_posts'); + FreshRSS_Context::userConf()->display_categories = Minz_Request::paramString('display_categories') ?: 'active'; + FreshRSS_Context::userConf()->show_tags = Minz_Request::paramString('show_tags') ?: '0'; + FreshRSS_Context::userConf()->show_tags_max = Minz_Request::paramInt('show_tags_max'); + FreshRSS_Context::userConf()->show_author_date = Minz_Request::paramString('show_author_date') ?: '0'; + FreshRSS_Context::userConf()->show_feed_name = Minz_Request::paramString('show_feed_name') ?: 't'; + FreshRSS_Context::userConf()->hide_read_feeds = Minz_Request::paramBoolean('hide_read_feeds'); + FreshRSS_Context::userConf()->onread_jump_next = Minz_Request::paramBoolean('onread_jump_next'); + FreshRSS_Context::userConf()->lazyload = Minz_Request::paramBoolean('lazyload'); + FreshRSS_Context::userConf()->sides_close_article = Minz_Request::paramBoolean('sides_close_article'); + FreshRSS_Context::userConf()->sticky_post = Minz_Request::paramBoolean('sticky_post'); + FreshRSS_Context::userConf()->reading_confirm = Minz_Request::paramBoolean('reading_confirm'); + FreshRSS_Context::userConf()->auto_remove_article = Minz_Request::paramBoolean('auto_remove_article'); + FreshRSS_Context::userConf()->mark_updated_article_unread = Minz_Request::paramBoolean('mark_updated_article_unread'); + if (in_array(Minz_Request::paramString('sort_order'), ['ASC', 'DESC'], true)) { + FreshRSS_Context::userConf()->sort_order = Minz_Request::paramString('sort_order'); + } else { + FreshRSS_Context::userConf()->sort_order = 'DESC'; + } + FreshRSS_Context::userConf()->mark_when = [ 'article' => Minz_Request::paramBoolean('mark_open_article'), 'gone' => Minz_Request::paramBoolean('read_upon_gone'), 'max_n_unread' => Minz_Request::paramBoolean('enable_keep_max_n_unread') ? Minz_Request::paramInt('keep_max_n_unread') : false, @@ -142,8 +146,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { 'site' => Minz_Request::paramBoolean('mark_open_site'), 'focus' => Minz_Request::paramBoolean('mark_focus'), ]; - FreshRSS_Context::$user_conf->_filtersAction('read', Minz_Request::paramTextToArray('filteractions_read')); - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->_filtersAction('read', Minz_Request::paramTextToArray('filteractions_read')); + FreshRSS_Context::userConf()->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'reading' ]); @@ -168,8 +172,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { if (Minz_Request::isPost()) { $params = $_POST; - FreshRSS_Context::$user_conf->sharing = $params['share']; - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->sharing = $params['share']; + FreshRSS_Context::userConf()->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'integration' ]); @@ -199,8 +203,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $default = Minz_Configuration::load(FRESHRSS_PATH . '/config-user.default.php'); $shortcuts = $default['shortcuts']; } - FreshRSS_Context::$user_conf->shortcuts = array_map('trim', $shortcuts); - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->shortcuts = array_map('trim', $shortcuts); + FreshRSS_Context::userConf()->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.shortcuts_updated'), ['c' => 'configure', 'a' => 'shortcut']); @@ -237,8 +241,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $keepPeriod = false; } - FreshRSS_Context::$user_conf->ttl_default = Minz_Request::paramInt('ttl_default') ?: FreshRSS_Feed::TTL_DEFAULT; - FreshRSS_Context::$user_conf->archiving = [ + FreshRSS_Context::userConf()->ttl_default = Minz_Request::paramInt('ttl_default') ?: FreshRSS_Feed::TTL_DEFAULT; + FreshRSS_Context::userConf()->archiving = [ 'keep_period' => $keepPeriod, 'keep_max' => $keepMax, 'keep_min' => Minz_Request::paramInt('keep_min_default'), @@ -246,9 +250,9 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { 'keep_labels' => Minz_Request::paramBoolean('keep_labels'), 'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'), ]; - FreshRSS_Context::$user_conf->keep_history_default = null; //Legacy < FreshRSS 1.15 - FreshRSS_Context::$user_conf->old_entries = null; //Legacy < FreshRSS 1.15 - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->keep_history_default = null; //Legacy < FreshRSS 1.15 + FreshRSS_Context::userConf()->old_entries = null; //Legacy < FreshRSS 1.15 + FreshRSS_Context::userConf()->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'archiving' ]); @@ -259,8 +263,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { 'keep_period_count' => '3', 'keep_period_unit' => 'P1M', ]; - if (!empty(FreshRSS_Context::$user_conf->archiving['keep_period'])) { - $keepPeriod = FreshRSS_Context::$user_conf->archiving['keep_period']; + if (!empty(FreshRSS_Context::userConf()->archiving['keep_period'])) { + $keepPeriod = FreshRSS_Context::userConf()->archiving['keep_period']; if (preg_match('/^PT?(?P<count>\d+)[YMWDH]$/', $keepPeriod, $matches)) { $volatile = [ 'enable_keep_period' => true, @@ -269,7 +273,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { ]; } } - FreshRSS_Context::$user_conf->volatile = $volatile; + FreshRSS_Context::userConf()->volatile = $volatile; $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->nb_total = $entryDAO->count(); @@ -316,13 +320,13 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { } $queries[$key] = (new FreshRSS_UserQuery($query, $feed_dao, $category_dao, $tag_dao))->toArray(); } - FreshRSS_Context::$user_conf->queries = $queries; - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->queries = $queries; + FreshRSS_Context::userConf()->save(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'queries' ]); } else { $this->view->queries = []; - foreach (FreshRSS_Context::$user_conf->queries as $key => $query) { + foreach (FreshRSS_Context::userConf()->queries as $key => $query) { $this->view->queries[intval($key)] = new FreshRSS_UserQuery($query, $feed_dao, $category_dao, $tag_dao); } } @@ -354,7 +358,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { } $id = Minz_Request::paramInt('id'); - if (Minz_Request::paramTernary('id') === null || empty(FreshRSS_Context::$user_conf->queries[$id])) { + if (Minz_Request::paramTernary('id') === null || empty(FreshRSS_Context::userConf()->queries[$id])) { Minz_Error::error(404); return; } @@ -363,7 +367,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $feed_dao = FreshRSS_Factory::createFeedDao(); $tag_dao = FreshRSS_Factory::createTagDao(); - $query = new FreshRSS_UserQuery(FreshRSS_Context::$user_conf->queries[$id], $feed_dao, $category_dao, $tag_dao); + $query = new FreshRSS_UserQuery(FreshRSS_Context::userConf()->queries[$id], $feed_dao, $category_dao, $tag_dao); $this->view->query = $query; $this->view->queryId = $id; $this->view->categories = $category_dao->listCategories(false) ?: []; @@ -371,7 +375,6 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $this->view->tags = $tag_dao->listTags() ?: []; if (Minz_Request::isPost()) { - /** @var array<string,string|array<string,string>> $params */ $params = array_filter(Minz_Request::paramArray('query')); $queryParams = []; if (!empty($params['get']) && is_string($params['get'])) { @@ -393,10 +396,10 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $queryParams['name'] = $name; $queryParams['url'] = Minz_Url::display(['params' => $queryParams]); - $queries = FreshRSS_Context::$user_conf->queries; + $queries = FreshRSS_Context::userConf()->queries; $queries[$id] = (new FreshRSS_UserQuery($queryParams, $feed_dao, $category_dao, $tag_dao))->toArray(); - FreshRSS_Context::$user_conf->queries = $queries; - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->queries = $queries; + FreshRSS_Context::userConf()->save(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'queries', 'params' => ['id' => (string)$id] ]); } @@ -409,15 +412,15 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { */ public function deleteQueryAction(): void { $id = Minz_Request::paramInt('id'); - if (Minz_Request::paramTernary('id') === null || empty(FreshRSS_Context::$user_conf->queries[$id])) { + if (Minz_Request::paramTernary('id') === null || empty(FreshRSS_Context::userConf()->queries[$id])) { Minz_Error::error(404); return; } - $queries = FreshRSS_Context::$user_conf->queries; + $queries = FreshRSS_Context::userConf()->queries; unset($queries[$id]); - FreshRSS_Context::$user_conf->queries = $queries; - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->queries = $queries; + FreshRSS_Context::userConf()->save(); Minz_Request::good(_t('feedback.conf.updated'), [ 'c' => 'configure', 'a' => 'queries' ]); } @@ -434,7 +437,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $feed_dao = FreshRSS_Factory::createFeedDao(); $tag_dao = FreshRSS_Factory::createTagDao(); $queries = []; - foreach (FreshRSS_Context::$user_conf->queries as $key => $query) { + foreach (FreshRSS_Context::userConf()->queries as $key => $query) { $queries[$key] = (new FreshRSS_UserQuery($query, $feed_dao, $category_dao, $tag_dao))->toArray(); } $params = $_GET; @@ -443,8 +446,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { $params['name'] = _t('conf.query.number', count($queries) + 1); $queries[] = (new FreshRSS_UserQuery($params, $feed_dao, $category_dao, $tag_dao))->toArray(); - FreshRSS_Context::$user_conf->queries = $queries; - FreshRSS_Context::$user_conf->save(); + FreshRSS_Context::userConf()->queries = $queries; + FreshRSS_Context::userConf()->save(); Minz_Request::good(_t('feedback.conf.query_created', $params['name']), [ 'c' => 'configure', 'a' => 'queries' ]); } @@ -473,17 +476,17 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController { } if (Minz_Request::isPost()) { - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $limits['max_registrations'] = Minz_Request::paramInt('max-registrations') ?: 1; $limits['max_feeds'] = Minz_Request::paramInt('max-feeds') ?: 16384; $limits['max_categories'] = Minz_Request::paramInt('max-categories') ?: 16384; $limits['cookie_duration'] = Minz_Request::paramInt('cookie-duration') ?: FreshRSS_Auth::DEFAULT_COOKIE_DURATION; - FreshRSS_Context::$system_conf->limits = $limits; - FreshRSS_Context::$system_conf->title = Minz_Request::paramString('instance-name') ?: 'FreshRSS'; - FreshRSS_Context::$system_conf->auto_update_url = Minz_Request::paramString('auto-update-url'); - FreshRSS_Context::$system_conf->force_email_validation = Minz_Request::paramBoolean('force-email-validation'); - FreshRSS_Context::$system_conf->base_url = Minz_Request::paramString('base-url'); - FreshRSS_Context::$system_conf->save(); + FreshRSS_Context::systemConf()->limits = $limits; + FreshRSS_Context::systemConf()->title = Minz_Request::paramString('instance-name') ?: 'FreshRSS'; + FreshRSS_Context::systemConf()->auto_update_url = Minz_Request::paramString('auto-update-url'); + FreshRSS_Context::systemConf()->force_email_validation = Minz_Request::paramBoolean('force-email-validation'); + FreshRSS_Context::systemConf()->base_url = Minz_Request::paramString('base-url'); + FreshRSS_Context::systemConf()->save(); invalidateHttpCache(); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 1ce490ea1..c30c6b6fa 100644 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -209,7 +209,7 @@ class FreshRSS_entry_Controller extends FreshRSS_ActionController { $feedDAO->beginTransaction(); foreach ($feeds as $feed) { - $nb_total += $feed->cleanOldEntries(); + $nb_total += ($feed->cleanOldEntries() ?: 0); } $feedDAO->updateCachedValues(); diff --git a/app/Controllers/extensionController.php b/app/Controllers/extensionController.php index 0158b2f76..9cfc6ff68 100644 --- a/app/Controllers/extensionController.php +++ b/app/Controllers/extensionController.php @@ -54,7 +54,7 @@ class FreshRSS_extension_Controller extends FreshRSS_ActionController { // fetch the list as an array /** @var array<string,mixed> $list*/ $list = json_decode($json, true); - if (empty($list)) { + if (empty($list) || !is_array($list)) { Minz_Log::warning('Failed to convert extension file list'); return []; } @@ -136,9 +136,9 @@ class FreshRSS_extension_Controller extends FreshRSS_ActionController { $conf = null; if ($type === 'system') { - $conf = FreshRSS_Context::$system_conf; + $conf = FreshRSS_Context::systemConf(); } elseif ($type === 'user') { - $conf = FreshRSS_Context::$user_conf; + $conf = FreshRSS_Context::userConf(); } $res = $ext->install(); @@ -198,9 +198,9 @@ class FreshRSS_extension_Controller extends FreshRSS_ActionController { $conf = null; if ($type === 'system') { - $conf = FreshRSS_Context::$system_conf; + $conf = FreshRSS_Context::systemConf(); } elseif ($type === 'user') { - $conf = FreshRSS_Context::$user_conf; + $conf = FreshRSS_Context::userConf(); } $res = $ext->uninstall(); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index aec39587e..cc560a224 100644 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -15,11 +15,11 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { // Token is useful in the case that anonymous refresh is forbidden // and CRON task cannot be used with php command so the user can // set a CRON task to refresh his feeds by using token inside url - $token = FreshRSS_Context::$user_conf->token; + $token = FreshRSS_Context::userConf()->token; $token_param = Minz_Request::paramString('token'); $token_is_ok = ($token != '' && $token == $token_param); $action = Minz_Request::actionName(); - $allow_anonymous_refresh = FreshRSS_Context::$system_conf->allow_anonymous_refresh; + $allow_anonymous_refresh = FreshRSS_Context::systemConf()->allow_anonymous_refresh; if ($action !== 'actualize' || !($allow_anonymous_refresh || $token_is_ok)) { Minz_Error::error(403); @@ -45,7 +45,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $url = trim($url); - /** @var string|null $url */ + /** @var string|null $urlHooked */ $urlHooked = Minz_ExtensionManager::callHook('check_url_before_add', $url); if ($urlHooked === null) { throw new FreshRSS_FeedNotAdded_Exception($url); @@ -72,7 +72,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $feed->_name($title); } $feed->_kind($kind); - $feed->_attributes('', $attributes); + $feed->_attributes($attributes); $feed->_httpAuth($http_auth); $feed->_categoryId($cat_id); switch ($kind) { @@ -151,7 +151,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { 'params' => [], ]; - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $this->view->feeds = $feedDAO->listFeeds(); if (count($this->view->feeds) >= $limits['max_feeds']) { Minz_Request::bad(_t('feedback.sub.feed.over_max', $limits['max_feeds']), $url_redirect); @@ -377,7 +377,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { } // WebSub (PubSubHubbub) support - $pubsubhubbubEnabledGeneral = FreshRSS_Context::$system_conf->pubsubhubbub_enabled; + $pubsubhubbubEnabledGeneral = FreshRSS_Context::systemConf()->pubsubhubbub_enabled; $pshbMinAge = time() - (3600 * 24); //TODO: Make a configuration. $updated_feeds = 0; @@ -406,14 +406,14 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $mtime = $feed->cacheModifiedTime() ?: 0; $ttl = $feed->ttl(); if ($ttl === FreshRSS_Feed::TTL_DEFAULT) { - $ttl = FreshRSS_Context::$user_conf->ttl_default; + $ttl = FreshRSS_Context::userConf()->ttl_default; } if ($simplePiePush === null && $feed_id === null && (time() <= $feed->lastUpdate() + $ttl)) { //Too early to refresh from source, but check whether the feed was updated by another user $ε = 10; // negligible offset errors in seconds if ($mtime <= 0 || $feed->lastUpdate() + $ε >= $mtime || - time() + $ε >= $mtime + FreshRSS_Context::$system_conf->limits['cache_duration']) { // is cache still valid? + time() + $ε >= $mtime + FreshRSS_Context::systemConf()->limits['cache_duration']) { // is cache still valid? continue; //Nothing newer from other users } Minz_Log::debug('Feed ' . $feed->url(false) . ' was updated at ' . date('c', $feed->lastUpdate()) . @@ -475,17 +475,20 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $nbMarkedUnread = 0; if (count($newGuids) > 0) { - $titlesAsRead = []; - $readWhenSameTitleInFeed = $feed->attributes('read_when_same_title_in_feed'); - if ($readWhenSameTitleInFeed == false) { - $readWhenSameTitleInFeed = FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']; + if ($feed->attributeBoolean('read_when_same_title_in_feed') === null) { + $readWhenSameTitleInFeed = (int)FreshRSS_Context::userConf()->mark_when['same_title_in_feed']; + } elseif ($feed->attributeBoolean('read_when_same_title_in_feed') === false) { + $readWhenSameTitleInFeed = 0; + } else { + $readWhenSameTitleInFeed = $feed->attributeInt('read_when_same_title_in_feed') ?? 0; } if ($readWhenSameTitleInFeed > 0) { - /** @var array<string,bool> $titlesAsRead*/ - $titlesAsRead = array_flip($feedDAO->listTitles($feed->id(), (int)$readWhenSameTitleInFeed)); + $titlesAsRead = array_flip($feedDAO->listTitles($feed->id(), $readWhenSameTitleInFeed)); + } else { + $titlesAsRead = []; } - $mark_updated_article_unread = $feed->attributes('mark_updated_article_unread') ?? FreshRSS_Context::$user_conf->mark_updated_article_unread; + $mark_updated_article_unread = $feed->attributeBoolean('mark_updated_article_unread') ?? FreshRSS_Context::userConf()->mark_updated_article_unread; // For this feed, check existing GUIDs already in database. $existingHashForGuids = $entryDAO->listHashForFeedGuids($feed->id(), $newGuids) ?: []; @@ -828,10 +831,10 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { // TODO: Delete old favicon // Remove related queries - FreshRSS_Context::$user_conf->queries = remove_query_by_get( - 'f_' . $feed_id, FreshRSS_Context::$user_conf->queries); - FreshRSS_Context::$user_conf->save(); - + /** @var array<array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string}> $queries */ + $queries = remove_query_by_get('f_' . $feed_id, FreshRSS_Context::userConf()->queries); + FreshRSS_Context::userConf()->queries = $queries; + FreshRSS_Context::userConf()->save(); return true; } return false; @@ -939,7 +942,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { //We need another DB connection in parallel for unbuffered streaming Minz_ModelPdo::$usesSharedPdo = false; - if (FreshRSS_Context::$system_conf->db['type'] === 'mysql') { + if (FreshRSS_Context::systemConf()->db['type'] === 'mysql') { // Second parallel connection for unbuffered streaming: MySQL $entryDAO2 = FreshRSS_Factory::createEntryDao(); } else { diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 6d37cc465..0de75d0ff 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -289,7 +289,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController { */ private function importJson(string $article_file, bool $starred = false): bool { $article_object = json_decode($article_file, true); - if ($article_object == null) { + if (!is_array($article_object)) { if (FreshRSS_Context::$isCli) { fwrite(STDERR, 'FreshRSS error trying to import a non-JSON file' . "\n"); } else { @@ -299,14 +299,14 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController { } $items = $article_object['items'] ?? $article_object; - $mark_as_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; + $mark_as_read = FreshRSS_Context::userConf()->mark_when['reception'] ? 1 : 0; $error = false; $article_to_feed = []; $nb_feeds = count($this->feedDAO->listFeeds()); $newFeedGuids = []; - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; // First, we check feeds of articles are in DB (and add them if needed). foreach ($items as &$item) { @@ -601,7 +601,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController { return; } - $username = Minz_User::name(); + $username = Minz_User::name() ?? '_'; $export_service = new FreshRSS_Export_Service($username); $export_opml = Minz_Request::paramBoolean('export_opml'); diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index a1c25e649..a83307714 100644 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -10,7 +10,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { * This action only redirect on the default view mode (normal or global) */ public function indexAction(): void { - $preferred_output = FreshRSS_Context::$user_conf->view_mode; + $preferred_output = FreshRSS_Context::userConf()->view_mode; Minz_Request::forward([ 'c' => 'index', 'a' => $preferred_output, @@ -21,7 +21,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { * This action displays the normal view of FreshRSS. */ public function normalAction(): void { - $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; + $allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous; if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(['c' => 'auth', 'a' => 'login']); return; @@ -107,7 +107,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { * This action displays the global view of FreshRSS. */ public function globalAction(): void { - $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; + $allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous; if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(['c' => 'auth', 'a' => 'login']); return; @@ -143,8 +143,8 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { * This action displays the RSS feed of FreshRSS. */ public function rssAction(): void { - $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; - $token = FreshRSS_Context::$user_conf->token; + $allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous; + $token = FreshRSS_Context::userConf()->token; $token_param = Minz_Request::paramString('token'); $token_is_ok = ($token != '' && $token === $token_param); @@ -176,8 +176,8 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { } public function opmlAction(): void { - $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; - $token = FreshRSS_Context::$user_conf->token; + $allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous; + $token = FreshRSS_Context::userConf()->token; $token_param = Minz_Request::paramString('token'); $token_is_ok = ($token != '' && $token === $token_param); @@ -259,7 +259,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController { $date_min = 0; if (FreshRSS_Context::$sinceHours) { $date_min = time() - (FreshRSS_Context::$sinceHours * 3600); - $limit = FreshRSS_Context::$user_conf->max_posts_per_rss; + $limit = FreshRSS_Context::userConf()->max_posts_per_rss; } foreach ($entryDAO->listWhere( diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index 6dd36dd72..a9c4993df 100644 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -21,10 +21,10 @@ class FreshRSS_javascript_Controller extends FreshRSS_ActionController { Minz_Session::_param('actualize_feeds', false); $catDAO = FreshRSS_Factory::createCategoryDao(); - $this->view->categories = $catDAO->listCategoriesOrderUpdate(FreshRSS_Context::$user_conf->dynamic_opml_ttl_default); + $this->view->categories = $catDAO->listCategoriesOrderUpdate(FreshRSS_Context::userConf()->dynamic_opml_ttl_default); $feedDAO = FreshRSS_Factory::createFeedDao(); - $this->view->feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); + $this->view->feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::userConf()->ttl_default); } public function nbUnreadsPerFeedAction(): void { @@ -48,10 +48,11 @@ class FreshRSS_javascript_Controller extends FreshRSS_ActionController { header('Pragma: no-cache'); $user = $_GET['user'] ?? ''; - if (FreshRSS_Context::initUser($user)) { + FreshRSS_Context::initUser($user); + if (!FreshRSS_Context::hasUserConf()) { try { - $salt = FreshRSS_Context::$system_conf->salt; - $s = FreshRSS_Context::$user_conf->passwordHash; + $salt = FreshRSS_Context::systemConf()->salt; + $s = FreshRSS_Context::userConf()->passwordHash; if (strlen($s) >= 60) { //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". $this->view->salt1 = substr($s, 0, 29); diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 21c5abb30..07e0fdcf8 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -118,13 +118,13 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { $feed->_ttl(Minz_Request::paramInt('ttl') ?: FreshRSS_Feed::TTL_DEFAULT); $feed->_mute(Minz_Request::paramBoolean('mute')); - $feed->_attributes('read_upon_gone', Minz_Request::paramTernary('read_upon_gone')); - $feed->_attributes('mark_updated_article_unread', Minz_Request::paramTernary('mark_updated_article_unread')); - $feed->_attributes('read_upon_reception', Minz_Request::paramTernary('read_upon_reception')); - $feed->_attributes('clear_cache', Minz_Request::paramTernary('clear_cache')); + $feed->_attribute('read_upon_gone', Minz_Request::paramTernary('read_upon_gone')); + $feed->_attribute('mark_updated_article_unread', Minz_Request::paramTernary('mark_updated_article_unread')); + $feed->_attribute('read_upon_reception', Minz_Request::paramTernary('read_upon_reception')); + $feed->_attribute('clear_cache', Minz_Request::paramTernary('clear_cache')); $keep_max_n_unread = Minz_Request::paramTernary('keep_max_n_unread') === true ? Minz_Request::paramInt('keep_max_n_unread') : null; - $feed->_attributes('keep_max_n_unread', $keep_max_n_unread >= 0 ? $keep_max_n_unread : null); + $feed->_attribute('keep_max_n_unread', $keep_max_n_unread >= 0 ? $keep_max_n_unread : null); $read_when_same_title_in_feed = Minz_Request::paramString('read_when_same_title_in_feed'); if ($read_when_same_title_in_feed === '') { @@ -135,7 +135,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { $read_when_same_title_in_feed = false; } } - $feed->_attributes('read_when_same_title_in_feed', $read_when_same_title_in_feed); + $feed->_attribute('read_when_same_title_in_feed', $read_when_same_title_in_feed); $cookie = Minz_Request::paramString('curl_params_cookie'); $cookie_file = Minz_Request::paramBoolean('curl_params_cookiefile'); @@ -163,16 +163,16 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { if ($useragent !== '') { $opts[CURLOPT_USERAGENT] = $useragent; } - $feed->_attributes('curl_params', empty($opts) ? null : $opts); + $feed->_attribute('curl_params', empty($opts) ? null : $opts); - $feed->_attributes('content_action', Minz_Request::paramString('content_action', true) ?: 'replace'); + $feed->_attribute('content_action', Minz_Request::paramString('content_action', true) ?: 'replace'); - $feed->_attributes('ssl_verify', Minz_Request::paramTernary('ssl_verify')); + $feed->_attribute('ssl_verify', Minz_Request::paramTernary('ssl_verify')); $timeout = Minz_Request::paramInt('timeout'); - $feed->_attributes('timeout', $timeout > 0 ? $timeout : null); + $feed->_attribute('timeout', $timeout > 0 ? $timeout : null); if (Minz_Request::paramBoolean('use_default_purge_options')) { - $feed->_attributes('archiving', null); + $feed->_attribute('archiving', null); } else { if (Minz_Request::paramBoolean('enable_keep_max')) { $keepMax = Minz_Request::paramInt('keep_max') ?: FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT; @@ -187,7 +187,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { } else { $keepPeriod = false; } - $feed->_attributes('archiving', [ + $feed->_attribute('archiving', [ 'keep_period' => $keepPeriod, 'keep_max' => $keepMax, 'keep_min' => Minz_Request::paramInt('keep_min'), @@ -223,10 +223,10 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { if (Minz_Request::paramString('xPathItemUid') != '') $xPathSettings['itemUid'] = Minz_Request::paramString('xPathItemUid', true); if (!empty($xPathSettings)) - $feed->_attributes('xpath', $xPathSettings); + $feed->_attribute('xpath', $xPathSettings); } - $feed->_attributes('path_entries_filter', Minz_Request::paramString('path_entries_filter', true)); + $feed->_attribute('path_entries_filter', Minz_Request::paramString('path_entries_filter', true)); $values = [ 'name' => Minz_Request::paramString('name'), diff --git a/app/Controllers/tagController.php b/app/Controllers/tagController.php index 13909d522..190df32b1 100644 --- a/app/Controllers/tagController.php +++ b/app/Controllers/tagController.php @@ -36,7 +36,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController { $id_tag = Minz_Request::paramInt('id_tag'); $name_tag = Minz_Request::paramString('name_tag'); $id_entry = Minz_Request::paramString('id_entry'); - $checked = Minz_Request::paramTernary('checked'); + $checked = Minz_Request::paramBoolean('checked'); if ($id_entry != '') { $tagDAO = FreshRSS_Factory::createTagDao(); if ($id_tag == 0 && $name_tag !== '' && $checked) { @@ -85,7 +85,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController { } public function getTagsForEntryAction(): void { - if (!FreshRSS_Auth::hasAccess() && !FreshRSS_Context::$system_conf->allow_anonymous) { + if (!FreshRSS_Auth::hasAccess() && !FreshRSS_Context::systemConf()->allow_anonymous) { Minz_Error::error(403); } $this->view->_layout(null); @@ -136,7 +136,7 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController { $tagDAO = FreshRSS_Factory::createTagDao(); $sourceTag = $tagDAO->searchById($sourceId); - $sourceName = $sourceTag === null ? null : $sourceTag->name(); + $sourceName = $sourceTag === null ? '' : $sourceTag->name(); $targetTag = $tagDAO->searchByName($targetName); if ($targetTag === null) { // There is no existing tag with the same target name diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 43860a50c..78f28e493 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -194,7 +194,7 @@ class FreshRSS_update_Controller extends FreshRSS_ActionController { return; } } else { - $auto_update_url = FreshRSS_Context::$system_conf->auto_update_url . '/?v=' . FRESHRSS_VERSION; + $auto_update_url = FreshRSS_Context::systemConf()->auto_update_url . '/?v=' . FRESHRSS_VERSION; Minz_Log::debug('HTTP GET ' . $auto_update_url); $curlResource = curl_init($auto_update_url); @@ -257,7 +257,7 @@ class FreshRSS_update_Controller extends FreshRSS_ActionController { } public function applyAction(): void { - if (FreshRSS_Context::$system_conf->disable_update || !file_exists(UPDATE_FILENAME) || !touch(FRESHRSS_PATH . '/index.html')) { + if (FreshRSS_Context::systemConf()->disable_update || !file_exists(UPDATE_FILENAME) || !touch(FRESHRSS_PATH . '/index.html')) { Minz_Request::forward(['c' => 'update'], true); } @@ -270,7 +270,7 @@ class FreshRSS_update_Controller extends FreshRSS_ActionController { $res = do_post_update(); } - Minz_ExtensionManager::callHook('post_update'); + Minz_ExtensionManager::callHookVoid('post_update'); if ($res === true) { @unlink(UPDATE_FILENAME); diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 6766182c1..b3fccac24 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -29,8 +29,8 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { if ($email !== null && $userConfig->mail_login !== $email) { $userConfig->mail_login = $email; - if (FreshRSS_Context::$system_conf->force_email_validation) { - $salt = FreshRSS_Context::$system_conf->salt; + if (FreshRSS_Context::systemConf()->force_email_validation) { + $salt = FreshRSS_Context::systemConf()->salt; $userConfig->email_validation_token = sha1($salt . uniqid('' . mt_rand(), true)); $mailer = new FreshRSS_User_Mailer(); $mailer->send_email_need_validation($user, $userConfig); @@ -88,7 +88,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { Minz_Error::error(403); } - $email_not_verified = FreshRSS_Context::$user_conf->email_validation_token != ''; + $email_not_verified = FreshRSS_Context::userConf()->email_validation_token != ''; $this->view->disable_aside = false; if ($email_not_verified) { $this->view->_layout('simple'); @@ -99,17 +99,15 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { FreshRSS_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); - if (Minz_Request::isPost()) { - $system_conf = FreshRSS_Context::$system_conf; - $user_config = FreshRSS_Context::$user_conf; - $old_email = $user_config->mail_login; + if (Minz_Request::isPost() && Minz_User::name() != null) { + $old_email = FreshRSS_Context::userConf()->mail_login; $email = Minz_Request::paramString('email'); $passwordPlain = Minz_Request::paramString('newPasswordPlain', true); Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP $_POST['newPasswordPlain'] = ''; - if ($system_conf->force_email_validation && empty($email)) { + if (FreshRSS_Context::systemConf()->force_email_validation && empty($email)) { Minz_Request::bad( _t('user.email.feedback.required'), ['c' => 'user', 'a' => 'profile'] @@ -132,10 +130,10 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { ] ); - Minz_Session::_param('passwordHash', FreshRSS_Context::$user_conf->passwordHash); + Minz_Session::_param('passwordHash', FreshRSS_Context::userConf()->passwordHash); if ($ok) { - if ($system_conf->force_email_validation && $email !== $old_email) { + if (FreshRSS_Context::systemConf()->force_email_validation && $email !== $old_email) { Minz_Request::good(_t('feedback.profile.updated'), ['c' => 'user', 'a' => 'validateEmail']); } elseif ($passwordPlain == '') { Minz_Request::good(_t('feedback.profile.updated'), ['c' => 'user', 'a' => 'profile']); @@ -202,7 +200,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { } } - $this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation; + $this->view->show_email_field = FreshRSS_Context::systemConf()->force_email_validation; $this->view->current_user = Minz_Request::paramString('u'); foreach (listUsers() as $user) { @@ -286,8 +284,6 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { } if (Minz_Request::isPost()) { - $system_conf = FreshRSS_Context::$system_conf; - $new_user_name = Minz_Request::paramString('new_user_name'); $email = Minz_Request::paramString('new_user_email'); $passwordPlain = Minz_Request::paramString('new_user_passwordPlain', true); @@ -320,7 +316,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { $tos_enabled = file_exists(TOS_FILENAME); $accept_tos = Minz_Request::paramBoolean('accept_tos'); - if ($system_conf->force_email_validation && empty($email)) { + if (FreshRSS_Context::systemConf()->force_email_validation && empty($email)) { Minz_Request::bad( _t('user.email.feedback.required'), $badRedirectUrl @@ -342,7 +338,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { } $ok = self::createUser($new_user_name, $email, $passwordPlain, [ - 'language' => Minz_Request::paramString('new_user_language') ?: FreshRSS_Context::$user_conf->language, + 'language' => Minz_Request::paramString('new_user_language') ?: FreshRSS_Context::userConf()->language, 'timezone' => Minz_Request::paramString('new_user_timezone'), 'is_admin' => Minz_Request::paramBoolean('new_user_is_admin'), 'enabled' => true, @@ -357,12 +353,16 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { // get started immediately. if ($ok && !FreshRSS_Auth::hasAccess('admin')) { $user_conf = get_user_configuration($new_user_name); - Minz_Session::_params([ - Minz_User::CURRENT_USER => $new_user_name, - 'passwordHash' => $user_conf->passwordHash, - 'csrf' => false, - ]); - FreshRSS_Auth::giveAccess(); + if ($user_conf !== null) { + Minz_Session::_params([ + Minz_User::CURRENT_USER => $new_user_name, + 'passwordHash' => $user_conf->passwordHash, + 'csrf' => false, + ]); + FreshRSS_Auth::giveAccess(); + } else { + $ok = false; + } } if ($ok) { @@ -379,7 +379,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { public static function deleteUser(string $username): bool { $ok = self::checkUsername($username); if ($ok) { - $default_user = FreshRSS_Context::$system_conf->default_user; + $default_user = FreshRSS_Context::systemConf()->default_user; $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user } $user_data = join_path(DATA_PATH, 'users', $username); @@ -415,7 +415,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { * It returns 403 if user isn’t logged in and `username` param isn’t passed. */ public function validateEmailAction(): void { - if (!FreshRSS_Context::$system_conf->force_email_validation) { + if (!FreshRSS_Context::systemConf()->force_email_validation) { Minz_Error::error(404); } @@ -428,7 +428,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { if ($username !== '') { $user_config = get_user_configuration($username); } elseif (FreshRSS_Auth::hasAccess()) { - $user_config = FreshRSS_Context::$user_conf; + $user_config = FreshRSS_Context::userConf(); } else { Minz_Error::error(403); return; @@ -489,9 +489,8 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { } $username = Minz_User::name(); - $user_config = FreshRSS_Context::$user_conf; - if ($user_config->email_validation_token === '') { + if (FreshRSS_Context::userConf()->email_validation_token === '') { Minz_Request::forward([ 'c' => 'index', 'a' => 'index', @@ -499,7 +498,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { } $mailer = new FreshRSS_User_Mailer(); - $ok = $mailer->send_email_need_validation($username, $user_config); + $ok = $username != null && $mailer->send_email_need_validation($username, FreshRSS_Context::userConf()); $redirect_url = ['c' => 'user', 'a' => 'validateEmail']; if ($ok) { @@ -541,7 +540,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { $challenge = Minz_Request::paramString('challenge'); $ok &= FreshRSS_FormAuth::checkCredentials( - $username, FreshRSS_Context::$user_conf->passwordHash, + $username, FreshRSS_Context::userConf()->passwordHash, $nonce, $challenge ); } @@ -596,6 +595,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { if (null === $userConfig = get_user_configuration($username)) { Minz_Error::error(500); + return; } $userConfig->_param($field, $value); @@ -639,6 +639,9 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { $databaseDAO = FreshRSS_Factory::createDatabaseDAO($username); $userConfiguration = get_user_configuration($username); + if ($userConfiguration === null) { + throw new Exception('Error loading user configuration!'); + } return [ 'feed_count' => $feedDAO->count(), @@ -649,7 +652,7 @@ class FreshRSS_user_Controller extends FreshRSS_ActionController { 'enabled' => $userConfiguration->enabled, 'is_admin' => $userConfiguration->is_admin, 'last_user_activity' => date('c', FreshRSS_UserDAO::mtime($username)) ?: '', - 'is_default' => FreshRSS_Context::$system_conf->default_user === $username, + 'is_default' => FreshRSS_Context::systemConf()->default_user === $username, ]; } } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index c4eaaec1b..264d8ff1a 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -27,13 +27,13 @@ class FreshRSS extends Minz_FrontController { Minz_ActionController::$defaultViewType = FreshRSS_View::class; FreshRSS_Context::initSystem(); - if (FreshRSS_Context::$system_conf == null) { + if (!FreshRSS_Context::hasSystemConf()) { $message = 'Error during context system init!'; Minz_Error::error(500, $message, false); die($message); } - if (FreshRSS_Context::$system_conf->logo_html != '') { + if (FreshRSS_Context::systemConf()->logo_html != '') { // Relax Content Security Policy to allow external images if a custom logo HTML is used Minz_ActionController::_defaultCsp([ 'default-src' => "'self'", @@ -47,10 +47,10 @@ class FreshRSS extends Minz_FrontController { // Auth has to be initialized before using currentUser session parameter // because it’s this part which create this parameter. self::initAuth(); - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { FreshRSS_Context::initUser(); } - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { $message = 'Error during context user init!'; Minz_Error::error(500, $message, false); die($message); @@ -60,28 +60,28 @@ class FreshRSS extends Minz_FrontController { self::initI18n(); self::loadNotifications(); // Enable extensions for the current (logged) user. - if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous) { - $ext_list = FreshRSS_Context::$user_conf->extensions_enabled; + if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::systemConf()->allow_anonymous) { + $ext_list = FreshRSS_Context::userConf()->extensions_enabled; Minz_ExtensionManager::enableByList($ext_list, 'user'); } - if (FreshRSS_Context::$system_conf->force_email_validation && !FreshRSS_Auth::hasAccess('admin')) { + if (FreshRSS_Context::systemConf()->force_email_validation && !FreshRSS_Auth::hasAccess('admin')) { self::checkEmailValidated(); } - Minz_ExtensionManager::callHook('freshrss_init'); + Minz_ExtensionManager::callHookVoid('freshrss_init'); } private static function initAuth(): void { FreshRSS_Auth::init(); if (Minz_Request::isPost()) { - if (FreshRSS_Context::$system_conf == null || !(FreshRSS_Auth::isCsrfOk() || + if (!FreshRSS_Context::hasSystemConf() || !(FreshRSS_Auth::isCsrfOk() || (Minz_Request::controllerName() === 'auth' && Minz_Request::actionName() === 'login') || (Minz_Request::controllerName() === 'user' && Minz_Request::actionName() === 'create' && !FreshRSS_Auth::hasAccess('admin')) || (Minz_Request::controllerName() === 'feed' && Minz_Request::actionName() === 'actualize' - && FreshRSS_Context::$system_conf->allow_anonymous_refresh) || + && FreshRSS_Context::systemConf()->allow_anonymous_refresh) || (Minz_Request::controllerName() === 'javascript' && Minz_Request::actionName() === 'actualize' - && FreshRSS_Context::$system_conf->allow_anonymous) + && FreshRSS_Context::systemConf()->allow_anonymous) )) { // Token-based protection against XSRF attacks, except for the login or self-create user forms self::initI18n(); @@ -91,14 +91,14 @@ class FreshRSS extends Minz_FrontController { } private static function initI18n(): void { - $userLanguage = isset(FreshRSS_Context::$user_conf) ? FreshRSS_Context::$user_conf->language : null; - $systemLanguage = isset(FreshRSS_Context::$system_conf) ? FreshRSS_Context::$system_conf->language : null; + $userLanguage = FreshRSS_Context::hasUserConf() ? FreshRSS_Context::userConf()->language : null; + $systemLanguage = FreshRSS_Context::hasSystemConf() ? FreshRSS_Context::systemConf()->language : null; $language = Minz_Translate::getLanguage($userLanguage, Minz_Request::getPreferredLanguages(), $systemLanguage); Minz_Session::_param('language', $language); Minz_Translate::init($language); - $timezone = isset(FreshRSS_Context::$user_conf) ? FreshRSS_Context::$user_conf->timezone : ''; + $timezone = FreshRSS_Context::hasUserConf() ? FreshRSS_Context::userConf()->timezone : ''; if ($timezone == '') { $timezone = FreshRSS_Context::defaultTimeZone(); } @@ -111,10 +111,10 @@ class FreshRSS extends Minz_FrontController { } public static function loadStylesAndScripts(): void { - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { return; } - $theme = FreshRSS_Themes::load(FreshRSS_Context::$user_conf->theme); + $theme = FreshRSS_Themes::load(FreshRSS_Context::userConf()->theme); if ($theme) { foreach(array_reverse($theme['files']) as $file) { switch (substr($file, -3)) { @@ -167,7 +167,7 @@ class FreshRSS extends Minz_FrontController { private static function checkEmailValidated(): void { $email_not_verified = FreshRSS_Auth::hasAccess() && - FreshRSS_Context::$user_conf !== null && FreshRSS_Context::$user_conf->email_validation_token !== ''; + FreshRSS_Context::hasUserConf() && FreshRSS_Context::userConf()->email_validation_token !== ''; $action_is_allowed = ( Minz_Request::is('user', 'validateEmail') || Minz_Request::is('user', 'sendValidationEmail') || diff --git a/app/Mailers/UserMailer.php b/app/Mailers/UserMailer.php index a78ab9b32..3e4b67d18 100644 --- a/app/Mailers/UserMailer.php +++ b/app/Mailers/UserMailer.php @@ -17,7 +17,7 @@ class FreshRSS_User_Mailer extends Minz_Mailer { $this->view->_path('user_mailer/email_need_validation.txt.php'); $this->view->username = $username; - $this->view->site_title = FreshRSS_Context::$system_conf->title; + $this->view->site_title = FreshRSS_Context::systemConf()->title; $this->view->validation_url = Minz_Url::display( [ 'c' => 'user', @@ -31,7 +31,7 @@ class FreshRSS_User_Mailer extends Minz_Mailer { true ); - $subject_prefix = '[' . FreshRSS_Context::$system_conf->title . ']'; + $subject_prefix = '[' . FreshRSS_Context::systemConf()->title . ']'; return $this->mail( $user_config->mail_login, $subject_prefix . ' ' ._t('user.mailer.email_need_validation.title') diff --git a/app/Models/AttributesTrait.php b/app/Models/AttributesTrait.php index 39154182b..e94a973d9 100644 --- a/app/Models/AttributesTrait.php +++ b/app/Models/AttributesTrait.php @@ -10,28 +10,54 @@ trait FreshRSS_AttributesTrait { */ private array $attributes = []; + /** @return array<string,mixed> */ + public function attributes(): array { + return $this->attributes; + } + /** - * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>) - * @return array<string,mixed>|mixed|null + * @param non-empty-string $key + * @return array<int|string,mixed>|null */ - public function attributes(string $key = '') { - if ($key === '') { - return $this->attributes; - } else { - return $this->attributes[$key] ?? null; + public function attributeArray(string $key): ?array { + $a = $this->attributes[$key] ?? null; + return is_array($a) ? $a : null; + } + + /** @param non-empty-string $key */ + public function attributeBoolean(string $key): ?bool { + $a = $this->attributes[$key] ?? null; + return is_bool($a) ? $a : null; + } + + /** @param non-empty-string $key */ + public function attributeInt(string $key): ?int { + $a = $this->attributes[$key] ?? null; + return is_int($a) ? $a : null; + } + + /** @param non-empty-string $key */ + public function attributeString(string $key): ?string { + $a = $this->attributes[$key] ?? null; + return is_string($a) ? $a : null; + } + + /** @param string|array<string,mixed> $values Values, not HTML-encoded */ + public function _attributes($values): void { + if (is_string($values)) { + $values = json_decode($values, true); + } + if (is_array($values)) { + $this->attributes = $values; } } - /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */ - public function _attributes(string $key, $value = null): void { - if ($key == '') { - if (is_string($value)) { - $value = json_decode($value, true); - } - if (is_array($value)) { - $this->attributes = $value; - } - } elseif ($value === null) { + /** + * @param non-empty-string $key + * @param array<string,mixed>|mixed|null $value Value, not HTML-encoded + */ + public function _attribute(string $key, $value = null): void { + if ($value === null) { unset($this->attributes[$key]); } else { $this->attributes[$key] = $value; diff --git a/app/Models/Auth.php b/app/Models/Auth.php index e5f7fc0b9..c66bb5016 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -24,7 +24,7 @@ class FreshRSS_Auth { self::$login_ok = Minz_Session::paramBoolean('loginOk'); $current_user = Minz_User::name(); if ($current_user === null) { - $current_user = FreshRSS_Context::$system_conf->default_user; + $current_user = FreshRSS_Context::systemConf()->default_user; Minz_Session::_params([ Minz_User::CURRENT_USER => $current_user, 'csrf' => false, @@ -51,7 +51,7 @@ class FreshRSS_Auth { * @return bool true if user can be connected, false otherwise. */ private static function accessControl(): bool { - $auth_type = FreshRSS_Context::$system_conf->auth_type; + $auth_type = FreshRSS_Context::systemConf()->auth_type; switch ($auth_type) { case 'form': $credentials = FreshRSS_FormAuth::getCredentialsFromCookie(); @@ -71,13 +71,13 @@ class FreshRSS_Auth { return false; } $login_ok = FreshRSS_UserDAO::exists($current_user); - if (!$login_ok && FreshRSS_Context::$system_conf->http_auth_auto_register) { + if (!$login_ok && FreshRSS_Context::systemConf()->http_auth_auto_register) { $email = null; - if (FreshRSS_Context::$system_conf->http_auth_auto_register_email_field !== '' && - isset($_SERVER[FreshRSS_Context::$system_conf->http_auth_auto_register_email_field])) { - $email = (string)$_SERVER[FreshRSS_Context::$system_conf->http_auth_auto_register_email_field]; + if (FreshRSS_Context::systemConf()->http_auth_auto_register_email_field !== '' && + isset($_SERVER[FreshRSS_Context::systemConf()->http_auth_auto_register_email_field])) { + $email = (string)$_SERVER[FreshRSS_Context::systemConf()->http_auth_auto_register_email_field]; } - $language = Minz_Translate::getLanguage(null, Minz_Request::getPreferredLanguages(), FreshRSS_Context::$system_conf->language); + $language = Minz_Translate::getLanguage(null, Minz_Request::getPreferredLanguages(), FreshRSS_Context::systemConf()->language); Minz_Translate::init($language); $login_ok = FreshRSS_user_Controller::createUser($current_user, $email, '', [ 'language' => $language, @@ -103,17 +103,17 @@ class FreshRSS_Auth { */ public static function giveAccess(): bool { FreshRSS_Context::initUser(); - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { self::$login_ok = false; return false; } - switch (FreshRSS_Context::$system_conf->auth_type) { + switch (FreshRSS_Context::systemConf()->auth_type) { case 'form': - self::$login_ok = Minz_Session::paramString('passwordHash') === FreshRSS_Context::$user_conf->passwordHash; + self::$login_ok = Minz_Session::paramString('passwordHash') === FreshRSS_Context::userConf()->passwordHash; break; case 'http_auth': - $current_user = Minz_User::name(); + $current_user = Minz_User::name() ?? ''; self::$login_ok = strcasecmp($current_user, httpAuthUser()) === 0; break; case 'none': @@ -138,12 +138,12 @@ class FreshRSS_Auth { * @return bool true if user has corresponding access, false else. */ public static function hasAccess(string $scope = 'general'): bool { - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { return false; } $currentUser = Minz_User::name(); - $isAdmin = FreshRSS_Context::$user_conf->is_admin; - $default_user = FreshRSS_Context::$system_conf->default_user; + $isAdmin = FreshRSS_Context::userConf()->is_admin; + $default_user = FreshRSS_Context::systemConf()->default_user; $ok = self::$login_ok; switch ($scope) { case 'general': @@ -180,11 +180,11 @@ class FreshRSS_Auth { } } if ($username == '') { - $username = FreshRSS_Context::$system_conf->default_user; + $username = FreshRSS_Context::systemConf()->default_user; } Minz_User::change($username); - switch (FreshRSS_Context::$system_conf->auth_type) { + switch (FreshRSS_Context::systemConf()->auth_type) { case 'form': Minz_Session::_param('passwordHash'); FreshRSS_FormAuth::deleteCookie(); @@ -202,20 +202,20 @@ class FreshRSS_Auth { * Return if authentication is enabled on this instance of FRSS. */ public static function accessNeedsLogin(): bool { - return FreshRSS_Context::$system_conf->auth_type !== 'none'; + return FreshRSS_Context::systemConf()->auth_type !== 'none'; } /** * Return if authentication requires a PHP action. */ public static function accessNeedsAction(): bool { - return FreshRSS_Context::$system_conf->auth_type === 'form'; + return FreshRSS_Context::systemConf()->auth_type === 'form'; } public static function csrfToken(): string { $csrf = Minz_Session::paramString('csrf'); if ($csrf == '') { - $salt = FreshRSS_Context::$system_conf->salt; + $salt = FreshRSS_Context::systemConf()->salt; $csrf = sha1($salt . uniqid('' . random_int(0, mt_getrandmax()), true)); Minz_Session::_param('csrf', $csrf); } diff --git a/app/Models/BooleanSearch.php b/app/Models/BooleanSearch.php index 8a750a713..78b7593b2 100644 --- a/app/Models/BooleanSearch.php +++ b/app/Models/BooleanSearch.php @@ -19,14 +19,20 @@ class FreshRSS_BooleanSearch { public function __construct(string $input, int $level = 0, string $operator = 'AND') { $this->operator = $operator; $input = trim($input); - if ($input == '') { + if ($input === '') { return; } $this->raw_input = $input; if ($level === 0) { $input = preg_replace('/:"(.*?)"/', ':"\1"', $input); + if (!is_string($input)) { + return; + } $input = preg_replace('/(?<=[\s!-]|^)"(.*?)"/', '"\1"', $input); + if (!is_string($input)) { + return; + } $input = $this->parseUserQueryNames($input); $input = $this->parseUserQueryIds($input); @@ -53,7 +59,7 @@ class FreshRSS_BooleanSearch { if (!empty($all_matches)) { /** @var array<string,FreshRSS_UserQuery> */ $queries = []; - foreach (FreshRSS_Context::$user_conf->queries as $raw_query) { + foreach (FreshRSS_Context::userConf()->queries as $raw_query) { $query = new FreshRSS_UserQuery($raw_query); $queries[$query->getName()] = $query; } @@ -95,7 +101,7 @@ class FreshRSS_BooleanSearch { /** @var array<string,FreshRSS_UserQuery> */ $queries = []; - foreach (FreshRSS_Context::$user_conf->queries as $raw_query) { + foreach (FreshRSS_Context::userConf()->queries as $raw_query) { $query = new FreshRSS_UserQuery($raw_query, $feed_dao, $category_dao, $tag_dao); $queries[] = $query; } diff --git a/app/Models/Category.php b/app/Models/Category.php index b1e35650a..cc25a1ec0 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -169,8 +169,8 @@ class FreshRSS_Category extends Minz_Model { } public function refreshDynamicOpml(): bool { - $url = $this->attributes('opml_url'); - if ($url == '') { + $url = $this->attributeString('opml_url'); + if ($url == null) { return false; } $ok = true; diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 20347e4f2..417ff7a6c 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -30,8 +30,8 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { } elseif ('attributes' === $name) { //v1.15.0 $ok = $this->pdo->exec('ALTER TABLE `_category` ADD COLUMN attributes TEXT') !== false; - /** @var array<array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, - * 'priority':int,'pathEntries':string,'httpAuth':string,'error':int,'ttl':int,'attributes':string}> $feeds */ + /** @var array<array{'id':int,'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int, + * 'priority':int,'pathEntries':string,'httpAuth':string,'error':int,'keep_history':?int,'ttl':int,'attributes':string}> $feeds */ $feeds = $this->fetchAssoc('SELECT * FROM `_feed`') ?? []; $stm = $this->pdo->prepare('UPDATE `_feed` SET attributes = :attributes WHERE id = :id'); @@ -153,7 +153,7 @@ SQL; } /** - * @param array{'name':string,'kind':int,'attributes'?:string|array<string,mixed>} $valuesTmp + * @param array{'name':string,'kind':int,'attributes'?:array<string,mixed>|mixed|null} $valuesTmp * @return int|false * @throws JsonException */ @@ -230,6 +230,7 @@ SQL; $stm = $this->pdo->query($sql); if ($stm !== false) { while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + /** @var array{'id':int,'name':string,'kind':int,'lastUpdate':int,'error':int,'attributes'?:array<string,mixed>} $row */ yield $row; } } else { @@ -245,7 +246,7 @@ SQL; public function searchById(int $id): ?FreshRSS_Category { $sql = 'SELECT * FROM `_category` WHERE id=:id'; $res = $this->fetchAssoc($sql, ['id' => $id]) ?? []; - /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ + /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate':int,'error':int|bool,'attributes':string}> $res */ $cat = self::daoToCategory($res); return $cat[0] ?? null; } @@ -253,7 +254,7 @@ SQL; public function searchByName(string $name): ?FreshRSS_Category { $sql = 'SELECT * FROM `_category` WHERE name=:name'; $res = $this->fetchAssoc($sql, ['name' => $name]) ?? []; - /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ + /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate':int,'error':int|bool,'attributes':string}> $res */ $cat = self::daoToCategory($res); return $cat[0] ?? null; } @@ -263,8 +264,8 @@ SQL; $categories = $this->listCategories($prePopulateFeeds, $details); uasort($categories, static function (FreshRSS_Category $a, FreshRSS_Category $b) { - $aPosition = $a->attributes('position'); - $bPosition = $b->attributes('position'); + $aPosition = $a->attributeInt('position'); + $bPosition = $b->attributeInt('position'); if ($aPosition === $bPosition) { return ($a->name() < $b->name()) ? -1 : 1; } elseif (null === $aPosition) { @@ -332,9 +333,9 @@ SQL; public function getDefault(): ?FreshRSS_Category { $sql = 'SELECT * FROM `_category` WHERE id=:id'; - $res = $this->fetchAssoc($sql, [':id' => self::DEFAULTCATEGORYID]); + $res = $this->fetchAssoc($sql, [':id' => self::DEFAULTCATEGORYID]) ?? []; /** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */ - $cat = self::daoToCategory($res ?? []); + $cat = self::daoToCategory($res); if (isset($cat[0])) { return $cat[0]; } else { @@ -444,7 +445,7 @@ SQL; $feedDao::daoToFeed($feedsDao, $previousLine['c_id']) ); $cat->_kind($previousLine['c_kind']); - $cat->_attributes('', $previousLine['c_attributes'] ?? '[]'); + $cat->_attributes($previousLine['c_attributes'] ?? '[]'); $list[(int)$previousLine['c_id']] = $cat; $feedsDao = []; //Prepare for next category @@ -464,7 +465,7 @@ SQL; $cat->_kind($previousLine['c_kind']); $cat->_lastUpdate($previousLine['c_last_update'] ?? 0); $cat->_error($previousLine['c_error'] ?? 0); - $cat->_attributes('', $previousLine['c_attributes'] ?? []); + $cat->_attributes($previousLine['c_attributes'] ?? []); $list[(int)$previousLine['c_id']] = $cat; } @@ -487,7 +488,7 @@ SQL; $cat->_kind($dao['kind']); $cat->_lastUpdate($dao['lastUpdate'] ?? 0); $cat->_error($dao['error'] ?? 0); - $cat->_attributes('', $dao['attributes'] ?? ''); + $cat->_attributes($dao['attributes'] ?? ''); $list[] = $cat; } diff --git a/app/Models/Context.php b/app/Models/Context.php index ac5547aa1..3ea5a29eb 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -7,8 +7,6 @@ declare(strict_types=1); */ final class FreshRSS_Context { - public static ?FreshRSS_UserConfiguration $user_conf = null; - public static ?FreshRSS_SystemConfiguration $system_conf = null; /** * @var array<int,FreshRSS_Category> */ @@ -57,21 +55,42 @@ final class FreshRSS_Context { public static bool $isCli = false; /** + * @deprecated Will be made `private`; use `FreshRSS_Context::systemConf()` instead. + * @internal + */ + public static ?FreshRSS_SystemConfiguration $system_conf = null; + /** + * @deprecated Will be made `private`; use `FreshRSS_Context::userConf()` instead. + * @internal + */ + public static ?FreshRSS_UserConfiguration $user_conf = null; + + /** * Initialize the context for the global system. */ - public static function initSystem(bool $reload = false): FreshRSS_SystemConfiguration { - if ($reload || FreshRSS_Context::$system_conf == null) { + public static function initSystem(bool $reload = false): void { + if ($reload || FreshRSS_Context::$system_conf === null) { //TODO: Keep in session what we need instead of always reloading from disk FreshRSS_Context::$system_conf = FreshRSS_SystemConfiguration::init(DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php'); } + } + + public static function &systemConf(): FreshRSS_SystemConfiguration { + if (FreshRSS_Context::$system_conf === null) { + throw new FreshRSS_Context_Exception('System configuration not initialised!'); + } return FreshRSS_Context::$system_conf; } + public static function hasSystemConf(): bool { + return FreshRSS_Context::$system_conf !== null; + } + /** * Initialize the context for the current user. * @throws Minz_ConfigurationParamException */ - public static function initUser(string $username = '', bool $userMustExist = true): ?FreshRSS_UserConfiguration { + public static function initUser(string $username = '', bool $userMustExist = true): void { FreshRSS_Context::$user_conf = null; if (!isset($_SESSION)) { Minz_Session::init('FreshRSS'); @@ -103,14 +122,16 @@ final class FreshRSS_Context { Minz_Session::unlock(); if (FreshRSS_Context::$user_conf == null) { - return null; + return; } FreshRSS_Context::$search = new FreshRSS_BooleanSearch(''); //Legacy - $oldEntries = (int)FreshRSS_Context::$user_conf->param('old_entries', 0); - $keepMin = (int)FreshRSS_Context::$user_conf->param('keep_history_default', -5); + $oldEntries = FreshRSS_Context::$user_conf->param('old_entries', 0); + $oldEntries = is_numeric($oldEntries) ? (int)$oldEntries : 0; + $keepMin = FreshRSS_Context::$user_conf->param('keep_history_default', -5); + $keepMin = is_numeric($keepMin) ? (int)$keepMin : -5; if ($oldEntries > 0 || $keepMin > -5) { //Freshrss < 1.15 $archiving = FreshRSS_Context::$user_conf->archiving; $archiving['keep_max'] = false; @@ -130,10 +151,23 @@ final class FreshRSS_Context { if (!in_array(FreshRSS_Context::$user_conf->display_categories, [ 'active', 'remember', 'all', 'none' ], true)) { FreshRSS_Context::$user_conf->display_categories = FreshRSS_Context::$user_conf->display_categories === true ? 'all' : 'active'; } + } + public static function &userConf(): FreshRSS_UserConfiguration { + if (FreshRSS_Context::$user_conf === null) { + throw new FreshRSS_Context_Exception('User configuration not initialised!'); + } return FreshRSS_Context::$user_conf; } + public static function hasUserConf(): bool { + return FreshRSS_Context::$user_conf !== null; + } + + public static function clearUserConf(): void { + FreshRSS_Context::$user_conf = null; + } + /** * This action updates the Context object by using request parameters. * @@ -162,28 +196,28 @@ final class FreshRSS_Context { self::_get(Minz_Request::paramString('get') ?: 'a'); - self::$state = Minz_Request::paramInt('state') ?: self::$user_conf->default_state; + self::$state = Minz_Request::paramInt('state') ?: FreshRSS_Context::userConf()->default_state; $state_forced_by_user = Minz_Request::paramString('state') !== ''; if (!$state_forced_by_user && !self::isStateEnabled(FreshRSS_Entry::STATE_READ)) { - if (self::$user_conf->default_view === 'all') { + if (FreshRSS_Context::userConf()->default_view === 'all') { self::$state |= FreshRSS_Entry::STATE_ALL; - } elseif (self::$user_conf->default_view === 'adaptive' && self::$get_unread <= 0) { + } elseif (FreshRSS_Context::userConf()->default_view === 'adaptive' && self::$get_unread <= 0) { self::$state |= FreshRSS_Entry::STATE_READ; } - if (self::$user_conf->show_fav_unread && + if (FreshRSS_Context::userConf()->show_fav_unread && (self::isCurrentGet('s') || self::isCurrentGet('T') || self::isTag())) { self::$state |= FreshRSS_Entry::STATE_READ; } } self::$search = new FreshRSS_BooleanSearch(Minz_Request::paramString('search')); - $order = Minz_Request::paramString('order') ?: self::$user_conf->sort_order; + $order = Minz_Request::paramString('order') ?: FreshRSS_Context::userConf()->sort_order; self::$order = in_array($order, ['ASC', 'DESC'], true) ? $order : 'DESC'; - self::$number = Minz_Request::paramInt('nb') ?: self::$user_conf->posts_per_page; - if (self::$number > self::$user_conf->max_posts_per_rss) { + self::$number = Minz_Request::paramInt('nb') ?: FreshRSS_Context::userConf()->posts_per_page; + if (self::$number > FreshRSS_Context::userConf()->max_posts_per_rss) { self::$number = max( - self::$user_conf->max_posts_per_rss, - self::$user_conf->posts_per_page); + FreshRSS_Context::userConf()->max_posts_per_rss, + FreshRSS_Context::userConf()->posts_per_page); } self::$first_id = Minz_Request::paramString('next'); self::$sinceHours = Minz_Request::paramInt('hours'); @@ -335,19 +369,19 @@ final class FreshRSS_Context { case 'a': self::$current_get['all'] = true; self::$name = _t('index.feed.title'); - self::$description = self::$system_conf->meta_description; + self::$description = FreshRSS_Context::systemConf()->meta_description; self::$get_unread = self::$total_unread; break; case 'i': self::$current_get['important'] = true; self::$name = _t('index.menu.important'); - self::$description = self::$system_conf->meta_description; + self::$description = FreshRSS_Context::systemConf()->meta_description; self::$get_unread = self::$total_unread; break; case 's': self::$current_get['starred'] = true; self::$name = _t('index.feed.title_fav'); - self::$description = self::$system_conf->meta_description; + self::$description = FreshRSS_Context::systemConf()->meta_description; self::$get_unread = self::$total_starred['unread']; // Update state if favorite is not yet enabled. @@ -355,7 +389,7 @@ final class FreshRSS_Context { break; case 'f': // We try to find the corresponding feed. When allowing robots, always retrieve the full feed including description - $feed = FreshRSS_Context::$system_conf->allow_robots ? null : FreshRSS_CategoryDAO::findFeed(self::$categories, $id); + $feed = FreshRSS_Context::systemConf()->allow_robots ? null : FreshRSS_CategoryDAO::findFeed(self::$categories, $id); if ($feed === null) { $feedDAO = FreshRSS_Factory::createFeedDao(); $feed = $feedDAO->searchById($id); @@ -427,7 +461,7 @@ final class FreshRSS_Context { self::$categories = $catDAO->listCategories(); } - if (self::$user_conf->onread_jump_next && strlen($get) > 2) { + if (FreshRSS_Context::userConf()->onread_jump_next && strlen($get) > 2) { $another_unread_id = ''; $found_current_get = false; switch ($get[0]) { @@ -491,7 +525,7 @@ final class FreshRSS_Context { * - the "unread" state is enable */ public static function isAutoRemoveAvailable(): bool { - if (!self::$user_conf->auto_remove_article) { + if (!FreshRSS_Context::userConf()->auto_remove_article) { return false; } if (self::isStateEnabled(FreshRSS_Entry::STATE_READ)) { @@ -510,7 +544,7 @@ final class FreshRSS_Context { * are read. */ public static function isStickyPostEnabled(): bool { - if (self::$user_conf->sticky_post) { + if (FreshRSS_Context::userConf()->sticky_post) { return true; } if (self::isAutoRemoveAvailable()) { diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php index 7dbe1db3f..cdc74fa12 100644 --- a/app/Models/DatabaseDAO.php +++ b/app/Models/DatabaseDAO.php @@ -22,7 +22,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { public function create(): string { require_once(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); - $db = FreshRSS_Context::$system_conf->db; + $db = FreshRSS_Context::systemConf()->db; try { $sql = sprintf($GLOBALS['SQL_CREATE_DB'], empty($db['base']) ? '' : $db['base']); @@ -174,7 +174,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { } public function size(bool $all = false): int { - $db = FreshRSS_Context::$system_conf->db; + $db = FreshRSS_Context::systemConf()->db; // MariaDB does not refresh size information automatically $sql = <<<'SQL' diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php index 4a92dd184..fe3d6149d 100644 --- a/app/Models/DatabaseDAOPGSQL.php +++ b/app/Models/DatabaseDAOPGSQL.php @@ -11,7 +11,7 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite { public const UNDEFINED_TABLE = '42P01'; public function tablesAreCorrect(): bool { - $db = FreshRSS_Context::$system_conf->db; + $db = FreshRSS_Context::systemConf()->db; $sql = 'SELECT * FROM pg_catalog.pg_tables where tableowner=:tableowner'; $res = $this->fetchAssoc($sql, [':tableowner' => $db['user']]); if ($res == null) { @@ -58,7 +58,7 @@ SQL; public function size(bool $all = false): int { if ($all) { - $db = FreshRSS_Context::$system_conf->db; + $db = FreshRSS_Context::systemConf()->db; $res = $this->fetchColumn('SELECT pg_database_size(:base)', 0, [':base' => $db['base']]); } else { $sql = <<<SQL diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php index 787380637..e72cc74e8 100644 --- a/app/Models/DatabaseDAOSQLite.php +++ b/app/Models/DatabaseDAOSQLite.php @@ -65,12 +65,12 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { $sum = 0; if ($all) { foreach (glob(DATA_PATH . '/users/*/db.sqlite') ?: [] as $filename) { - $sum += @filesize($filename); + $sum += (@filesize($filename) ?: 0); } } else { - $sum = @filesize(DATA_PATH . '/users/' . $this->current_user . '/db.sqlite'); + $sum = (@filesize(DATA_PATH . '/users/' . $this->current_user . '/db.sqlite') ?: 0); } - return intval($sum); + return $sum; } public function optimize(): bool { diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 186b1f166..62ba91db3 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -90,7 +90,7 @@ class FreshRSS_Entry extends Minz_Model { $entry->_lastSeen($dao['lastSeen']); } if (!empty($dao['attributes'])) { - $entry->_attributes('', $dao['attributes']); + $entry->_attributes($dao['attributes']); } if (!empty($dao['hash'])) { $entry->_hash($dao['hash']); @@ -145,7 +145,7 @@ class FreshRSS_Entry extends Minz_Model { * Provides the original content without additional content potentially added by loadCompleteContent(). */ public function originalContent(): string { - return preg_replace('#<!-- FULLCONTENT start //-->.*<!-- FULLCONTENT end //-->#s', '', $this->content); + return preg_replace('#<!-- FULLCONTENT start //-->.*<!-- FULLCONTENT end //-->#s', '', $this->content) ?? ''; } /** @@ -160,7 +160,7 @@ class FreshRSS_Entry extends Minz_Model { $content = $this->content; - $thumbnailAttribute = $this->attributes('thumbnail'); + $thumbnailAttribute = $this->attributeArray('thumbnail') ?? []; if (!empty($thumbnailAttribute['url'])) { $elink = $thumbnailAttribute['url']; if ($allowDuplicateEnclosures || !self::containsLink($content, $elink)) { @@ -174,12 +174,15 @@ HTML; } } - $attributeEnclosures = $this->attributes('enclosures'); + $attributeEnclosures = $this->attributeArray('enclosures'); if (empty($attributeEnclosures)) { return $content; } foreach ($attributeEnclosures as $enclosure) { + if (!is_array($enclosure)) { + continue; + } $elink = $enclosure['url'] ?? ''; if ($elink == '') { continue; @@ -192,7 +195,10 @@ HTML; $length = $enclosure['length'] ?? 0; $medium = $enclosure['medium'] ?? ''; $mime = $enclosure['type'] ?? ''; - $thumbnails = $enclosure['thumbnails'] ?? []; + $thumbnails = $enclosure['thumbnails'] ?? null; + if (!is_array($thumbnails)) { + $thumbnails = []; + } $etitle = $enclosure['title'] ?? ''; $content .= "\n"; @@ -235,7 +241,7 @@ HTML; /** @return Traversable<array{'url':string,'type'?:string,'medium'?:string,'length'?:int,'title'?:string,'description'?:string,'credit'?:string,'height'?:int,'width'?:int,'thumbnails'?:array<string>}> */ public function enclosures(bool $searchBodyImages = false): Traversable { - $attributeEnclosures = $this->attributes('enclosures'); + $attributeEnclosures = $this->attributeArray('enclosures'); if (is_iterable($attributeEnclosures)) { // FreshRSS 1.20.1+: The enclosures are saved as attributes yield from $attributeEnclosures; @@ -249,7 +255,7 @@ HTML; $dom->loadHTML('<?xml version="1.0" encoding="UTF-8" ?>' . $this->content, LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING); $xpath = new DOMXPath($dom); } - if ($searchEnclosures) { + if ($searchEnclosures && $xpath !== null) { // Legacy code for database entries < FreshRSS 1.20.1 $enclosures = $xpath->query('//div[@class="enclosure"]/p[@class="enclosure-content"]/*[@src]'); if (!empty($enclosures)) { @@ -272,7 +278,7 @@ HTML; } } } - if ($searchBodyImages) { + if ($searchBodyImages && $xpath !== null) { $images = $xpath->query('//img'); if (!empty($images)) { /** @var DOMElement $img */ @@ -300,7 +306,7 @@ HTML; * @return array{'url':string,'type'?:string,'medium'?:string,'length'?:int,'title'?:string,'description'?:string,'credit'?:string,'height'?:int,'width'?:int,'thumbnails'?:array<string>}|null */ public function thumbnail(bool $searchEnclosures = true): ?array { - $thumbnail = $this->attributes('thumbnail'); + $thumbnail = $this->attributeArray('thumbnail') ?? []; // First, use the provided thumbnail, if any if (!empty($thumbnail['url'])) { return $thumbnail; @@ -558,7 +564,7 @@ HTML; $ok &= in_array($this->feedId, $filter->getFeedIds(), true); } if ($ok && $filter->getNotFeedIds()) { - $ok &= !in_array($this->feedId, $filter->getFeedIds(), true); + $ok &= !in_array($this->feedId, $filter->getNotFeedIds(), true); } if ($ok && $filter->getAuthor()) { foreach ($filter->getAuthor() as $author) { @@ -630,14 +636,15 @@ HTML; return (bool)$ok; } - /** @param array<string,bool> $titlesAsRead */ + /** @param array<string,bool|int> $titlesAsRead */ public function applyFilterActions(array $titlesAsRead = []): void { - if ($this->feed === null) { + $feed = $this->feed; + if ($feed === null) { return; } if (!$this->isRead()) { - if ($this->feed->attributes('read_upon_reception') || - ($this->feed->attributes('read_upon_reception') === null && FreshRSS_Context::$user_conf->mark_when['reception'])) { + if ($feed->attributeBoolean('read_upon_reception') || + ($feed->attributeBoolean('read_upon_reception') === null && FreshRSS_Context::userConf()->mark_when['reception'])) { $this->_isRead(true); Minz_ExtensionManager::callHook('entry_auto_read', $this, 'upon_reception'); } @@ -647,11 +654,11 @@ HTML; Minz_ExtensionManager::callHook('entry_auto_read', $this, 'same_title_in_feed'); } } - FreshRSS_Context::$user_conf->applyFilterActions($this); - if ($this->feed->category() !== null) { - $this->feed->category()->applyFilterActions($this); + FreshRSS_Context::userConf()->applyFilterActions($this); + if ($feed->category() !== null) { + $feed->category()->applyFilterActions($this); } - $this->feed->applyFilterActions($this); + $feed->applyFilterActions($this); } public function isDay(int $day, int $today): bool { @@ -684,10 +691,9 @@ HTML; if ($maxRedirs > 0) { //Follow any HTML redirection - $metas = $xpath->query('//meta[@content]'); - /** @var array<DOMElement> $metas */ + $metas = $xpath->query('//meta[@content]') ?: []; foreach ($metas as $meta) { - if (strtolower(trim($meta->getAttribute('http-equiv'))) === 'refresh') { + if ($meta instanceof DOMElement && strtolower(trim($meta->getAttribute('http-equiv'))) === 'refresh') { $refresh = preg_replace('/^[0-9.; ]*\s*(url\s*=)?\s*/i', '', trim($meta->getAttribute('content'))); $refresh = SimplePie_Misc::absolutize_url($refresh, $url); if ($refresh != false && $refresh !== $url) { @@ -712,6 +718,9 @@ HTML; if (!empty($attributes['path_entries_filter'])) { $filterednodes = $xpath->query((new Gt\CssXPath\Translator($attributes['path_entries_filter']))->asXPath(), $node) ?: []; foreach ($filterednodes as $filterednode) { + if ($filterednode->parentNode === null) { + continue; + } $filterednode->parentNode->removeChild($filterednode); } } @@ -747,7 +756,7 @@ HTML; if ('' !== $fullContent) { $fullContent = "<!-- FULLCONTENT start //-->{$fullContent}<!-- FULLCONTENT end //-->"; $originalContent = $this->originalContent(); - switch ($feed->attributes('content_action')) { + switch ($feed->attributeString('content_action')) { case 'prepend': $this->content = $fullContent . $originalContent; break; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 23ac3c918..232db8521 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -315,7 +315,7 @@ SQL; $affected = 0; $idsChunks = array_chunk($ids, FreshRSS_DatabaseDAO::MAX_VARIABLE_NUMBER); foreach ($idsChunks as $idsChunk) { - $affected += $this->markFavorite($idsChunk, $is_favorite); + $affected += ($this->markFavorite($idsChunk, $is_favorite) ?: 0); } return $affected; } @@ -387,7 +387,7 @@ SQL; if (count($ids) < 6) { //Speed heuristics $affected = 0; foreach ($ids as $id) { - $affected += $this->markRead($id, $is_read); + $affected += ($this->markRead($id, $is_read) ?: 0); } return $affected; } elseif (count($ids) > FreshRSS_DatabaseDAO::MAX_VARIABLE_NUMBER) { @@ -395,7 +395,7 @@ SQL; $affected = 0; $idsChunks = array_chunk($ids, FreshRSS_DatabaseDAO::MAX_VARIABLE_NUMBER); foreach ($idsChunks as $idsChunk) { - $affected += $this->markRead($idsChunk, $is_read); + $affected += ($this->markRead($idsChunk, $is_read) ?: 0); } return $affected; } @@ -630,7 +630,7 @@ SQL; /** * Remember to call updateCachedValue($id_feed) or updateCachedValues() just after. - * @param array<string,int|bool|string> $options + * @param array<string,bool|int|string> $options * @return int|false */ public function cleanOldEntries(int $id_feed, array $options = []) { @@ -704,6 +704,8 @@ SQL; $stm = $this->pdo->query($sql); if ($stm != false) { while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + /** @var array{'id':string,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int,'lastSeen':int, + * 'hash':string,'is_read':?bool,'is_favorite':?bool,'id_feed':int,'tags':string,'attributes':array<string,mixed>} $row */ yield $row; } } else { @@ -777,7 +779,7 @@ SQL; } // Searches are combined by OR and are not recursive $sub_search = ''; - if ($filter->getEntryIds()) { + if ($filter->getEntryIds() !== null) { $sub_search .= 'AND ' . $alias . 'id IN ('; foreach ($filter->getEntryIds() as $entry_id) { $sub_search .= '?,'; @@ -786,7 +788,7 @@ SQL; $sub_search = rtrim($sub_search, ','); $sub_search .= ') '; } - if ($filter->getNotEntryIds()) { + if ($filter->getNotEntryIds() !== null) { $sub_search .= 'AND ' . $alias . 'id NOT IN ('; foreach ($filter->getNotEntryIds() as $entry_id) { $sub_search .= '?,'; @@ -796,56 +798,56 @@ SQL; $sub_search .= ') '; } - if ($filter->getMinDate()) { + if ($filter->getMinDate() !== null) { $sub_search .= 'AND ' . $alias . 'id >= ? '; $values[] = "{$filter->getMinDate()}000000"; } - if ($filter->getMaxDate()) { + if ($filter->getMaxDate() !== null) { $sub_search .= 'AND ' . $alias . 'id <= ? '; $values[] = "{$filter->getMaxDate()}000000"; } - if ($filter->getMinPubdate()) { + if ($filter->getMinPubdate() !== null) { $sub_search .= 'AND ' . $alias . 'date >= ? '; $values[] = $filter->getMinPubdate(); } - if ($filter->getMaxPubdate()) { + if ($filter->getMaxPubdate() !== null) { $sub_search .= 'AND ' . $alias . 'date <= ? '; $values[] = $filter->getMaxPubdate(); } //Negation of date intervals must be combined by OR - if ($filter->getNotMinDate() || $filter->getNotMaxDate()) { + if ($filter->getNotMinDate() !== null || $filter->getNotMaxDate() !== null) { $sub_search .= 'AND ('; - if ($filter->getNotMinDate()) { + if ($filter->getNotMinDate() !== null) { $sub_search .= $alias . 'id < ?'; $values[] = "{$filter->getNotMinDate()}000000"; if ($filter->getNotMaxDate()) { $sub_search .= ' OR '; } } - if ($filter->getNotMaxDate()) { + if ($filter->getNotMaxDate() !== null) { $sub_search .= $alias . 'id > ?'; $values[] = "{$filter->getNotMaxDate()}000000"; } $sub_search .= ') '; } - if ($filter->getNotMinPubdate() || $filter->getNotMaxPubdate()) { + if ($filter->getNotMinPubdate() !== null || $filter->getNotMaxPubdate() !== null) { $sub_search .= 'AND ('; - if ($filter->getNotMinPubdate()) { + if ($filter->getNotMinPubdate() !== null) { $sub_search .= $alias . 'date < ?'; $values[] = $filter->getNotMinPubdate(); if ($filter->getNotMaxPubdate()) { $sub_search .= ' OR '; } } - if ($filter->getNotMaxPubdate()) { + if ($filter->getNotMaxPubdate() !== null) { $sub_search .= $alias . 'date > ?'; $values[] = $filter->getNotMaxPubdate(); } $sub_search .= ') '; } - if ($filter->getFeedIds()) { + if ($filter->getFeedIds() !== null) { $sub_search .= 'AND ' . $alias . 'id_feed IN ('; foreach ($filter->getFeedIds() as $feed_id) { $sub_search .= '?,'; @@ -854,7 +856,7 @@ SQL; $sub_search = rtrim($sub_search, ','); $sub_search .= ') '; } - if ($filter->getNotFeedIds()) { + if ($filter->getNotFeedIds() !== null) { $sub_search .= 'AND ' . $alias . 'id_feed NOT IN ('; foreach ($filter->getNotFeedIds() as $feed_id) { $sub_search .= '?,'; @@ -864,7 +866,7 @@ SQL; $sub_search .= ') '; } - if ($filter->getLabelIds()) { + if ($filter->getLabelIds() !== null) { if ($filter->getLabelIds() === '*') { $sub_search .= 'AND EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) '; } else { @@ -877,7 +879,7 @@ SQL; $sub_search .= ')) '; } } - if ($filter->getNotLabelIds()) { + if ($filter->getNotLabelIds() !== null) { if ($filter->getNotLabelIds() === '*') { $sub_search .= 'AND NOT EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) '; } else { @@ -891,7 +893,7 @@ SQL; } } - if ($filter->getLabelNames()) { + if ($filter->getLabelNames() !== null) { $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN ('; foreach ($filter->getLabelNames() as $label_name) { $sub_search .= '?,'; @@ -900,7 +902,7 @@ SQL; $sub_search = rtrim($sub_search, ','); $sub_search .= ')) '; } - if ($filter->getNotLabelNames()) { + if ($filter->getNotLabelNames() !== null) { $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN ('; foreach ($filter->getNotLabelNames() as $label_name) { $sub_search .= '?,'; @@ -910,57 +912,57 @@ SQL; $sub_search .= ')) '; } - if ($filter->getAuthor()) { + if ($filter->getAuthor() !== null) { foreach ($filter->getAuthor() as $author) { $sub_search .= 'AND ' . $alias . 'author LIKE ? '; $values[] = "%{$author}%"; } } - if ($filter->getIntitle()) { + if ($filter->getIntitle() !== null) { foreach ($filter->getIntitle() as $title) { $sub_search .= 'AND ' . $alias . 'title LIKE ? '; $values[] = "%{$title}%"; } } - if ($filter->getTags()) { + if ($filter->getTags() !== null) { foreach ($filter->getTags() as $tag) { $sub_search .= 'AND ' . static::sqlConcat('TRIM(' . $alias . 'tags) ', " ' #'") . ' LIKE ? '; $values[] = "%{$tag} #%"; } } - if ($filter->getInurl()) { + if ($filter->getInurl() !== null) { foreach ($filter->getInurl() as $url) { $sub_search .= 'AND ' . $alias . 'link LIKE ? '; $values[] = "%{$url}%"; } } - if ($filter->getNotAuthor()) { + if ($filter->getNotAuthor() !== null) { foreach ($filter->getNotAuthor() as $author) { $sub_search .= 'AND ' . $alias . 'author NOT LIKE ? '; $values[] = "%{$author}%"; } } - if ($filter->getNotIntitle()) { + if ($filter->getNotIntitle() !== null) { foreach ($filter->getNotIntitle() as $title) { $sub_search .= 'AND ' . $alias . 'title NOT LIKE ? '; $values[] = "%{$title}%"; } } - if ($filter->getNotTags()) { + if ($filter->getNotTags() !== null) { foreach ($filter->getNotTags() as $tag) { $sub_search .= 'AND ' . static::sqlConcat('TRIM(' . $alias . 'tags) ', " ' #'") . ' NOT LIKE ? '; $values[] = "%{$tag} #%"; } } - if ($filter->getNotInurl()) { + if ($filter->getNotInurl() !== null) { foreach ($filter->getNotInurl() as $url) { $sub_search .= 'AND ' . $alias . 'link NOT LIKE ? '; $values[] = "%{$url}%"; } } - if ($filter->getSearch()) { + if ($filter->getSearch() !== null) { foreach ($filter->getSearch() as $search_value) { if (static::isCompressed()) { // MySQL-only $sub_search .= 'AND CONCAT(' . $alias . 'title, UNCOMPRESS(' . $alias . 'content_bin)) LIKE ? '; @@ -972,7 +974,7 @@ SQL; } } } - if ($filter->getNotSearch()) { + if ($filter->getNotSearch() !== null) { foreach ($filter->getNotSearch() as $search_value) { if (static::isCompressed()) { // MySQL-only $sub_search .= 'AND CONCAT(' . $alias . 'title, UNCOMPRESS(' . $alias . 'content_bin)) NOT LIKE ? '; @@ -1163,9 +1165,11 @@ SQL; $stm = $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filters, $date_min); if ($stm) { while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { - /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, - * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ - yield FreshRSS_Entry::fromArray($row); + if (is_array($row)) { + /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, + * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ + yield FreshRSS_Entry::fromArray($row); + } } } } @@ -1206,9 +1210,11 @@ SQL; return; } while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { - /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, - * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ - yield FreshRSS_Entry::fromArray($row); + if (is_array($row)) { + /** @var array{'id':string,'id_feed':int,'guid':string,'title':string,'author':string,'content':string,'link':string,'date':int, + * 'hash':string,'is_read':int,'is_favorite':int,'tags':string,'attributes'?:string} $row */ + yield FreshRSS_Entry::fromArray($row); + } } } @@ -1283,7 +1289,7 @@ SQL; $affected = 0; $guidsChunks = array_chunk($guids, FreshRSS_DatabaseDAO::MAX_VARIABLE_NUMBER); foreach ($guidsChunks as $guidsChunk) { - $affected += $this->updateLastSeen($id_feed, $guidsChunk, $mtime); + $affected += ($this->updateLastSeen($id_feed, $guidsChunk, $mtime) ?: 0); } return $affected; } diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 66feb567b..1a87d03be 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -80,7 +80,7 @@ SQL; //if (true) { //Speed heuristics //TODO: Not implemented yet for SQLite (so always call IDs one by one) $affected = 0; foreach ($ids as $id) { - $affected += $this->markRead($id, $is_read); + $affected += ($this->markRead($id, $is_read) ?: 0); } return $affected; //} diff --git a/app/Models/Factory.php b/app/Models/Factory.php index eef3c81f5..f69c7f6aa 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -14,7 +14,7 @@ class FreshRSS_Factory { * @throws Minz_ConfigurationNamespaceException|Minz_PDOConnectionException */ public static function createCategoryDao(?string $username = null): FreshRSS_CategoryDAO { - switch (FreshRSS_Context::$system_conf->db['type'] ?? '') { + switch (FreshRSS_Context::systemConf()->db['type'] ?? '') { case 'sqlite': return new FreshRSS_CategoryDAOSQLite($username); default: @@ -26,7 +26,7 @@ class FreshRSS_Factory { * @throws Minz_ConfigurationNamespaceException|Minz_PDOConnectionException */ public static function createFeedDao(?string $username = null): FreshRSS_FeedDAO { - switch (FreshRSS_Context::$system_conf->db['type'] ?? '') { + switch (FreshRSS_Context::systemConf()->db['type'] ?? '') { case 'sqlite': return new FreshRSS_FeedDAOSQLite($username); default: @@ -38,7 +38,7 @@ class FreshRSS_Factory { * @throws Minz_ConfigurationNamespaceException|Minz_PDOConnectionException */ public static function createEntryDao(?string $username = null): FreshRSS_EntryDAO { - switch (FreshRSS_Context::$system_conf->db['type'] ?? '') { + switch (FreshRSS_Context::systemConf()->db['type'] ?? '') { case 'sqlite': return new FreshRSS_EntryDAOSQLite($username); case 'pgsql': @@ -52,7 +52,7 @@ class FreshRSS_Factory { * @throws Minz_ConfigurationNamespaceException|Minz_PDOConnectionException */ public static function createTagDao(?string $username = null): FreshRSS_TagDAO { - switch (FreshRSS_Context::$system_conf->db['type'] ?? '') { + switch (FreshRSS_Context::systemConf()->db['type'] ?? '') { case 'sqlite': return new FreshRSS_TagDAOSQLite($username); case 'pgsql': @@ -66,7 +66,7 @@ class FreshRSS_Factory { * @throws Minz_ConfigurationNamespaceException|Minz_PDOConnectionException */ public static function createStatsDAO(?string $username = null): FreshRSS_StatsDAO { - switch (FreshRSS_Context::$system_conf->db['type'] ?? '') { + switch (FreshRSS_Context::systemConf()->db['type'] ?? '') { case 'sqlite': return new FreshRSS_StatsDAOSQLite($username); case 'pgsql': @@ -80,7 +80,7 @@ class FreshRSS_Factory { * @throws Minz_ConfigurationNamespaceException|Minz_PDOConnectionException */ public static function createDatabaseDAO(?string $username = null): FreshRSS_DatabaseDAO { - switch (FreshRSS_Context::$system_conf->db['type'] ?? '') { + switch (FreshRSS_Context::systemConf()->db['type'] ?? '') { case 'sqlite': return new FreshRSS_DatabaseDAOSQLite($username); case 'pgsql': diff --git a/app/Models/Feed.php b/app/Models/Feed.php index dbe6aaa73..024a7841a 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -82,7 +82,7 @@ class FreshRSS_Feed extends Minz_Model { public function hash(): string { if ($this->hash == '') { - $salt = FreshRSS_Context::$system_conf->salt; + $salt = FreshRSS_Context::systemConf()->salt; $this->hash = hash('crc32b', $salt . $this->url); } return $this->hash; @@ -126,7 +126,7 @@ class FreshRSS_Feed extends Minz_Model { return $simplePie == null ? [] : iterator_to_array($this->loadEntries($simplePie)); } public function name(bool $raw = false): string { - return $raw || $this->name != '' ? $this->name : preg_replace('%^https?://(www[.])?%i', '', $this->url); + return $raw || $this->name != '' ? $this->name : (preg_replace('%^https?://(www[.])?%i', '', $this->url) ?? ''); } /** @return string HTML-encoded URL of the Web site of the feed */ public function website(): string { @@ -179,7 +179,7 @@ class FreshRSS_Feed extends Minz_Model { if ($raw) { $ttl = $this->ttl; if ($this->mute && FreshRSS_Feed::TTL_DEFAULT === $ttl) { - $ttl = FreshRSS_Context::$user_conf ? FreshRSS_Context::$user_conf->ttl_default : 3600; + $ttl = FreshRSS_Context::userConf()->ttl_default; } return $ttl * ($this->mute ? -1 : 1); } @@ -331,7 +331,7 @@ class FreshRSS_Feed extends Minz_Model { } else { $url = htmlspecialchars_decode($this->url, ENT_QUOTES); if ($this->httpAuth != '') { - $url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url); + $url = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url) ?? ''; } $simplePie = customSimplePie($this->attributes()); if (substr($url, -11) === '#force_feed') { @@ -342,7 +342,7 @@ class FreshRSS_Feed extends Minz_Model { if (!$loadDetails) { //Only activates auto-discovery when adding a new feed $simplePie->set_autodiscovery_level(SIMPLEPIE_LOCATOR_NONE); } - if ($this->attributes('clear_cache')) { + if ($this->attributeBoolean('clear_cache')) { // Do not use `$simplePie->enable_cache(false);` as it would prevent caching in multiuser context $this->clearCache(); } @@ -370,7 +370,7 @@ class FreshRSS_Feed extends Minz_Model { if ($loadDetails) { // si on a utilisé l’auto-discover, notre url va avoir changé - $subscribe_url = $simplePie->subscribe_url(false); + $subscribe_url = $simplePie->subscribe_url(false) ?? ''; if ($this->name(true) === '') { //HTML to HTML-PRE //ENT_COMPAT except '&' @@ -385,11 +385,11 @@ class FreshRSS_Feed extends Minz_Model { } } else { //The case of HTTP 301 Moved Permanently - $subscribe_url = $simplePie->subscribe_url(true); + $subscribe_url = $simplePie->subscribe_url(true) ?? ''; } $clean_url = SimplePie_Misc::url_remove_credentials($subscribe_url); - if ($subscribe_url !== null && $subscribe_url !== $url) { + if ($subscribe_url !== '' && $subscribe_url !== $url) { $this->_url($clean_url); } @@ -411,7 +411,7 @@ class FreshRSS_Feed extends Minz_Model { $testGuids = []; $guids = []; $links = []; - $hadBadGuids = $this->attributes('hasBadGuids'); + $hadBadGuids = $this->attributeBoolean('hasBadGuids'); $items = $simplePie->get_items(); if (empty($items)) { @@ -426,7 +426,10 @@ class FreshRSS_Feed extends Minz_Model { $hasUniqueGuids &= empty($testGuids['_' . $guid]); $testGuids['_' . $guid] = true; $guids[] = $guid; - $links[] = $item->get_permalink(); + $permalink = $item->get_permalink(); + if ($permalink != null) { + $links[] = $permalink; + } } if ($hadBadGuids != !$hasUniqueGuids) { @@ -444,7 +447,7 @@ class FreshRSS_Feed extends Minz_Model { /** @return Traversable<FreshRSS_Entry> */ public function loadEntries(SimplePie $simplePie): Traversable { - $hasBadGuids = $this->attributes('hasBadGuids'); + $hasBadGuids = $this->attributeBoolean('hasBadGuids'); $items = $simplePie->get_items(); if (empty($items)) { @@ -560,15 +563,15 @@ class FreshRSS_Feed extends Minz_Model { $title == '' ? '' : $title, $authorNames, $content == '' ? '' : $content, - $link == '' ? '' : $link, + $link == null ? '' : $link, $date ?: time() ); $entry->_tags($tags); $entry->_feed($this); if (!empty($attributeThumbnail['url'])) { - $entry->_attributes('thumbnail', $attributeThumbnail); + $entry->_attribute('thumbnail', $attributeThumbnail); } - $entry->_attributes('enclosures', $attributeEnclosures); + $entry->_attribute('enclosures', $attributeEnclosures); $entry->hash(); //Must be computed before loading full content $entry->loadCompleteContent(); // Optionally load full content for truncated feeds @@ -587,11 +590,14 @@ class FreshRSS_Feed extends Minz_Model { if ($this->httpAuth != '') { $feedSourceUrl = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $feedSourceUrl); } + if ($feedSourceUrl == null) { + return null; + } // Same naming conventions than https://rss-bridge.github.io/rss-bridge/Bridge_API/XPathAbstract.html // https://rss-bridge.github.io/rss-bridge/Bridge_API/BridgeAbstract.html#collectdata /** @var array<string,string> $xPathSettings */ - $xPathSettings = $this->attributes('xpath'); + $xPathSettings = $this->attributeArray('xpath'); $xPathFeedTitle = $xPathSettings['feedTitle'] ?? ''; $xPathItem = $xPathSettings['item'] ?? ''; $xPathItemTitle = $xPathSettings['itemTitle'] ?? ''; @@ -725,9 +731,9 @@ class FreshRSS_Feed extends Minz_Model { * @throws JsonException */ public function keepMaxUnread() { - $keepMaxUnread = $this->attributes('keep_max_n_unread'); + $keepMaxUnread = $this->attributeInt('keep_max_n_unread'); if ($keepMaxUnread === null) { - $keepMaxUnread = FreshRSS_Context::$user_conf->mark_when['max_n_unread']; + $keepMaxUnread = FreshRSS_Context::userConf()->mark_when['max_n_unread']; } return is_int($keepMaxUnread) && $keepMaxUnread >= 0 ? $keepMaxUnread : null; } @@ -754,9 +760,9 @@ class FreshRSS_Feed extends Minz_Model { * @return int|false the number of lines affected, or false if not applicable */ public function markAsReadUponGone(bool $upstreamIsEmpty, int $maxTimestamp = 0) { - $readUponGone = $this->attributes('read_upon_gone'); + $readUponGone = $this->attributeBoolean('read_upon_gone'); if ($readUponGone === null) { - $readUponGone = FreshRSS_Context::$user_conf->mark_when['gone']; + $readUponGone = FreshRSS_Context::userConf()->mark_when['gone']; } if (!$readUponGone) { return false; @@ -782,13 +788,15 @@ class FreshRSS_Feed extends Minz_Model { * @return int|false */ public function cleanOldEntries() { - $archiving = $this->attributes('archiving'); - if ($archiving == null) { + /** @var array<string,bool|int|string>|null $archiving */ + $archiving = $this->attributeArray('archiving'); + if ($archiving === null) { $catDAO = FreshRSS_Factory::createCategoryDao(); $category = $catDAO->searchById($this->categoryId); - $archiving = $category == null ? null : $category->attributes('archiving'); - if ($archiving == null) { - $archiving = FreshRSS_Context::$user_conf->archiving; + $archiving = $category === null ? null : $category->attributeArray('archiving'); + /** @var array<string,bool|int|string>|null $archiving */ + if ($archiving === null) { + $archiving = FreshRSS_Context::userConf()->archiving; } } if (is_array($archiving)) { @@ -850,7 +858,7 @@ class FreshRSS_Feed extends Minz_Model { $hubFilename = PSHB_PATH . '/feeds/' . sha1($url) . '/!hub.json'; if ($hubFile = @file_get_contents($hubFilename)) { $hubJson = json_decode($hubFile, true); - if ($hubJson && empty($hubJson['error']) && + if (is_array($hubJson) && empty($hubJson['error']) && (empty($hubJson['lease_end']) || $hubJson['lease_end'] > time())) { return true; } @@ -862,8 +870,8 @@ class FreshRSS_Feed extends Minz_Model { $url = $this->selfUrl ?: $this->url; $hubFilename = PSHB_PATH . '/feeds/' . sha1($url) . '/!hub.json'; $hubFile = @file_get_contents($hubFilename); - $hubJson = $hubFile ? json_decode($hubFile, true) : []; - if (!isset($hubJson['error']) || $hubJson['error'] !== $error) { + $hubJson = is_string($hubFile) ? json_decode($hubFile, true) : null; + if (is_array($hubJson) && !isset($hubJson['error']) || $hubJson['error'] !== $error) { $hubJson['error'] = $error; file_put_contents($hubFilename, json_encode($hubJson)); Minz_Log::warning('Set error to ' . ($error ? 1 : 0) . ' for ' . $url, PSHB_LOG); @@ -876,13 +884,13 @@ class FreshRSS_Feed extends Minz_Model { */ public function pubSubHubbubPrepare() { $key = ''; - if (Minz_Request::serverIsPublic(FreshRSS_Context::$system_conf->base_url) && + if (Minz_Request::serverIsPublic(FreshRSS_Context::systemConf()->base_url) && $this->hubUrl && $this->selfUrl && @is_dir(PSHB_PATH)) { $path = PSHB_PATH . '/feeds/' . sha1($this->selfUrl); $hubFilename = $path . '/!hub.json'; if ($hubFile = @file_get_contents($hubFilename)) { $hubJson = json_decode($hubFile, true); - if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key'])) { + if (!is_array($hubJson) || empty($hubJson['key']) || !ctype_xdigit($hubJson['key'])) { $text = 'Invalid JSON for WebSub: ' . $this->url; Minz_Log::warning($text); Minz_Log::warning($text, PSHB_LOG); @@ -901,7 +909,7 @@ class FreshRSS_Feed extends Minz_Model { } } else { @mkdir($path, 0770, true); - $key = sha1($path . FreshRSS_Context::$system_conf->salt); + $key = sha1($path . FreshRSS_Context::systemConf()->salt); $hubJson = [ 'hub' => $this->hubUrl, 'key' => $key, @@ -913,7 +921,7 @@ class FreshRSS_Feed extends Minz_Model { Minz_Log::debug($text); Minz_Log::debug($text, PSHB_LOG); } - $currentUser = Minz_User::name(); + $currentUser = Minz_User::name() ?? ''; if (FreshRSS_user_Controller::checkUsername($currentUser) && !file_exists($path . '/' . $currentUser . '.txt')) { touch($path . '/' . $currentUser . '.txt'); } @@ -928,7 +936,7 @@ class FreshRSS_Feed extends Minz_Model { } else { $url = $this->url; //Always use current URL during unsubscribe } - if ($url && (Minz_Request::serverIsPublic(FreshRSS_Context::$system_conf->base_url) || !$state)) { + if ($url && (Minz_Request::serverIsPublic(FreshRSS_Context::systemConf()->base_url) || !$state)) { $hubFilename = PSHB_PATH . '/feeds/' . sha1($url) . '/!hub.json'; $hubFile = @file_get_contents($hubFilename); if ($hubFile === false) { @@ -936,7 +944,7 @@ class FreshRSS_Feed extends Minz_Model { return false; } $hubJson = json_decode($hubFile, true); - if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key']) || empty($hubJson['hub'])) { + if (!is_array($hubJson) || empty($hubJson['key']) || !ctype_xdigit($hubJson['key']) || empty($hubJson['hub'])) { Minz_Log::warning('Invalid JSON for WebSub: ' . $this->url); return false; } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index ac844217a..895d2d333 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -118,7 +118,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { // Merge existing and import attributes $existingAttributes = $feed_search->attributes(); $importAttributes = $feed->attributes(); - $feed->_attributes('', array_replace_recursive($existingAttributes, $importAttributes)); + $feed->_attributes(array_replace_recursive($existingAttributes, $importAttributes)); // Update some values of the existing feed using the import $values = [ @@ -190,11 +190,12 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } /** + * @param non-empty-string $key * @param string|array<mixed>|bool|int|null $value * @return int|false */ public function updateFeedAttribute(FreshRSS_Feed $feed, string $key, $value) { - $feed->_attributes($key, $value); + $feed->_attribute($key, $value); return $this->updateFeed( $feed->id(), ['attributes' => $feed->attributes()] @@ -236,6 +237,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if ($newCat === null) { $newCat = $catDAO->getDefault(); } + if ($newCat === null) { + return false; + } $sql = 'UPDATE `_feed` SET category=? WHERE category=?'; $stm = $this->pdo->prepare($sql); @@ -305,6 +309,8 @@ SQL; return; } while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + /** @var array{'id':int,'url':string,'kind':int,'category':int,'name':string,'website':string,'description':string,'lastUpdate':int,'priority'?:int, + * 'pathEntries'?:string,'httpAuth':string,'error':int|bool,'ttl'?:int,'attributes'?:string} $row */ yield $row; } } @@ -393,12 +399,12 @@ SQL; } } - /** @return array<string> */ + /** @return array<int,string> */ public function listTitles(int $id, int $limit = 0): array { $sql = 'SELECT title FROM `_entry` WHERE id_feed=:id_feed ORDER BY id DESC' . ($limit < 1 ? '' : ' LIMIT ' . intval($limit)); $res = $this->fetchColumn($sql, 0, [':id_feed' => $id]) ?? []; - /** @var array<string> $res */ + /** @var array<int,string> $res */ return $res; } @@ -609,7 +615,7 @@ SQL; $myFeed->_httpAuth(base64_decode($dao['httpAuth'] ?? '', true) ?: ''); $myFeed->_error($dao['error'] ?? 0); $myFeed->_ttl($dao['ttl'] ?? FreshRSS_Feed::TTL_DEFAULT); - $myFeed->_attributes('', $dao['attributes'] ?? ''); + $myFeed->_attributes($dao['attributes'] ?? ''); $myFeed->_nbNotRead($dao['cache_nbUnreads'] ?? -1); $myFeed->_nbEntries($dao['cache_nbEntries'] ?? -1); if (isset($dao['id'])) { diff --git a/app/Models/FilterAction.php b/app/Models/FilterAction.php index 6487d5100..bf5a79fe7 100644 --- a/app/Models/FilterAction.php +++ b/app/Models/FilterAction.php @@ -44,7 +44,7 @@ class FreshRSS_FilterAction { /** @param array|mixed|null $json */ public static function fromJSON($json): ?FreshRSS_FilterAction { - if (!empty($json['search']) && !empty($json['actions']) && is_array($json['actions'])) { + if (is_array($json) && !empty($json['search']) && !empty($json['actions']) && is_array($json['actions'])) { return new FreshRSS_FilterAction(new FreshRSS_BooleanSearch($json['search']), $json['actions']); } return null; diff --git a/app/Models/FilterActionsTrait.php b/app/Models/FilterActionsTrait.php index 869992b21..aefb549af 100644 --- a/app/Models/FilterActionsTrait.php +++ b/app/Models/FilterActionsTrait.php @@ -15,13 +15,11 @@ trait FreshRSS_FilterActionsTrait { private function filterActions(): array { if (empty($this->filterActions)) { $this->filterActions = []; - $filters = $this->attributes('filters'); - if (is_array($filters)) { - foreach ($filters as $filter) { - $filterAction = FreshRSS_FilterAction::fromJSON($filter); - if ($filterAction != null) { - $this->filterActions[] = $filterAction; - } + $filters = $this->attributeArray('filters') ?? []; + foreach ($filters as $filter) { + $filterAction = FreshRSS_FilterAction::fromJSON($filter); + if ($filterAction != null) { + $this->filterActions[] = $filterAction; } } } @@ -33,12 +31,12 @@ trait FreshRSS_FilterActionsTrait { */ private function _filterActions(?array $filterActions): void { $this->filterActions = $filterActions; - if (is_array($this->filterActions) && !empty($this->filterActions)) { - $this->_attributes('filters', array_map(static function (?FreshRSS_FilterAction $af) { + if ($this->filterActions !== null && !empty($this->filterActions)) { + $this->_attribute('filters', array_map(static function (?FreshRSS_FilterAction $af) { return $af == null ? null : $af->toJSON(); }, $this->filterActions)); } else { - $this->_attributes('filters', null); + $this->_attribute('filters', null); } } diff --git a/app/Models/FormAuth.php b/app/Models/FormAuth.php index 83fb60e3c..a8b4dab8a 100644 --- a/app/Models/FormAuth.php +++ b/app/Models/FormAuth.php @@ -23,7 +23,7 @@ class FreshRSS_FormAuth { $token_file = DATA_PATH . '/tokens/' . $token . '.txt'; $mtime = @filemtime($token_file) ?: 0; - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $cookie_duration = empty($limits['cookie_duration']) ? FreshRSS_Auth::DEFAULT_COOKIE_DURATION : $limits['cookie_duration']; if ($mtime + $cookie_duration < time()) { // Token has expired (> cookie_duration) or does not exist. @@ -42,7 +42,7 @@ class FreshRSS_FormAuth { private static function renewCookie(string $token) { $token_file = DATA_PATH . '/tokens/' . $token . '.txt'; if (touch($token_file)) { - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $cookie_duration = empty($limits['cookie_duration']) ? FreshRSS_Auth::DEFAULT_COOKIE_DURATION : $limits['cookie_duration']; $expire = time() + $cookie_duration; Minz_Session::setLongTermCookie('FreshRSS_login', $token, $expire); @@ -54,7 +54,7 @@ class FreshRSS_FormAuth { /** @return string|false */ public static function makeCookie(string $username, string $password_hash) { do { - $token = sha1(FreshRSS_Context::$system_conf->salt . $username . uniqid('' . mt_rand(), true)); + $token = sha1(FreshRSS_Context::systemConf()->salt . $username . uniqid('' . mt_rand(), true)); $token_file = DATA_PATH . '/tokens/' . $token . '.txt'; } while (file_exists($token_file)); @@ -78,7 +78,7 @@ class FreshRSS_FormAuth { } public static function purgeTokens(): void { - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $cookie_duration = empty($limits['cookie_duration']) ? FreshRSS_Auth::DEFAULT_COOKIE_DURATION : $limits['cookie_duration']; $oldest = time() - $cookie_duration; foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $file_info) { diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index 0e0bad623..9bdc255e3 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -248,8 +248,8 @@ WHERE c.id = f.category GROUP BY label ORDER BY data DESC SQL; - $res = $this->fetchAssoc($sql); /** @var array<array{'label':string,'data':int}>|null @res */ + $res = $this->fetchAssoc($sql); return $res == null ? [] : $res; } diff --git a/app/Models/SystemConfiguration.php b/app/Models/SystemConfiguration.php index 294ca1e3a..3efe33b15 100644 --- a/app/Models/SystemConfiguration.php +++ b/app/Models/SystemConfiguration.php @@ -5,7 +5,7 @@ declare(strict_types=1); * @property bool $allow_anonymous * @property bool $allow_anonymous_refresh * @property-read bool $allow_referrer - * @property-read bool $allow_robots + * @property bool $allow_robots * @property bool $api_enabled * @property string $archiving * @property 'form'|'http_auth'|'none' $auth_type @@ -16,7 +16,7 @@ declare(strict_types=1); * @property bool $force_email_validation * @property-read bool $http_auth_auto_register * @property-read string $http_auth_auto_register_email_field - * @property-read string $language + * @property string $language * @property array<string,int> $limits * @property-read string $logo_html * @property-read string $meta_description @@ -25,6 +25,7 @@ declare(strict_types=1); * @property-read bool $simplepie_syslog_enabled * @property bool $unsafe_autologin_enabled * @property array<string> $trusted_sources + * @property array<string,array<string,mixed>> $extensions */ final class FreshRSS_SystemConfiguration extends Minz_Configuration { diff --git a/app/Models/TagDAO.php b/app/Models/TagDAO.php index 8587e576f..fba27dc7b 100644 --- a/app/Models/TagDAO.php +++ b/app/Models/TagDAO.php @@ -96,11 +96,12 @@ SQL; } /** + * @param non-empty-string $key * @param mixed $value * @return int|false */ public function updateTagAttribute(FreshRSS_Tag $tag, string $key, $value) { - $tag->_attributes($key, $value); + $tag->_attribute($key, $value); return $this->updateTagAttributes($tag->id(), $tag->attributes()); } @@ -134,6 +135,7 @@ SQL; return; } while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + /** @var array{'id':int,'name':string,'attributes'?:array<string,mixed>} $row */ yield $row; } } @@ -410,7 +412,7 @@ SQL; $tag = new FreshRSS_Tag($dao['name']); $tag->_id($dao['id']); if (!empty($dao['attributes'])) { - $tag->_attributes('', $dao['attributes']); + $tag->_attributes($dao['attributes']); } if (isset($dao['unreads'])) { $tag->_nbUnread($dao['unreads']); diff --git a/app/Models/Themes.php b/app/Models/Themes.php index 53ae1dc27..fab29e986 100644 --- a/app/Models/Themes.php +++ b/app/Models/Themes.php @@ -38,11 +38,12 @@ class FreshRSS_Themes extends Minz_Model { if (file_exists($json_filename)) { $content = file_get_contents($json_filename) ?: ''; $res = json_decode($content, true); - if ($res && + if (is_array($res) && !empty($res['name']) && isset($res['files']) && is_array($res['files'])) { $res['id'] = $theme_id; + /** @var array{'id':string,'name':string,'author':string,'description':string,'version':float|string,'files':array<string>,'theme-color'?:string|array{'dark'?:string,'light'?:string,'default'?:string}} */ return $res; } } @@ -155,7 +156,7 @@ class FreshRSS_Themes extends Minz_Model { } if ($type == self::ICON_DEFAULT) { - if ((FreshRSS_Context::$user_conf && FreshRSS_Context::$user_conf->icons_as_emojis) + if ((FreshRSS_Context::hasUserConf() && FreshRSS_Context::userConf()->icons_as_emojis) // default to emoji alternate for some icons ) { $type = self::ICON_EMOJI; diff --git a/app/Models/UserConfiguration.php b/app/Models/UserConfiguration.php index 0aec3a05f..a1e0dbbaa 100644 --- a/app/Models/UserConfiguration.php +++ b/app/Models/UserConfiguration.php @@ -70,6 +70,7 @@ declare(strict_types=1); * @property-read bool $unsafe_autologin_enabled * @property string $view_mode * @property array<string,mixed> $volatile + * @property array<string,array<string,mixed>> $extensions */ final class FreshRSS_UserConfiguration extends Minz_Configuration { use FreshRSS_FilterActionsTrait; @@ -81,22 +82,37 @@ final class FreshRSS_UserConfiguration extends Minz_Configuration { } /** - * @phpstan-return ($key is non-empty-string ? mixed : array<string,mixed>) - * @return array<string,mixed>|mixed|null + * @param non-empty-string $key + * @return array<int|string,mixed>|null */ - public function attributes(string $key = '') { - if ($key === '') { - return []; // Not implemented for user configuration - } else { - return parent::param($key, null); - } + public function attributeArray(string $key): ?array { + $a = parent::param($key, null); + return is_array($a) ? $a : null; } - /** @param string|array<mixed>|bool|int|null $value Value, not HTML-encoded */ - public function _attributes(string $key, $value = null): void { - if ($key == '') { - return; // Not implemented for user configuration - } + /** @param non-empty-string $key */ + public function attributeBool(string $key): ?bool { + $a = parent::param($key, null); + return is_bool($a) ? $a : null; + } + + /** @param non-empty-string $key */ + public function attributeInt(string $key): ?int { + $a = parent::param($key, null); + return is_numeric($a) ? (int)$a : null; + } + + /** @param non-empty-string $key */ + public function attributeString(string $key): ?string { + $a = parent::param($key, null); + return is_string($a) ? $a : null; + } + + /** + * @param non-empty-string $key + * @param array<string,mixed>|mixed|null $value Value, not HTML-encoded + */ + public function _attribute(string $key, $value = null): void { parent::_param($key, $value); } } diff --git a/app/Services/ImportService.php b/app/Services/ImportService.php index f33ebed96..278fa4cec 100644 --- a/app/Services/ImportService.php +++ b/app/Services/ImportService.php @@ -68,7 +68,7 @@ class FreshRSS_Import_Service { // verify the user can import its categories/feeds. $nb_categories = count($categories); $nb_feeds = count($this->feedDAO->listFeeds()); - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; // Process the OPML outlines to get a list of categories and a list of // feeds elements indexed by their categories names. @@ -173,7 +173,7 @@ class FreshRSS_Import_Service { } if (isset($feed_elt['frss:cssFullContentFilter'])) { - $feed->_attributes('path_entries_filter', $feed_elt['frss:cssFullContentFilter']); + $feed->_attribute('path_entries_filter', $feed_elt['frss:cssFullContentFilter']); } if (isset($feed_elt['frss:filtersActionRead'])) { @@ -216,7 +216,7 @@ class FreshRSS_Import_Service { } if (!empty($xPathSettings)) { - $feed->_attributes('xpath', $xPathSettings); + $feed->_attribute('xpath', $xPathSettings); } // Call the extension hook @@ -263,7 +263,7 @@ class FreshRSS_Import_Service { $opml_url = checkUrl($category_element['frss:opmlUrl']); if ($opml_url != '') { $category->_kind(FreshRSS_Category::KIND_DYNAMIC_OPML); - $category->_attributes('opml_url', $opml_url); + $category->_attribute('opml_url', $opml_url); } } diff --git a/app/Utils/feverUtil.php b/app/Utils/feverUtil.php index 3c2b8bd2e..571c2bc00 100644 --- a/app/Utils/feverUtil.php +++ b/app/Utils/feverUtil.php @@ -29,10 +29,7 @@ class FreshRSS_fever_Util { * @throws FreshRSS_Context_Exception */ public static function getKeyPath(string $feverKey): string { - if (FreshRSS_Context::$system_conf === null) { - throw new FreshRSS_Context_Exception('System configuration not initialised!'); - } - $salt = sha1(FreshRSS_Context::$system_conf->salt); + $salt = sha1(FreshRSS_Context::systemConf()->salt); return self::FEVER_PATH . '/.key-' . $salt . '-' . $feverKey . '.txt'; } diff --git a/app/actualize_script.php b/app/actualize_script.php index 5eb76b3ec..df3327e10 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -19,11 +19,8 @@ $_SERVER['HTTP_HOST'] = ''; $app = new FreshRSS(); FreshRSS_Context::initSystem(); -if (FreshRSS_Context::$system_conf === null) { - throw new FreshRSS_Context_Exception('System configuration not initialised!'); -} -FreshRSS_Context::$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!) -define('SIMPLEPIE_SYSLOG_ENABLED', FreshRSS_Context::$system_conf->simplepie_syslog_enabled); +FreshRSS_Context::systemConf()->auth_type = 'none'; // avoid necessity to be logged in (not saved!) +define('SIMPLEPIE_SYSLOG_ENABLED', FreshRSS_Context::systemConf()->simplepie_syslog_enabled); /** * Writes to FreshRSS admin log, and if it is not already done by default, @@ -62,7 +59,7 @@ notice('FreshRSS starting feeds actualization at ' . $begin_date->format('c')); // make sure the PHP setup of the CLI environment is compatible with FreshRSS as well echo 'Failed requirements!', "\n"; -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); ob_clean(); echo 'Results: ', "\n"; //Buffered @@ -71,24 +68,24 @@ echo 'Results: ', "\n"; //Buffered // Users are processed in a random order but always start with default user $users = listUsers(); shuffle($users); -if (FreshRSS_Context::$system_conf->default_user !== '') { - array_unshift($users, FreshRSS_Context::$system_conf->default_user); +if (FreshRSS_Context::systemConf()->default_user !== '') { + array_unshift($users, FreshRSS_Context::systemConf()->default_user); $users = array_unique($users); } -$limits = FreshRSS_Context::$system_conf->limits; +$limits = FreshRSS_Context::systemConf()->limits; $min_last_activity = time() - $limits['max_inactivity']; foreach ($users as $user) { FreshRSS_Context::initUser($user); - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { notice('Invalid user ' . $user); continue; } - if (!FreshRSS_Context::$user_conf->enabled) { + if (!FreshRSS_Context::userConf()->enabled) { notice('FreshRSS skip disabled user ' . $user); continue; } - if (($user !== FreshRSS_Context::$system_conf->default_user) && + if (($user !== FreshRSS_Context::systemConf()->default_user) && (FreshRSS_UserDAO::mtime($user) < $min_last_activity)) { notice('FreshRSS skip inactive user ' . $user); continue; @@ -106,7 +103,7 @@ foreach ($users as $user) { notice('FreshRSS actualize ' . $user . '…'); echo $user, ' '; //Buffered - Minz_ExtensionManager::callHook('freshrss_user_maintenance'); + Minz_ExtensionManager::callHookVoid('freshrss_user_maintenance'); $app->run(); if (!invalidateHttpCache()) { diff --git a/app/install.php b/app/install.php index 348ba079b..b4da2911f 100644 --- a/app/install.php +++ b/app/install.php @@ -70,20 +70,20 @@ function saveStep1(): void { // First, we try to get previous configurations FreshRSS_Context::initSystem(); - FreshRSS_Context::initUser(FreshRSS_Context::$system_conf->default_user, false); + FreshRSS_Context::initUser(FreshRSS_Context::systemConf()->default_user, false); // Then, we set $_SESSION vars Minz_Session::_params([ - 'title' => FreshRSS_Context::$system_conf->title, - 'auth_type' => FreshRSS_Context::$system_conf->auth_type, - 'default_user' => Minz_User::name(), - 'passwordHash' => FreshRSS_Context::$user_conf->passwordHash, - 'bd_type' => FreshRSS_Context::$system_conf->db['type'] ?? '', - 'bd_host' => FreshRSS_Context::$system_conf->db['host'] ?? '', - 'bd_user' => FreshRSS_Context::$system_conf->db['user'] ?? '', - 'bd_password' => FreshRSS_Context::$system_conf->db['password'] ?? '', - 'bd_base' => FreshRSS_Context::$system_conf->db['base'] ?? '', - 'bd_prefix' => FreshRSS_Context::$system_conf->db['prefix'] ?? '', + 'title' => FreshRSS_Context::systemConf()->title, + 'auth_type' => FreshRSS_Context::systemConf()->auth_type, + 'default_user' => Minz_User::name() ?? '', + 'passwordHash' => FreshRSS_Context::userConf()->passwordHash, + 'bd_type' => FreshRSS_Context::systemConf()->db['type'] ?? '', + 'bd_host' => FreshRSS_Context::systemConf()->db['host'] ?? '', + 'bd_user' => FreshRSS_Context::systemConf()->db['user'] ?? '', + 'bd_password' => FreshRSS_Context::systemConf()->db['password'] ?? '', + 'bd_base' => FreshRSS_Context::systemConf()->db['base'] ?? '', + 'bd_prefix' => FreshRSS_Context::systemConf()->db['prefix'] ?? '', 'bd_error' => false, ]); @@ -191,33 +191,34 @@ function saveStep3(): bool { Minz_Translate::init(Minz_Session::paramString('language')); if (!empty($_POST)) { - if (param('auth_type', 'form') != '') { - FreshRSS_Context::$system_conf->auth_type = param('auth_type', 'form'); - Minz_Session::_param('auth_type', FreshRSS_Context::$system_conf->auth_type); + $auth_type = param('auth_type', 'form'); + if (in_array($auth_type, ['form', 'http_auth', 'none'], true)) { + FreshRSS_Context::systemConf()->auth_type = $auth_type; + Minz_Session::_param('auth_type', FreshRSS_Context::systemConf()->auth_type); } else { return false; } $password_plain = param('passwordPlain', ''); - if (FreshRSS_Context::$system_conf->auth_type === 'form' && $password_plain == '') { + if (FreshRSS_Context::systemConf()->auth_type === 'form' && $password_plain == '') { return false; } if (FreshRSS_user_Controller::checkUsername(param('default_user', ''))) { - FreshRSS_Context::$system_conf->default_user = param('default_user', ''); - Minz_Session::_param('default_user', FreshRSS_Context::$system_conf->default_user); + FreshRSS_Context::systemConf()->default_user = param('default_user', ''); + Minz_Session::_param('default_user', FreshRSS_Context::systemConf()->default_user); } else { return false; } - if (FreshRSS_Context::$system_conf->auth_type === 'http_auth' && + if (FreshRSS_Context::systemConf()->auth_type === 'http_auth' && connectionRemoteAddress() !== '' && empty($_SERVER['REMOTE_USER']) && empty($_SERVER['REDIRECT_REMOTE_USER']) && // No safe authentication HTTP headers (!empty($_SERVER['HTTP_REMOTE_USER']) || !empty($_SERVER['HTTP_X_WEBAUTH_USER'])) // but has unsafe authentication HTTP headers ) { // Trust by default the remote IP address (e.g. last proxy) used during install to provide remote user name via unsafe HTTP header - FreshRSS_Context::$system_conf->trusted_sources[] = connectionRemoteAddress(); - FreshRSS_Context::$system_conf->trusted_sources = array_unique(FreshRSS_Context::$system_conf->trusted_sources); + FreshRSS_Context::systemConf()->trusted_sources[] = connectionRemoteAddress(); + FreshRSS_Context::systemConf()->trusted_sources = array_unique(FreshRSS_Context::systemConf()->trusted_sources); } // Create default user files but first, we delete previous data to @@ -244,7 +245,7 @@ function saveStep3(): bool { return false; } - FreshRSS_Context::$system_conf->save(); + FreshRSS_Context::systemConf()->save(); header('Location: index.php?step=4'); } diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index b59293f21..729b61f5c 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -48,7 +48,7 @@ <a href="<?= _url('index', 'logs') ?>"><?= _t('gen.menu.logs') ?></a> </li> <?php } ?> - <?= Minz_ExtensionManager::callHook('menu_configuration_entry') ?> + <?= Minz_ExtensionManager::callHookString('menu_configuration_entry') ?> </ul> </li> @@ -68,7 +68,7 @@ <li class="item<?= Minz_Request::controllerName() === 'update' && Minz_Request::actionName() === 'checkInstall' ? ' active' : '' ?>"> <a href="<?= _url('update', 'checkInstall') ?>"><?= _t('gen.menu.check_install') ?></a> </li> - <?php if (!FreshRSS_Context::$system_conf->disable_update) { ?> + <?php if (!FreshRSS_Context::systemConf()->disable_update) { ?> <li class="item<?= Minz_Request::controllerName() === 'update' && Minz_Request::actionName() === 'index' ? ' active' : '' ?>"> <a href="<?= _url('update', 'index') ?>"><?= _t('gen.menu.update') ?></a> </li> @@ -76,7 +76,7 @@ <li class="item<?= Minz_Request::actionName() === 'logs' ? ' active' : '' ?>"> <a href="<?= _url('index', 'logs') ?>"><?= _t('gen.menu.logs') ?></a> </li> - <?= Minz_ExtensionManager::callHook('menu_admin_entry') ?> + <?= Minz_ExtensionManager::callHookString('menu_admin_entry') ?> </ul> </li> <?php } ?> diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index 1d491ad79..29b110647 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -3,7 +3,7 @@ /** @var FreshRSS_View $this */ $actual_view = Minz_Request::actionName(); $class = ''; - if (FreshRSS_Context::$user_conf->hide_read_feeds && + if (FreshRSS_Context::userConf()->hide_read_feeds && FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_NOT_READ) && !FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_READ)) { $class = ' state_unread'; @@ -54,7 +54,7 @@ <?php $t_active = FreshRSS_Context::isCurrentGet('T'); - $t_show = ($t_active && in_array(FreshRSS_Context::$user_conf->display_categories, ['active', 'remember'], true)) || FreshRSS_Context::$user_conf->display_categories === 'all'; + $t_show = ($t_active && in_array(FreshRSS_Context::userConf()->display_categories, ['active', 'remember'], true)) || FreshRSS_Context::userConf()->display_categories === 'all'; ?> <li id="tags" class="tree-folder category tags<?= $t_active ? ' active' : '' ?>" data-unread="<?= format_number($this->nbUnreadTags) ?>"> <div class="tree-folder-title"> @@ -85,11 +85,11 @@ foreach ($this->categories as $cat): $feeds = $cat->feeds(); - $position = $cat->attributes('position'); + $position = $cat->attributeInt('position'); if (!empty($feeds)) { $c_active = FreshRSS_Context::isCurrentGet('c_' . $cat->id()); - $c_show = ($c_active && in_array(FreshRSS_Context::$user_conf->display_categories, ['active', 'remember'], true)) - || FreshRSS_Context::$user_conf->display_categories === 'all'; + $c_show = ($c_active && in_array(FreshRSS_Context::userConf()->display_categories, ['active', 'remember'], true)) + || FreshRSS_Context::userConf()->display_categories === 'all'; ?> <li id="c_<?= $cat->id() ?>" class="tree-folder category<?= $c_active ? ' active' : '' ?>"<?= null === $position ? '' : " data-position='$position'" ?> data-unread="<?= $cat->nbNotRead() ?>"> @@ -124,11 +124,11 @@ ?> <li id="f_<?= $feed->id() ?>" class="item feed<?= $f_active_class, $mute_class, $error_class, $empty_class ?>" title="<?= $error_title, $empty_title ?>" data-unread="<?= $feed->nbNotRead() ?>" data-priority="<?= $feed->priority() ?>"><?php - if ($f_active || $nbFeedsTotal < FreshRSS_Context::$user_conf->simplify_over_n_feeds): + if ($f_active || $nbFeedsTotal < FreshRSS_Context::userConf()->simplify_over_n_feeds): ?><div class="dropdown no-mobile"> <div class="dropdown-target"></div><a class="dropdown-toggle" data-fweb="<?= $feed->website() ?>"><?= _i('configure') ?></a><?php /* feed_config_template */ ?> </div><?php - if (FreshRSS_Context::$user_conf->show_favicons) { ?><img class="favicon test" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php } + if (FreshRSS_Context::userConf()->show_favicons) { ?><img class="favicon test" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php } endif; ?><a class="item-title" data-unread="<?= format_number($feed->nbNotRead()) ?>" href="<?= _url('index', $actual_view, 'get', 'f_' . $feed->id()) . $state_filter_manual ?>"><?= $feed->name() ?></a></li> @@ -179,7 +179,7 @@ <li class="item"><a class="configure open-slider" href="<?= $url ?>"><?= _t('gen.action.manage') ?></a></li> <li class="item"><a href="<?= _url('feed', 'actualize', 'id', '------') ?>"><?= _t('gen.action.actualize') ?></a></li> <li class="item"> - <?php $confirm = FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : ''; ?> + <?php $confirm = FreshRSS_Context::userConf()->reading_confirm ? 'confirm" disabled="disabled' : ''; ?> <button class="read_all as-link <?= $confirm ?>" form="mark-read-aside" formaction="<?= _url('entry', 'read', 'get', 'f_------') ?>" diff --git a/app/layout/header.phtml b/app/layout/header.phtml index e5722abb6..18e67fd2d 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -4,18 +4,18 @@ <header class="header"> <div class="item title"> <a href="<?= _url('index', 'index') ?>"> - <?php if (FreshRSS_Context::$system_conf->logo_html == '') { ?> + <?php if (FreshRSS_Context::systemConf()->logo_html == '') { ?> <img class="logo" src="<?= _i('FreshRSS-logo', FreshRSS_Themes::ICON_URL) ?>" alt="FreshRSS" loading="lazy" /> <?php } else { - echo FreshRSS_Context::$system_conf->logo_html; + echo FreshRSS_Context::systemConf()->logo_html; } ?> </a> </div> <div class="item search"> - <?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous) { ?> + <?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::systemConf()->allow_anonymous) { ?> <form action="<?= _url('index', 'index') ?>" method="get"> <div class="stick"> <input type="search" name="search" id="search" @@ -81,7 +81,7 @@ <li class="item"><a href="<?= _url('configure', 'shortcut') ?>"><?= _t('gen.menu.shortcuts') ?></a></li> <li class="item"><a href="<?= _url('configure', 'queries') ?>"><?= _t('gen.menu.queries') ?></a></li> <li class="item"><a href="<?= _url('extension', 'index') ?>"><?= _t('gen.menu.extensions') ?></a></li> - <?= Minz_ExtensionManager::callHook('menu_configuration_entry') ?> + <?= Minz_ExtensionManager::callHookString('menu_configuration_entry') ?> </ul> </li> <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> @@ -94,10 +94,10 @@ <li class="item"><a href="<?= _url('user', 'manage') ?>"><?= _t('gen.menu.user_management') ?></a></li> <li class="item"><a href="<?= _url('auth', 'index') ?>"><?= _t('gen.menu.authentication') ?></a></li> <li class="item"><a href="<?= _url('update', 'checkInstall') ?>"><?= _t('gen.menu.check_install') ?></a></li> - <?php if (!FreshRSS_Context::$system_conf->disable_update) { ?> + <?php if (!FreshRSS_Context::systemConf()->disable_update) { ?> <li class="item"><a href="<?= _url('update', 'index') ?>"><?= _t('gen.menu.update') ?></a></li> <?php } ?> - <?= Minz_ExtensionManager::callHook('menu_admin_entry') ?> + <?= Minz_ExtensionManager::callHookString('menu_admin_entry') ?> </ul> </li> <?php } ?> @@ -111,7 +111,7 @@ <a href="<?= _url('index', 'tos') ?>"><?= _t('index.tos.title')?></a> </li> <?php } ?> - <?= Minz_ExtensionManager::callHook('menu_other_entry') ?> + <?= Minz_ExtensionManager::callHookString('menu_other_entry') ?> </ul> </li> </ul> diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 49cf85a02..adbd52327 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -4,13 +4,13 @@ FreshRSS::preLayout(); ?> <!DOCTYPE html> -<html lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>"<?php +<html lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>"<?php $class = ''; if (_t('gen.dir') === 'rtl') { echo ' dir="rtl"'; $class = 'rtl '; } -?> class="<?= $class ?><?= (FreshRSS_Context::$user_conf->darkMode === 'no') ? '' : 'darkMode_' . FreshRSS_Context::$user_conf->darkMode ?>"> +?> class="<?= $class ?><?= (FreshRSS_Context::userConf()->darkMode === 'no') ? '' : 'darkMode_' . FreshRSS_Context::userConf()->darkMode ?>"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> @@ -26,10 +26,10 @@ if (_t('gen.dir') === 'rtl') { <link rel="apple-touch-icon" href="<?= Minz_Url::display('/themes/icons/apple-touch-icon.png') ?>" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> - <meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::$system_conf->title ?>"> + <meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::systemConf()->title ?>"> <meta name="msapplication-TileColor" content="#FFF" /> <meta name="theme-color" content="#FFF" /> -<?php if (!FreshRSS_Context::$system_conf->allow_referrer) { ?> +<?php if (!FreshRSS_Context::systemConf()->allow_referrer) { ?> <meta name="referrer" content="never" /> <?php } ?> <?= FreshRSS_View::headTitle() ?> @@ -39,8 +39,8 @@ if (_t('gen.dir') === 'rtl') { $url_rss = $url_base; $url_rss['a'] = 'rss'; unset($url_rss['params']['rid']); - if (FreshRSS_Context::$user_conf->since_hours_posts_per_rss) { - $url_rss['params']['hours'] = FreshRSS_Context::$user_conf->since_hours_posts_per_rss; + if (FreshRSS_Context::userConf()->since_hours_posts_per_rss) { + $url_rss['params']['hours'] = FreshRSS_Context::userConf()->since_hours_posts_per_rss; } ?> <link rel="alternate" type="application/rss+xml" title="<?= $this->rss_title ?>" href="<?= Minz_Url::display($url_rss) ?>" /> @@ -50,7 +50,7 @@ if (_t('gen.dir') === 'rtl') { unset($opml_rss['params']['rid']); ?> <link rel="outline" type="text/x-opml" title="OPML" href="<?= Minz_Url::display($opml_rss) ?>" /> -<?php } if (FreshRSS_Context::$system_conf->allow_robots) { ?> +<?php } if (FreshRSS_Context::systemConf()->allow_robots) { ?> <meta name="description" content="<?= htmlspecialchars(FreshRSS_Context::$name . ' | ' . FreshRSS_Context::$description, ENT_COMPAT, 'UTF-8') ?>" /> <?php } else { ?> <meta name="robots" content="noindex,nofollow" /> diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 72838094f..3d0027f17 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -84,7 +84,7 @@ <a href="<?= _url('configure', 'queries') ?>"><?= _i('configure') ?></a> </li> - <?php foreach (FreshRSS_Context::$user_conf->queries as $raw_query): ?> + <?php foreach (FreshRSS_Context::userConf()->queries as $raw_query): ?> <li class="item query"> <?php if (!empty($raw_query['url'])): ?> <a href="<?= $raw_query['url'] ?>"><?= $raw_query['name'] ?? $raw_query['url'] ?></a> @@ -97,7 +97,7 @@ <?php $classSeparator = ''; - if (count(FreshRSS_Context::$user_conf->queries) > 0) { + if (count(FreshRSS_Context::userConf()->queries) > 0) { $classSeparator = ' separator'; } @@ -140,7 +140,7 @@ <div class="group stick" id="nav_menu_read_all"> <form id="mark-read-menu" method="post"> - <?php $confirm = FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : ''; ?> + <?php $confirm = FreshRSS_Context::userConf()->reading_confirm ? 'confirm" disabled="disabled' : ''; ?> <button class="read_all btn <?= $confirm ?>" form="mark-read-menu" formaction="<?= Minz_Url::display($mark_read_url) ?>" @@ -196,6 +196,9 @@ <?php $readingModes = FreshRSS_ReadingMode::getReadingModes(); $readingModes = Minz_ExtensionManager::callHook('nav_reading_modes', $readingModes); + if (!is_iterable($readingModes)) { + $readingModes = FreshRSS_ReadingMode::getReadingModes(); + } /** @var FreshRSS_ReadingMode $mode */ foreach ($readingModes as $mode) { @@ -210,12 +213,12 @@ <?php $url_output['a'] = 'rss'; - if (FreshRSS_Context::$user_conf->token) { + if (FreshRSS_Context::userConf()->token) { $url_output['params']['user'] = Minz_User::name(); - $url_output['params']['token'] = FreshRSS_Context::$user_conf->token; + $url_output['params']['token'] = FreshRSS_Context::userConf()->token; } - if (FreshRSS_Context::$user_conf->since_hours_posts_per_rss) { - $url_output['params']['hours'] = FreshRSS_Context::$user_conf->since_hours_posts_per_rss; + if (FreshRSS_Context::userConf()->since_hours_posts_per_rss) { + $url_output['params']['hours'] = FreshRSS_Context::userConf()->since_hours_posts_per_rss; } ?> <a class="view-rss btn" target="_blank" rel="noreferrer" title="<?= _t('index.menu.rss_view') ?>" href="<?= Minz_Url::display($url_output) ?>"> @@ -223,7 +226,7 @@ </a> </div> - <?php $nav_menu_hooks = Minz_ExtensionManager::callHook('nav_menu'); ?> + <?php $nav_menu_hooks = Minz_ExtensionManager::callHookString('nav_menu'); ?> <?php if ($nav_menu_hooks != '') { ?> <div class="group" id="nav_menu_hooks"> <?= $nav_menu_hooks ?> @@ -249,7 +252,7 @@ </a> </div> - <?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous_refresh) { ?> + <?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::systemConf()->allow_anonymous_refresh) { ?> <div class="group"> <a id="actualize" class="btn" href="<?= _url('feed', 'actualize') ?>" title="<?= _t('gen.action.actualize') ?>"><?= _i('refresh') ?></a> </div> diff --git a/app/layout/simple.phtml b/app/layout/simple.phtml index b9f74cf59..f2e6bbd25 100644 --- a/app/layout/simple.phtml +++ b/app/layout/simple.phtml @@ -4,7 +4,7 @@ FreshRSS::preLayout(); ?> <!DOCTYPE html> -<html lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>"> +<html lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="initial-scale=1.0" /> @@ -18,7 +18,7 @@ <link rel="apple-touch-icon" href="<?= Minz_Url::display('/themes/icons/apple-touch-icon.png') ?>" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> - <meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::$system_conf->title ?>"> + <meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::systemConf()->title ?>"> <meta name="msapplication-TileColor" content="#FFF" /> <meta name="referrer" content="never" /> <meta name="robots" content="noindex,nofollow" /> @@ -31,11 +31,11 @@ <div class="header"> <div class="item title"> <a href="<?= _url('index', 'index') ?>"> - <?php if (FreshRSS_Context::$system_conf->logo_html == '') { ?> + <?php if (FreshRSS_Context::systemConf()->logo_html == '') { ?> <img class="logo" src="<?= _i('FreshRSS-logo', FreshRSS_Themes::ICON_URL) ?>" alt="FreshRSS" loading="lazy" /> <?php } else { - echo FreshRSS_Context::$system_conf->logo_html; + echo FreshRSS_Context::systemConf()->logo_html; } ?> </a> diff --git a/app/views/auth/index.phtml b/app/views/auth/index.phtml index 78762c19e..f82adaaae 100644 --- a/app/views/auth/index.phtml +++ b/app/views/auth/index.phtml @@ -15,15 +15,15 @@ <div class="form-group"> <label class="group-name" for="auth_type"><?= _t('admin.auth.type') ?></label> <div class="group-controls"> - <select id="auth_type" name="auth_type" required="required" data-leave-validation="<?= FreshRSS_Context::$system_conf->auth_type ?>"> - <?php if (!in_array(FreshRSS_Context::$system_conf->auth_type, ['form', 'http_auth', 'none'], true)) { ?> + <select id="auth_type" name="auth_type" required="required" data-leave-validation="<?= FreshRSS_Context::systemConf()->auth_type ?>"> + <?php if (!in_array(FreshRSS_Context::systemConf()->auth_type, ['form', 'http_auth', 'none'], true)) { ?> <option selected="selected"></option> <?php } ?> - <option value="form"<?= FreshRSS_Context::$system_conf->auth_type === 'form' ? ' selected="selected"' : '', + <option value="form"<?= FreshRSS_Context::systemConf()->auth_type === 'form' ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"' ?>><?= _t('admin.auth.form') ?></option> - <option value="http_auth"<?= FreshRSS_Context::$system_conf->auth_type === 'http_auth' ? ' selected="selected"' : '', + <option value="http_auth"<?= FreshRSS_Context::systemConf()->auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : '' ?>><?= _t('admin.auth.http') ?> (REMOTE_USER = '<?= httpAuthUser() ?>')</option> - <option value="none"<?= FreshRSS_Context::$system_conf->auth_type === 'none' ? ' selected="selected"' : '' ?>><?= _t('admin.auth.none') ?></option> + <option value="none"<?= FreshRSS_Context::systemConf()->auth_type === 'none' ? ' selected="selected"' : '' ?>><?= _t('admin.auth.none') ?></option> </select> </div> </div> @@ -32,9 +32,9 @@ <div class="group-controls"> <label class="checkbox" for="anon_access"> <input type="checkbox" name="anon_access" id="anon_access" value="1"<?= - FreshRSS_Context::$system_conf->allow_anonymous ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->allow_anonymous ?>"/> - <?= _t('admin.auth.allow_anonymous', FreshRSS_Context::$system_conf->default_user) ?> + FreshRSS_Context::systemConf()->allow_anonymous ? ' checked="checked"' : '', + FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::systemConf()->allow_anonymous ?>"/> + <?= _t('admin.auth.allow_anonymous', FreshRSS_Context::systemConf()->default_user) ?> </label> </div> </div> @@ -43,8 +43,8 @@ <div class="group-controls"> <label class="checkbox" for="anon_refresh"> <input type="checkbox" name="anon_refresh" id="anon_refresh" value="1"<?= - FreshRSS_Context::$system_conf->allow_anonymous_refresh ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->allow_anonymous_refresh ?>"/> + FreshRSS_Context::systemConf()->allow_anonymous_refresh ? ' checked="checked"' : '', + FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::systemConf()->allow_anonymous_refresh ?>"/> <?= _t('admin.auth.allow_anonymous_refresh') ?> </label> </div> @@ -54,8 +54,8 @@ <div class="group-controls"> <label class="checkbox" for="unsafe_autologin"> <input type="checkbox" name="unsafe_autologin" id="unsafe_autologin" value="1"<?= - FreshRSS_Context::$system_conf->unsafe_autologin_enabled ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->unsafe_autologin_enabled ?>"/> + FreshRSS_Context::systemConf()->unsafe_autologin_enabled ? ' checked="checked"' : '', + FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::systemConf()->unsafe_autologin_enabled ?>"/> <?= _t('admin.auth.unsafe_autologin') ?> <kbd><?= Minz_Url::display(array('c' => 'auth', 'a' => 'login', 'params' => array('u' => 'alice', 'p' => '1234')), 'html', true) ?></kbd> </label> @@ -66,8 +66,8 @@ <div class="group-controls"> <label class="checkbox" for="api_enabled"> <input type="checkbox" name="api_enabled" id="api_enabled" value="1"<?= - FreshRSS_Context::$system_conf->api_enabled ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsLogin() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->api_enabled ?>"/> + FreshRSS_Context::systemConf()->api_enabled ? ' checked="checked"' : '', + FreshRSS_Auth::accessNeedsLogin() ? '' : ' disabled="disabled"' ?> data-leave-validation="<?= FreshRSS_Context::systemConf()->api_enabled ?>"/> <?= _t('admin.auth.api_enabled') ?> </label> </div> diff --git a/app/views/category/update.phtml b/app/views/category/update.phtml index daf4523bb..2ef42d207 100644 --- a/app/views/category/update.phtml +++ b/app/views/category/update.phtml @@ -5,6 +5,6 @@ declare(strict_types=1); if (!Minz_Request::paramBoolean('ajax')) { $this->partial('aside_subscription'); } -if ($this->category) { +if ($this->category !== null) { $this->renderHelper('category/update'); } diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index 6004cec1b..86b8246e3 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -17,21 +17,21 @@ <div class="form-group"> <label class="group-name" for="ttl_default"><?= _t('conf.archiving.ttl') ?></label> <div class="group-controls"> - <select class="number" name="ttl_default" id="ttl_default" required="required" data-leave-validation="<?= FreshRSS_Context::$user_conf->ttl_default ?>"><?php + <select class="number" name="ttl_default" id="ttl_default" required="required" data-leave-validation="<?= FreshRSS_Context::userConf()->ttl_default ?>"><?php $found = false; foreach (array(1200 => '20min', 1500 => '25min', 1800 => '30min', 2700 => '45min', 3600 => '1h', 5400 => '1.5h', 7200 => '2h', 10800 => '3h', 14400 => '4h', 18800 => '5h', 21600 => '6h', 25200 => '7h', 28800 => '8h', 36000 => '10h', 43200 => '12h', 64800 => '18h', 86400 => '1d', 129600 => '1.5d', 172800 => '2d', 259200 => '3d', 345600 => '4d', 432000 => '5d', 518400 => '6d', 604800 => '1wk') as $v => $t) { - echo '<option value="' . $v . (FreshRSS_Context::$user_conf->ttl_default == $v ? '" selected="selected' : '') . '">' . $t . '</option>'; - if (FreshRSS_Context::$user_conf->ttl_default == $v) { + echo '<option value="' . $v . (FreshRSS_Context::userConf()->ttl_default == $v ? '" selected="selected' : '') . '">' . $t . '</option>'; + if (FreshRSS_Context::userConf()->ttl_default == $v) { $found = true; } } if (!$found) { - echo '<option value="' . intval(FreshRSS_Context::$user_conf->ttl_default) . '" selected="selected">' - . intval(FreshRSS_Context::$user_conf->ttl_default) . 's</option>'; + echo '<option value="' . intval(FreshRSS_Context::userConf()->ttl_default) . '" selected="selected">' + . intval(FreshRSS_Context::userConf()->ttl_default) . 's</option>'; } ?></select> (<?= _t('gen.short.by_default') ?>) </div> @@ -46,10 +46,10 @@ <div class="group-controls"> <label class="checkbox" for="enable_keep_max"> <input type="checkbox" name="enable_keep_max" id="enable_keep_max" value="1"<?= - empty(FreshRSS_Context::$user_conf->archiving['keep_max']) ? '' : ' checked="checked"' ?> - data-leave-validation="<?= empty(FreshRSS_Context::$user_conf->archiving['keep_max']) ? 0 : 1 ?>"/> + empty(FreshRSS_Context::userConf()->archiving['keep_max']) ? '' : ' checked="checked"' ?> + data-leave-validation="<?= empty(FreshRSS_Context::userConf()->archiving['keep_max']) ? 0 : 1 ?>"/> <?= _t('conf.archiving.keep_max') ?> - <?php $keepMax = empty(FreshRSS_Context::$user_conf->archiving['keep_max']) ? 200 : FreshRSS_Context::$user_conf->archiving['keep_max']; ?> + <?php $keepMax = empty(FreshRSS_Context::userConf()->archiving['keep_max']) ? 200 : FreshRSS_Context::userConf()->archiving['keep_max']; ?> <input type="number" id="keep_max" name="keep_max" min="0" value="<?= $keepMax ?>" data-leave-validation="<?= $keepMax ?>"/> </label> </div> @@ -59,18 +59,18 @@ <div class="group-controls"> <label class="checkbox" for="enable_keep_period"> <input type="checkbox" name="enable_keep_period" id="enable_keep_period" value="1"<?= - FreshRSS_Context::$user_conf->volatile['enable_keep_period'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->volatile['enable_keep_period'] ? 1 : 0 ?>"/> + FreshRSS_Context::userConf()->volatile['enable_keep_period'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->volatile['enable_keep_period'] ? 1 : 0 ?>"/> <?= _t('conf.archiving.keep_period') ?> - <input type="number" id="keep_period_count" name="keep_period_count" min="0" value="<?= FreshRSS_Context::$user_conf->volatile['keep_period_count'] ?>" - data-leave-validation="<?= FreshRSS_Context::$user_conf->volatile['keep_period_count'] ?>"/> - <select class="number" name="keep_period_unit" id="keep_period_unit" data-leave-validation="<?= FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ?>"> + <input type="number" id="keep_period_count" name="keep_period_count" min="0" value="<?= FreshRSS_Context::userConf()->volatile['keep_period_count'] ?>" + data-leave-validation="<?= FreshRSS_Context::userConf()->volatile['keep_period_count'] ?>"/> + <select class="number" name="keep_period_unit" id="keep_period_unit" data-leave-validation="<?= FreshRSS_Context::userConf()->volatile['keep_period_unit'] ?>"> <option></option> - <option value="P1Y" <?= 'P1Y' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.years') ?></option> - <option value="P1M" <?= 'P1M' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.months') ?></option> - <option value="P1W" <?= 'P1W' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.weeks') ?></option> - <option value="P1D" <?= 'P1D' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.days') ?></option> - <option value="PT1H" <?= 'PT1H' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.hours') ?></option> + <option value="P1Y" <?= 'P1Y' === FreshRSS_Context::userConf()->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.years') ?></option> + <option value="P1M" <?= 'P1M' === FreshRSS_Context::userConf()->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.months') ?></option> + <option value="P1W" <?= 'P1W' === FreshRSS_Context::userConf()->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.weeks') ?></option> + <option value="P1D" <?= 'P1D' === FreshRSS_Context::userConf()->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.days') ?></option> + <option value="PT1H" <?= 'PT1H' === FreshRSS_Context::userConf()->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.hours') ?></option> </select> </label> </div> @@ -81,8 +81,8 @@ <div class="group-controls"> <label class="checkbox" for="keep_favourites"> <input type="checkbox" name="keep_favourites" id="keep_favourites" value="1"<?= - FreshRSS_Context::$user_conf->archiving['keep_favourites'] !== false ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_favourites'] !== false ? 1 : 0 ?>"/> + FreshRSS_Context::userConf()->archiving['keep_favourites'] !== false ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->archiving['keep_favourites'] !== false ? 1 : 0 ?>"/> <?= _t('conf.archiving.keep_favourites') ?> </label> </div> @@ -92,8 +92,8 @@ <div class="group-controls"> <label class="checkbox" for="keep_labels"> <input type="checkbox" name="keep_labels" id="keep_labels" value="1"<?= - FreshRSS_Context::$user_conf->archiving['keep_labels'] !== false ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_labels'] !== false ? 1 : 0 ?>"/> + FreshRSS_Context::userConf()->archiving['keep_labels'] !== false ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->archiving['keep_labels'] !== false ? 1 : 0 ?>"/> <?= _t('conf.archiving.keep_labels') ?> </label> </div> @@ -103,8 +103,8 @@ <div class="group-controls"> <label class="checkbox" for="keep_unreads"> <input type="checkbox" name="keep_unreads" id="keep_unreads" value="1"<?= - FreshRSS_Context::$user_conf->archiving['keep_unreads'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_unreads'] ? 1 : 0 ?>"/> + FreshRSS_Context::userConf()->archiving['keep_unreads'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->archiving['keep_unreads'] ? 1 : 0 ?>"/> <?= _t('conf.archiving.keep_unreads') ?> </label> </div> @@ -114,8 +114,8 @@ <div class="group-controls"> <label for="keep_min_default"><?= _t('conf.archiving.keep_min_by_feed') ?> <input type="number" id="keep_min_default" name="keep_min_default" min="0" value="<?= - FreshRSS_Context::$user_conf->archiving['keep_min'] ?>" - data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_min'] ?>"> + FreshRSS_Context::userConf()->archiving['keep_min'] ?>" + data-leave-validation="<?= FreshRSS_Context::userConf()->archiving['keep_min'] ?>"> </label> </div> </div> diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index b48dcbf71..d373c0bb5 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -16,10 +16,10 @@ <div class="form-group"> <label class="group-name" for="language"><?= _t('conf.display.language') ?></label> <div class="group-controls"> - <select name="language" id="language" data-leave-validation="<?= FreshRSS_Context::$user_conf->language ?>"> + <select name="language" id="language" data-leave-validation="<?= FreshRSS_Context::userConf()->language ?>"> <?php $languages = Minz_Translate::availableLanguages(); ?> <?php foreach ($languages as $lang) { ?> - <option value="<?= $lang ?>"<?= FreshRSS_Context::$user_conf->language === $lang ? ' selected="selected"' : '' ?>><?= _t('gen.lang.' . $lang) ?></option> + <option value="<?= $lang ?>"<?= FreshRSS_Context::userConf()->language === $lang ? ' selected="selected"' : '' ?>><?= _t('gen.lang.' . $lang) ?></option> <?php } ?> </select> </div> @@ -28,15 +28,15 @@ <div class="form-group"> <label class="group-name" for="timezone"><?= _t('conf.display.timezone') ?></label> <div class="group-controls"> - <select name="timezone" id="timezone" data-leave-validation="<?= FreshRSS_Context::$user_conf->timezone ?>"> + <select name="timezone" id="timezone" data-leave-validation="<?= FreshRSS_Context::userConf()->timezone ?>"> <?php $timezones = array_merge([''], DateTimeZone::listIdentifiers()); - if (!in_array(FreshRSS_Context::$user_conf->timezone, $timezones, true)) { - FreshRSS_Context::$user_conf->timezone = ''; + if (!in_array(FreshRSS_Context::userConf()->timezone, $timezones, true)) { + FreshRSS_Context::userConf()->timezone = ''; } ?> <?php foreach ($timezones as $timezone): ?> - <option value="<?= $timezone ?>"<?= FreshRSS_Context::$user_conf->timezone === $timezone ? ' selected="selected"' : '' ?>> + <option value="<?= $timezone ?>"<?= FreshRSS_Context::userConf()->timezone === $timezone ? ' selected="selected"' : '' ?>> <?= $timezone == '' ? _t('gen.short.by_default') . ' (' . FreshRSS_Context::defaultTimeZone() . ')' : $timezone ?> </option> <?php endforeach; ?> @@ -49,16 +49,16 @@ <div class="group-controls"> <ul class="theme-preview-list"> <?php $slides = count($this->themes); $i = 1; $themeAvailable = false; ?> - <?php /** @var array{'id':string, 'deprecated':bool, 'author':string, 'name':string, 'description':string} $theme */ - foreach($this->themes as $theme) { ?> - <?php if (FreshRSS_Context::$user_conf->theme === $theme['id']) { + <?php + foreach ($this->themes as $theme) { ?> + <?php if (FreshRSS_Context::userConf()->theme === $theme['id']) { $checked = 'checked="checked"'; $themeAvailable = true; } else { $checked = ''; } ?> <input type="radio" name="theme" id="img-<?= $i ?>" <?= $checked ?> value="<?= $theme['id'] ?>" - data-leave-validation="<?= (FreshRSS_Context::$user_conf->theme === $theme['id']) ? 1 : 0 ?>" /> + data-leave-validation="<?= (FreshRSS_Context::userConf()->theme === $theme['id']) ? 1 : 0 ?>" /> <li class="preview-container"> <div class="preview"> <img src="<?= Minz_Url::display('/themes/' . $theme['id'] . '/thumbs/original.png') ?>" loading="lazy" /> @@ -98,7 +98,7 @@ <label for="img-<?= $i - 1 ?>" class="prev">‹</label> </div> <div class="properties alert-error"> - <div><?= _t('conf.display.theme_not_available', FreshRSS_Context::$user_conf->theme)?></div> + <div><?= _t('conf.display.theme_not_available', FreshRSS_Context::userConf()->theme)?></div> </div> </li> <?php }?> @@ -109,14 +109,14 @@ <div class="form-group"> <label class="group-name" for="darkMode"><?= _t('conf.display.darkMode') ?></label> <div class="group-controls"> - <select name="darkMode" id="darkMode" data-leave-validation="<?= FreshRSS_Context::$user_conf->darkMode ?>"> - <option value="no"<?= FreshRSS_Context::$user_conf->darkMode === 'no' ? ' selected' : '' ?>><?= _t('conf.display.darkMode.no') ?></option> - <option value="auto"<?= FreshRSS_Context::$user_conf->darkMode === 'auto' ? ' selected' : '' ?>><?= _t('conf.display.darkMode.auto') ?></option> + <select name="darkMode" id="darkMode" data-leave-validation="<?= FreshRSS_Context::userConf()->darkMode ?>"> + <option value="no"<?= FreshRSS_Context::userConf()->darkMode === 'no' ? ' selected' : '' ?>><?= _t('conf.display.darkMode.no') ?></option> + <option value="auto"<?= FreshRSS_Context::userConf()->darkMode === 'auto' ? ' selected' : '' ?>><?= _t('conf.display.darkMode.auto') ?></option> </select> </div> </div> - <?php $width = FreshRSS_Context::$user_conf->content_width; ?> + <?php $width = FreshRSS_Context::userConf()->content_width; ?> <div class="form-group"> <label class="group-name" for="content_width"><?= _t('conf.display.width.content') ?></label> <div class="group-controls"> @@ -137,7 +137,7 @@ </div> </div> - <?php $topline_website = FreshRSS_Context::$user_conf->topline_website; ?> + <?php $topline_website = FreshRSS_Context::userConf()->topline_website; ?> <div class="form-group"> <label class="group-name" for="topline_website"><?= _t('conf.display.website.label') ?></label> <div class="group-controls"> @@ -158,7 +158,7 @@ </div> </div> - <?php $topline_thumbnail = FreshRSS_Context::$user_conf->topline_thumbnail; ?> + <?php $topline_thumbnail = FreshRSS_Context::userConf()->topline_thumbnail; ?> <div class="form-group"> <label class="group-name" for="topline_thumbnail"><?= _t('conf.display.thumbnail.label') ?></label> <div class="group-controls"> @@ -201,50 +201,50 @@ <tr> <th><?= _t('conf.display.icon.top_line') ?></th> <td><input type="checkbox" name="topline_read" value="1"<?= - FreshRSS_Context::$user_conf->topline_read ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_read ?>" /></td> + FreshRSS_Context::userConf()->topline_read ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->topline_read ?>" /></td> <td><input type="checkbox" name="topline_favorite" value="1"<?= - FreshRSS_Context::$user_conf->topline_favorite ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_favorite ?>" /></td> + FreshRSS_Context::userConf()->topline_favorite ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->topline_favorite ?>" /></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" name="topline_summary" value="1"<?= - FreshRSS_Context::$user_conf->topline_summary ? 'checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_summary ?>" /></td> + FreshRSS_Context::userConf()->topline_summary ? 'checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->topline_summary ?>" /></td> <td><input type="checkbox" name="topline_display_authors" value="1"<?= - FreshRSS_Context::$user_conf->topline_display_authors ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_display_authors ?>" /></td> + FreshRSS_Context::userConf()->topline_display_authors ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->topline_display_authors ?>" /></td> <td><input type="checkbox" name="topline_date" value="1"<?= - FreshRSS_Context::$user_conf->topline_date ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_date ?>" /></td> - <td><input type="checkbox" name="topline_link" value="1"<?= FreshRSS_Context::$user_conf->topline_link ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_link ?>" /></td> + FreshRSS_Context::userConf()->topline_date ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->topline_date ?>" /></td> + <td><input type="checkbox" name="topline_link" value="1"<?= FreshRSS_Context::userConf()->topline_link ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->topline_link ?>" /></td> </tr><tr> <th><?= _t('conf.display.icon.bottom_line') ?></th> <td><input type="checkbox" name="bottomline_read" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_read ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_read ?>" /></td> + FreshRSS_Context::userConf()->bottomline_read ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_read ?>" /></td> <td><input type="checkbox" name="bottomline_favorite" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_favorite ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_favorite ?>" /></td> + FreshRSS_Context::userConf()->bottomline_favorite ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_favorite ?>" /></td> <td><input type="checkbox" name="bottomline_myLabels" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_myLabels ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_myLabels ?>" /></td> + FreshRSS_Context::userConf()->bottomline_myLabels ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_myLabels ?>" /></td> <td><input type="checkbox" name="bottomline_tags" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_tags ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_tags ?>" /></td> + FreshRSS_Context::userConf()->bottomline_tags ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_tags ?>" /></td> <td><input type="checkbox" name="bottomline_sharing" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_sharing ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_sharing ?>" /></td> + FreshRSS_Context::userConf()->bottomline_sharing ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_sharing ?>" /></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" name="bottomline_date" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_date ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_date ?>" /></td> + FreshRSS_Context::userConf()->bottomline_date ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_date ?>" /></td> <td><input type="checkbox" name="bottomline_link" value="1"<?= - FreshRSS_Context::$user_conf->bottomline_link ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_link ?>" /></td> + FreshRSS_Context::userConf()->bottomline_link ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->bottomline_link ?>" /></td> </tr> </tbody> </table> @@ -255,8 +255,8 @@ <label class="group-name" for="html5_notif_timeout"><?= _t('conf.display.notif_html5.timeout') ?></label> <div class="group-controls"> <input type="number" id="html5_notif_timeout" name="html5_notif_timeout" value="<?= - FreshRSS_Context::$user_conf->html5_notif_timeout ?>" - data-leave-validation="<?= FreshRSS_Context::$user_conf->html5_notif_timeout ?>" /> <?= _t('conf.display.notif_html5.seconds') ?> + FreshRSS_Context::userConf()->html5_notif_timeout ?>" + data-leave-validation="<?= FreshRSS_Context::userConf()->html5_notif_timeout ?>" /> <?= _t('conf.display.notif_html5.seconds') ?> </div> </div> @@ -264,8 +264,8 @@ <div class="group-controls"> <label class="checkbox" for="show_nav_buttons"> <input type="checkbox" name="show_nav_buttons" id="show_nav_buttons" value="1"<?= - FreshRSS_Context::$user_conf->show_nav_buttons ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->show_nav_buttons ?>" /> + FreshRSS_Context::userConf()->show_nav_buttons ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->show_nav_buttons ?>" /> <?= _t('conf.display.show_nav_buttons') ?> </label> </div> diff --git a/app/views/configure/integration.phtml b/app/views/configure/integration.phtml index fc3cc2a32..973ee128f 100644 --- a/app/views/configure/integration.phtml +++ b/app/views/configure/integration.phtml @@ -57,8 +57,11 @@ </template> <?php - foreach (FreshRSS_Context::$user_conf->sharing as $key => $share_options) { + foreach (FreshRSS_Context::userConf()->sharing as $key => $share_options) { $share = FreshRSS_Share::get($share_options['type']); + if ($share === null) { + continue; + } $share->update($share_options); ?> <formgroup class="group-share dragbox" id="group-share-<?= $key ?>"> diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index 94cf51977..17fdbe7c3 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -17,10 +17,10 @@ <div class="form-group"> <label class="group-name" for="view_mode"><?= _t('conf.reading.view.default') ?></label> <div class="group-controls"> - <select name="view_mode" id="view_mode" data-leave-validation="<?= FreshRSS_Context::$user_conf->view_mode ?>"> - <option value="normal"<?= FreshRSS_Context::$user_conf->view_mode === 'normal' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.normal') ?></option> - <option value="reader"<?= FreshRSS_Context::$user_conf->view_mode === 'reader' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.reader') ?></option> - <option value="global"<?= FreshRSS_Context::$user_conf->view_mode === 'global' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.global') ?></option> + <select name="view_mode" id="view_mode" data-leave-validation="<?= FreshRSS_Context::userConf()->view_mode ?>"> + <option value="normal"<?= FreshRSS_Context::userConf()->view_mode === 'normal' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.normal') ?></option> + <option value="reader"<?= FreshRSS_Context::userConf()->view_mode === 'reader' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.reader') ?></option> + <option value="global"<?= FreshRSS_Context::userConf()->view_mode === 'global' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.global') ?></option> </select> </div> </div> @@ -28,10 +28,10 @@ <div class="form-group"> <label class="group-name" for="default_view"><?= _t('conf.reading.show') ?></label> <div class="group-controls"> - <select name="default_view" id="default_view" data-leave-validation="<?= FreshRSS_Context::$user_conf->default_view ?>"> - <option value="adaptive"<?= FreshRSS_Context::$user_conf->default_view === 'adaptive' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.adaptive') ?></option> - <option value="all"<?= FreshRSS_Context::$user_conf->default_view === 'all' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.all_articles') ?></option> - <option value="unread"<?= FreshRSS_Context::$user_conf->default_view === 'unread' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.unread') ?></option> + <select name="default_view" id="default_view" data-leave-validation="<?= FreshRSS_Context::userConf()->default_view ?>"> + <option value="adaptive"<?= FreshRSS_Context::userConf()->default_view === 'adaptive' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.adaptive') ?></option> + <option value="all"<?= FreshRSS_Context::userConf()->default_view === 'all' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.all_articles') ?></option> + <option value="unread"<?= FreshRSS_Context::userConf()->default_view === 'unread' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.unread') ?></option> </select> </div> </div> @@ -40,8 +40,8 @@ <label class="group-name" for="posts_per_page"><?= _t('conf.reading.articles_per_page') ?></label> <div class="group-controls"> <input type="number" id="posts_per_page" name="posts_per_page" value="<?= - FreshRSS_Context::$user_conf->posts_per_page ?>" min="5" max="500" - data-leave-validation="<?= FreshRSS_Context::$user_conf->posts_per_page ?>"/> + FreshRSS_Context::userConf()->posts_per_page ?>" min="5" max="500" + data-leave-validation="<?= FreshRSS_Context::userConf()->posts_per_page ?>"/> <p class="help"><?= _i('help') ?> <?= _t('conf.reading.number_divided_when_reader') ?></p> </div> </div> @@ -50,8 +50,8 @@ <div class="group-controls"> <label class="checkbox" for="auto_load_more"> <input type="checkbox" name="auto_load_more" id="auto_load_more" value="1"<?= - FreshRSS_Context::$user_conf->auto_load_more ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->auto_load_more ?>"/> + FreshRSS_Context::userConf()->auto_load_more ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->auto_load_more ?>"/> <?= _t('conf.reading.auto_load_more') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> @@ -61,9 +61,9 @@ <div class="form-group"> <label class="group-name" for="sort_order"><?= _t('conf.reading.sort') ?></label> <div class="group-controls"> - <select name="sort_order" id="sort_order" data-leave-validation="<?= FreshRSS_Context::$user_conf->sort_order ?>"> - <option value="DESC"<?= FreshRSS_Context::$user_conf->sort_order === 'DESC' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.sort.newer_first') ?></option> - <option value="ASC"<?= FreshRSS_Context::$user_conf->sort_order === 'ASC' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.sort.older_first') ?></option> + <select name="sort_order" id="sort_order" data-leave-validation="<?= FreshRSS_Context::userConf()->sort_order ?>"> + <option value="DESC"<?= FreshRSS_Context::userConf()->sort_order === 'DESC' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.sort.newer_first') ?></option> + <option value="ASC"<?= FreshRSS_Context::userConf()->sort_order === 'ASC' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.sort.older_first') ?></option> </select> </div> </div> @@ -75,14 +75,14 @@ <div class="form-group"> <label class="group-name" for="display_categories"><?= _t('conf.reading.display_categories_unfolded') ?></label> <div class="group-controls"> - <select name="display_categories" id="display_categories" data-leave-validation="<?= FreshRSS_Context::$user_conf->display_categories ?>"> - <option value="active"<?= FreshRSS_Context::$user_conf->display_categories === 'active' ? ' selected="selected"' : '' ?>><?= + <select name="display_categories" id="display_categories" data-leave-validation="<?= FreshRSS_Context::userConf()->display_categories ?>"> + <option value="active"<?= FreshRSS_Context::userConf()->display_categories === 'active' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.active_category') ?></option> - <option value="remember"<?= FreshRSS_Context::$user_conf->display_categories === 'remember' ? ' selected="selected"' : '' ?>><?= + <option value="remember"<?= FreshRSS_Context::userConf()->display_categories === 'remember' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.remember_categories') ?></option> - <option value="all"<?= FreshRSS_Context::$user_conf->display_categories === 'all' ? ' selected="selected"' : '' ?>><?= + <option value="all"<?= FreshRSS_Context::userConf()->display_categories === 'all' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.all_categories') ?></option> - <option value="none"<?= FreshRSS_Context::$user_conf->display_categories === 'none' ? ' selected="selected"' : '' ?>><?= + <option value="none"<?= FreshRSS_Context::userConf()->display_categories === 'none' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.no_category') ?></option> </select> </div> @@ -92,8 +92,8 @@ <div class="group-controls"> <label class="checkbox" for="show_fav_unread"> <input type="checkbox" name="show_fav_unread" id="show_fav_unread" value="1"<?= - FreshRSS_Context::$user_conf->show_fav_unread ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->show_fav_unread ?>"/> + FreshRSS_Context::userConf()->show_fav_unread ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->show_fav_unread ?>"/> <?= _t('conf.reading.always_show_favorites') ?> <p class="help"><?= _i('help') ?> <?= _t('conf.reading.show_fav_unread_help') ?></p> </label> @@ -104,8 +104,8 @@ <div class="group-controls"> <label class="checkbox" for="hide_read_feeds"> <input type="checkbox" name="hide_read_feeds" id="hide_read_feeds" value="1"<?= - FreshRSS_Context::$user_conf->hide_read_feeds ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->hide_read_feeds ?>"/> + FreshRSS_Context::userConf()->hide_read_feeds ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->hide_read_feeds ?>"/> <?= _t('conf.reading.hide_read_feeds') ?> </label> </div> @@ -117,32 +117,32 @@ <div class="form-group"> <label class="group-name" for="show_feed_name"><?= _t('conf.reading.article.feed_title') ?></label> <div class="group-controls"> - <select name="show_feed_name" id="show_feed_name" data-leave-validation="<?= FreshRSS_Context::$user_conf->show_feed_name ?>"> - <option value="0"<?= FreshRSS_Context::$user_conf->show_feed_name === '0' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.feed_name.none') ?></option> - <option value="t"<?= FreshRSS_Context::$user_conf->show_feed_name === 't' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.feed_name.above_title') ?></option> - <option value="a"<?= FreshRSS_Context::$user_conf->show_feed_name === 'a' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.feed_name.with_authors') ?></option> + <select name="show_feed_name" id="show_feed_name" data-leave-validation="<?= FreshRSS_Context::userConf()->show_feed_name ?>"> + <option value="0"<?= FreshRSS_Context::userConf()->show_feed_name === '0' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.feed_name.none') ?></option> + <option value="t"<?= FreshRSS_Context::userConf()->show_feed_name === 't' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.feed_name.above_title') ?></option> + <option value="a"<?= FreshRSS_Context::userConf()->show_feed_name === 'a' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.feed_name.with_authors') ?></option> </select> </div> </div> <div class="form-group"> <label class="group-name" for="show_author_date"><?= _t('conf.reading.article.authors_date') ?></label> <div class="group-controls"> - <select name="show_author_date" id="show_author_date" data-leave-validation="<?= FreshRSS_Context::$user_conf->show_author_date ?>"> - <option value="0" <?= FreshRSS_Context::$user_conf->show_author_date === '0' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.none') ?></option> - <option value="h" <?= FreshRSS_Context::$user_conf->show_author_date === 'h' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.header') ?></option> - <option value="f" <?= FreshRSS_Context::$user_conf->show_author_date === 'f' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.footer') ?></option> - <option value="b" <?= FreshRSS_Context::$user_conf->show_author_date === 'b' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.both') ?></option> + <select name="show_author_date" id="show_author_date" data-leave-validation="<?= FreshRSS_Context::userConf()->show_author_date ?>"> + <option value="0" <?= FreshRSS_Context::userConf()->show_author_date === '0' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.none') ?></option> + <option value="h" <?= FreshRSS_Context::userConf()->show_author_date === 'h' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.header') ?></option> + <option value="f" <?= FreshRSS_Context::userConf()->show_author_date === 'f' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.footer') ?></option> + <option value="b" <?= FreshRSS_Context::userConf()->show_author_date === 'b' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.article.authors_date.both') ?></option> </select> </div> </div> <div class="form-group"> <label class="group-name" for="show_tags"><?= _t('conf.reading.article.tags') ?></label> <div class="group-controls"> - <select class="select-input-changer" name="show_tags" id="show_tags" data-name="show_tags_max" data-leave-validation="<?= FreshRSS_Context::$user_conf->show_tags ?>"> - <option value="0" <?= FreshRSS_Context::$user_conf->show_tags === '0' ? ' selected="selected"' : '' ?> data-input-visible="false"><?= _t('conf.reading.article.tags.none') ?></option> - <option value="h" <?= FreshRSS_Context::$user_conf->show_tags === 'h' ? ' selected="selected"' : '' ?> data-input-visible="true"><?= _t('conf.reading.article.tags.header') ?></option> - <option value="f" <?= FreshRSS_Context::$user_conf->show_tags === 'f' ? ' selected="selected"' : '' ?> data-input-visible="true"><?= _t('conf.reading.article.tags.footer') ?></option> - <option value="b" <?= FreshRSS_Context::$user_conf->show_tags === 'b' ? ' selected="selected"' : '' ?> data-input-visible="true"><?= _t('conf.reading.article.tags.both') ?></option> + <select class="select-input-changer" name="show_tags" id="show_tags" data-name="show_tags_max" data-leave-validation="<?= FreshRSS_Context::userConf()->show_tags ?>"> + <option value="0" <?= FreshRSS_Context::userConf()->show_tags === '0' ? ' selected="selected"' : '' ?> data-input-visible="false"><?= _t('conf.reading.article.tags.none') ?></option> + <option value="h" <?= FreshRSS_Context::userConf()->show_tags === 'h' ? ' selected="selected"' : '' ?> data-input-visible="true"><?= _t('conf.reading.article.tags.header') ?></option> + <option value="f" <?= FreshRSS_Context::userConf()->show_tags === 'f' ? ' selected="selected"' : '' ?> data-input-visible="true"><?= _t('conf.reading.article.tags.footer') ?></option> + <option value="b" <?= FreshRSS_Context::userConf()->show_tags === 'b' ? ' selected="selected"' : '' ?> data-input-visible="true"><?= _t('conf.reading.article.tags.both') ?></option> </select> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </div> @@ -150,7 +150,7 @@ <div class="form-group" id="show_tags_max-block"> <label class="group-name" for="show_tags_max"><?= _t('conf.reading.article.tags_max') ?></label> <div class="group-controls"> - <input type="number" id="show_tags_max" name="show_tags_max" value="<?= FreshRSS_Context::$user_conf->show_tags_max ?>" min="0" data-leave-validation="<?= FreshRSS_Context::$user_conf->show_tags_max ?>" data-number="2" /> + <input type="number" id="show_tags_max" name="show_tags_max" value="<?= FreshRSS_Context::userConf()->show_tags_max ?>" min="0" data-leave-validation="<?= FreshRSS_Context::userConf()->show_tags_max ?>" data-number="2" /> <p class="help"><?= _i('help') ?> <?= _t('conf.reading.article.tags_max.help') ?></p> </div> </div> @@ -162,8 +162,8 @@ <div class="group-controls"> <label class="checkbox" for="display_posts"> <input type="checkbox" name="display_posts" id="display_posts" value="1"<?= - FreshRSS_Context::$user_conf->display_posts ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->display_posts ?>"/> + FreshRSS_Context::userConf()->display_posts ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->display_posts ?>"/> <?= _t('conf.reading.display_articles_unfolded') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> @@ -174,8 +174,8 @@ <div class="group-controls"> <label class="checkbox" for="sticky_post"> <input type="checkbox" name="sticky_post" id="sticky_post" value="1"<?= - FreshRSS_Context::$user_conf->sticky_post ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->sticky_post ?>"/> + FreshRSS_Context::userConf()->sticky_post ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->sticky_post ?>"/> <?= _t('conf.reading.sticky_post') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> @@ -186,8 +186,8 @@ <div class="group-controls"> <label class="checkbox" for="sides_close_article"> <input type="checkbox" name="sides_close_article" id="sides_close_article" value="1"<?= - FreshRSS_Context::$user_conf->sides_close_article ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->sides_close_article ?>"/> + FreshRSS_Context::userConf()->sides_close_article ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->sides_close_article ?>"/> <?= _t('conf.reading.sides_close_article') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> @@ -201,8 +201,8 @@ <div class="group-controls"> <label class="checkbox" for="auto_remove_article"> <input type="checkbox" name="auto_remove_article" id="auto_remove_article" value="1"<?= - FreshRSS_Context::$user_conf->auto_remove_article ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->auto_remove_article ?>"/> + FreshRSS_Context::userConf()->auto_remove_article ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->auto_remove_article ?>"/> <?= _t('conf.reading.auto_remove_article') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> @@ -213,8 +213,8 @@ <div class="group-controls"> <label class="checkbox" for="reading_confirm"> <input type="checkbox" name="reading_confirm" id="reading_confirm" value="1"<?= - FreshRSS_Context::$user_conf->reading_confirm ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->reading_confirm ?>"/> + FreshRSS_Context::userConf()->reading_confirm ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->reading_confirm ?>"/> <?= _t('conf.reading.confirm_enabled') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> @@ -226,8 +226,8 @@ <div class="group-controls"> <label class="checkbox" for="onread_jump_next"> <input type="checkbox" name="onread_jump_next" id="onread_jump_next" value="1"<?= - FreshRSS_Context::$user_conf->onread_jump_next ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->onread_jump_next ?>"/> + FreshRSS_Context::userConf()->onread_jump_next ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->onread_jump_next ?>"/> <?= _t('conf.reading.jump_next') ?> </label> </div> @@ -237,8 +237,8 @@ <div class="group-controls"> <label class="checkbox" for="mark_updated_article_unread"> <input type="checkbox" name="mark_updated_article_unread" id="mark_updated_article_unread" value="1"<?= - FreshRSS_Context::$user_conf->mark_updated_article_unread ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_updated_article_unread ?>"/> + FreshRSS_Context::userConf()->mark_updated_article_unread ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_updated_article_unread ?>"/> <?= _t('conf.reading.mark_updated_article_unread') ?> </label> </div> @@ -249,8 +249,8 @@ <div class="group-controls"> <label class="checkbox" for="check_open_article"> <input type="checkbox" name="mark_open_article" id="check_open_article" value="1"<?= - FreshRSS_Context::$user_conf->mark_when['article'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['article'] ?>"/> + FreshRSS_Context::userConf()->mark_when['article'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_when['article'] ?>"/> <?= _t('conf.reading.read.article_viewed') ?> </label> </div> @@ -261,8 +261,8 @@ <div class="group-controls"> <label class="checkbox" for="check_open_site"> <input type="checkbox" name="mark_open_site" id="check_open_site" value="1"<?= - FreshRSS_Context::$user_conf->mark_when['site'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['site'] ?>"/> + FreshRSS_Context::userConf()->mark_when['site'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_when['site'] ?>"/> <?= _t('conf.reading.read.article_open_on_website') ?> </label> </div> @@ -273,8 +273,8 @@ <div class="group-controls"> <label class="checkbox" for="check_scroll"> <input type="checkbox" name="mark_scroll" id="check_scroll" value="1"<?= - FreshRSS_Context::$user_conf->mark_when['scroll'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['scroll'] ?>"/> + FreshRSS_Context::userConf()->mark_when['scroll'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_when['scroll'] ?>"/> <?= _t('conf.reading.read.scroll') ?> </label> </div> @@ -285,8 +285,8 @@ <div class="group-controls"> <label class="checkbox" for="check_focus"> <input type="checkbox" name="mark_focus" id="check_focus" value="1"<?= - FreshRSS_Context::$user_conf->mark_when['focus'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['focus'] ?>" /> + FreshRSS_Context::userConf()->mark_when['focus'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_when['focus'] ?>" /> <?= _t('conf.reading.read.focus') ?> </label> </div> @@ -297,10 +297,10 @@ <div class="group-controls"> <label class="checkbox" for="enable_read_when_same_title_in_feed"> <input type="checkbox" name="enable_read_when_same_title_in_feed" id="enable_read_when_same_title_in_feed" value="1"<?= - empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? '' : ' checked="checked"' ?> - data-leave-validation="<?= empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? 0 : 1 ?>"/> + empty(FreshRSS_Context::userConf()->mark_when['same_title_in_feed']) ? '' : ' checked="checked"' ?> + data-leave-validation="<?= empty(FreshRSS_Context::userConf()->mark_when['same_title_in_feed']) ? 0 : 1 ?>"/> <?= _t('conf.reading.read.when_same_title') ?> - <?php $read_when_same_title_in_feed = empty(FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']) ? 25 : FreshRSS_Context::$user_conf->mark_when['same_title_in_feed']; ?> + <?php $read_when_same_title_in_feed = empty(FreshRSS_Context::userConf()->mark_when['same_title_in_feed']) ? 25 : FreshRSS_Context::userConf()->mark_when['same_title_in_feed']; ?> <input type="number" id="read_when_same_title_in_feed" name="read_when_same_title_in_feed" min="0" value="<?= $read_when_same_title_in_feed ?>" data-leave-validation="<?= $read_when_same_title_in_feed ?>" /> </label> @@ -312,8 +312,8 @@ <div class="group-controls"> <label class="checkbox" for="mark_upon_reception"> <input type="checkbox" name="mark_upon_reception" id="mark_upon_reception" value="1"<?= - FreshRSS_Context::$user_conf->mark_when['reception'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['reception'] ?>"/> + FreshRSS_Context::userConf()->mark_when['reception'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_when['reception'] ?>"/> <?= _t('conf.reading.read.upon_reception') ?> </label> </div> @@ -324,8 +324,8 @@ <div class="group-controls"> <label class="checkbox" for="read_upon_gone"> <input type="checkbox" name="read_upon_gone" id="read_upon_gone" value="1"<?= - FreshRSS_Context::$user_conf->mark_when['gone'] ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['gone'] ?>"/> + FreshRSS_Context::userConf()->mark_when['gone'] ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->mark_when['gone'] ?>"/> <?= _t('conf.reading.read.upon_gone') ?> </label> </div> @@ -335,10 +335,10 @@ <div class="group-controls"> <label class="checkbox" for="keep_max_n_unread"> <input type="checkbox" name="enable_keep_max_n_unread" id="enable_keep_max_n_unread" value="1"<?= - empty(FreshRSS_Context::$user_conf->mark_when['max_n_unread']) ? '' : ' checked="checked"' ?> - data-leave-validation="<?= empty(FreshRSS_Context::$user_conf->mark_when['max_n_unread']) ? 0 : 1 ?>"/> + empty(FreshRSS_Context::userConf()->mark_when['max_n_unread']) ? '' : ' checked="checked"' ?> + data-leave-validation="<?= empty(FreshRSS_Context::userConf()->mark_when['max_n_unread']) ? 0 : 1 ?>"/> <?= _t('conf.reading.read.keep_max_n_unread') ?> - <?php $keep_max_n_unread = empty(FreshRSS_Context::$user_conf->mark_when['max_n_unread']) ? 1000 : FreshRSS_Context::$user_conf->mark_when['max_n_unread']; ?> + <?php $keep_max_n_unread = empty(FreshRSS_Context::userConf()->mark_when['max_n_unread']) ? 1000 : FreshRSS_Context::userConf()->mark_when['max_n_unread']; ?> <input type="number" id="keep_max_n_unread" name="keep_max_n_unread" min="0" value="<?= $keep_max_n_unread ?>" data-leave-validation="<?= $keep_max_n_unread ?>" /> </label> </div> @@ -351,7 +351,7 @@ <label class="group-name" for="filteractions_read"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <textarea name="filteractions_read" id="filteractions_read" class="w100"><?php - foreach (FreshRSS_Context::$user_conf->filtersAction('read') as $filterRead) { + foreach (FreshRSS_Context::userConf()->filtersAction('read') as $filterRead) { echo $filterRead->getRawInput(), PHP_EOL; } ?></textarea> @@ -366,8 +366,8 @@ <div class="group-controls"> <label class="checkbox" for="lazyload"> <input type="checkbox" name="lazyload" id="lazyload" value="1"<?= - FreshRSS_Context::$user_conf->lazyload ? ' checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$user_conf->lazyload ?>"/> + FreshRSS_Context::userConf()->lazyload ? ' checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::userConf()->lazyload ?>"/> <?= _t('conf.reading.img_with_lazyload') ?> <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index e578938cd..0d7ee4cb7 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -16,7 +16,7 @@ <?php } ?> </datalist> - <?php $s = FreshRSS_Context::$user_conf->shortcuts; ?> + <?php $s = FreshRSS_Context::userConf()->shortcuts; ?> <?php if ([] !== $nonStandard = getNonStandardShortcuts($s)): ?> <p class="alert alert-error"> diff --git a/app/views/configure/system.phtml b/app/views/configure/system.phtml index d19698932..09d1fe188 100644 --- a/app/views/configure/system.phtml +++ b/app/views/configure/system.phtml @@ -16,16 +16,16 @@ <div class="form-group"> <label class="group-name" for="instance-name"><?= _t('admin.system.instance-name') ?></label> <div class="group-controls"> - <input type="text" id="instance-name" name="instance-name" value="<?= FreshRSS_Context::$system_conf->title ?>" - data-leave-validation="<?= FreshRSS_Context::$system_conf->title ?>"/> + <input type="text" id="instance-name" name="instance-name" value="<?= FreshRSS_Context::systemConf()->title ?>" + data-leave-validation="<?= FreshRSS_Context::systemConf()->title ?>"/> </div> </div> <div class="form-group"> <label class="group-name" for="base-url"><?= _t('admin.system.base-url') ?></label> <div class="group-controls"> - <input type="text" id="base-url" name="base-url" value="<?= FreshRSS_Context::$system_conf->base_url ?>" - data-leave-validation="<?= FreshRSS_Context::$system_conf->base_url ?>" disabled="disabled" /> + <input type="text" id="base-url" name="base-url" value="<?= FreshRSS_Context::systemConf()->base_url ?>" + data-leave-validation="<?= FreshRSS_Context::systemConf()->base_url ?>" disabled="disabled" /> <p class="help"><?= _i('help') ?> <?= _t('admin.system.base-url.recommendation', dirname(Minz_Request::guessBaseUrl())) ?></p> <p class="help"><?= _i('help') ?> <?= _t('admin.system.sensitive-parameter') ?></p> </div> @@ -35,7 +35,7 @@ <label class="group-name" for="websub"><?= _t('sub.feed.websub') ?></label> <div class="group-controls"> <input type="checkbox" id="websub" name="websub" disabled="disabled" <?= - FreshRSS_Context::$system_conf->pubsubhubbub_enabled && Minz_Request::serverIsPublic(FreshRSS_Context::$system_conf->base_url) ? 'checked="checked"' : '' ?> /> + FreshRSS_Context::systemConf()->pubsubhubbub_enabled && Minz_Request::serverIsPublic(FreshRSS_Context::systemConf()->base_url) ? 'checked="checked"' : '' ?> /> <p class="help"><?= _i('help') ?> <?= _t('admin.system.websub.help') ?></p> <p class="help"><?= _i('help') ?> <?= _t('admin.system.sensitive-parameter') ?></p> </div> @@ -44,32 +44,32 @@ <div class="form-group"> <label class="group-name" for="auto-update-url"><?= _t('admin.system.auto-update-url') ?></label> <div class="group-controls"> - <input type="text" id="auto-update-url" name="auto-update-url" value="<?= FreshRSS_Context::$system_conf->auto_update_url ?>" - data-leave-validation="<?= FreshRSS_Context::$system_conf->auto_update_url ?>"/> + <input type="text" id="auto-update-url" name="auto-update-url" value="<?= FreshRSS_Context::systemConf()->auto_update_url ?>" + data-leave-validation="<?= FreshRSS_Context::systemConf()->auto_update_url ?>"/> </div> </div> <div class="form-group"> <label class="group-name" for="max-feeds"><?= _t('admin.system.max-feeds') ?></label> <div class="group-controls"> - <input type="number" id="max-feeds" name="max-feeds" value="<?= FreshRSS_Context::$system_conf->limits['max_feeds'] ?>" min="1" - data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['max_feeds'] ?>"/> + <input type="number" id="max-feeds" name="max-feeds" value="<?= FreshRSS_Context::systemConf()->limits['max_feeds'] ?>" min="1" + data-leave-validation="<?= FreshRSS_Context::systemConf()->limits['max_feeds'] ?>"/> </div> </div> <div class="form-group"> <label class="group-name" for="max-categories"><?= _t('admin.system.max-categories') ?></label> <div class="group-controls"> - <input type="number" id="max-categories" name="max-categories" value="<?= FreshRSS_Context::$system_conf->limits['max_categories'] ?>" min="1" - data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['max_categories'] ?>"/> + <input type="number" id="max-categories" name="max-categories" value="<?= FreshRSS_Context::systemConf()->limits['max_categories'] ?>" min="1" + data-leave-validation="<?= FreshRSS_Context::systemConf()->limits['max_categories'] ?>"/> </div> </div> <div class="form-group"> <label class="group-name" for="cookie-duration"><?= _t('admin.system.cookie-duration.number') ?></label> <div class="group-controls"> - <input type="number" id="cookie-duration" name="cookie-duration" value="<?= FreshRSS_Context::$system_conf->limits['cookie_duration'] ?>" min="0" - data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['cookie_duration'] ?>"/> + <input type="number" id="cookie-duration" name="cookie-duration" value="<?= FreshRSS_Context::systemConf()->limits['cookie_duration'] ?>" min="0" + data-leave-validation="<?= FreshRSS_Context::systemConf()->limits['cookie_duration'] ?>"/> <p class="help"><?= _i('help') ?> <?= _t('admin.system.cookie-duration.help') ?></p> </div> </div> @@ -80,9 +80,9 @@ <label class="group-name" for="max-registrations-select"><?= _t('admin.system.registration.select.label') ?></label> <div class="group-controls"> <select class="select-input-changer" name="" data-name="max-registrations"> - <option value="1" <?= FreshRSS_Context::$system_conf->limits['max_registrations'] == 1 ? 'selected = "selected"' : ''; ?> data-input-visible="false"><?= _t('admin.system.registration.select.option.noform') ?></option> - <option value="0" <?= FreshRSS_Context::$system_conf->limits['max_registrations'] == 0 ? 'selected = "selected"' : ''; ?> data-input-visible="false"><?= _t('admin.system.registration.select.option.nolimit') ?></option> - <option value="2" <?= FreshRSS_Context::$system_conf->limits['max_registrations'] > 1 ? 'selected = "selected"' : ''; ?> data-input-visible="true"><?= _t('admin.system.registration.select.option.setaccountsnumber') ?></option> + <option value="1" <?= FreshRSS_Context::systemConf()->limits['max_registrations'] == 1 ? 'selected = "selected"' : ''; ?> data-input-visible="false"><?= _t('admin.system.registration.select.option.noform') ?></option> + <option value="0" <?= FreshRSS_Context::systemConf()->limits['max_registrations'] == 0 ? 'selected = "selected"' : ''; ?> data-input-visible="false"><?= _t('admin.system.registration.select.option.nolimit') ?></option> + <option value="2" <?= FreshRSS_Context::systemConf()->limits['max_registrations'] > 1 ? 'selected = "selected"' : ''; ?> data-input-visible="true"><?= _t('admin.system.registration.select.option.setaccountsnumber') ?></option> </select> </div> </div> @@ -91,8 +91,8 @@ <label class="group-name" for="max-registrations-input"><?= _t('admin.system.registration.number') ?></label> <div class="group-controls"> <?php $number = count(listUsers()); ?> - <input type="number" id="max-registrations-input" name="" value="<?= FreshRSS_Context::$system_conf->limits['max_registrations'] > 1 ? FreshRSS_Context::$system_conf->limits['max_registrations'] : $number + 1; ?>" min="2" - data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['max_registrations'] > 1 ? FreshRSS_Context::$system_conf->limits['max_registrations'] : $number + 1; ?>" data-number="<?= $number ?>"/> + <input type="number" id="max-registrations-input" name="" value="<?= FreshRSS_Context::systemConf()->limits['max_registrations'] > 1 ? FreshRSS_Context::systemConf()->limits['max_registrations'] : $number + 1; ?>" min="2" + data-leave-validation="<?= FreshRSS_Context::systemConf()->limits['max_registrations'] > 1 ? FreshRSS_Context::systemConf()->limits['max_registrations'] : $number + 1; ?>" data-number="<?= $number ?>"/> <span id="max-registrations-status-disabled">(= <?= _t('admin.system.registration.status.disabled') ?>)</span><span id="max-registrations-status-enabled">(= <?= _t('admin.system.registration.status.enabled') ?>)</span> </div> </div> @@ -124,8 +124,8 @@ name="force-email-validation" id="force-email-validation" value="1" - <?= FreshRSS_Context::$system_conf->force_email_validation ? 'checked="checked"' : '' ?> - data-leave-validation="<?= FreshRSS_Context::$system_conf->force_email_validation ?>" + <?= FreshRSS_Context::systemConf()->force_email_validation ? 'checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::systemConf()->force_email_validation ?>" /> <?= _t('admin.system.force_email_validation') ?> </label> diff --git a/app/views/feed/add.phtml b/app/views/feed/add.phtml index c51289f56..da3bcf844 100644 --- a/app/views/feed/add.phtml +++ b/app/views/feed/add.phtml @@ -1,7 +1,7 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ - if ($this->feed) { + if ($this->feed !== null) { ?> <main class="post"> <h1><?= _t('sub.feed.add') ?></h1> diff --git a/app/views/feed/contentSelectorPreview.phtml b/app/views/feed/contentSelectorPreview.phtml index a5afb10f4..a93e88783 100644 --- a/app/views/feed/contentSelectorPreview.phtml +++ b/app/views/feed/contentSelectorPreview.phtml @@ -4,7 +4,7 @@ FreshRSS::preLayout(); ?> <!DOCTYPE html> -<html class="preview_background" lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>"> +<html class="preview_background" lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>"> <head> <?= FreshRSS_View::headStyle() ?> <script src="<?= Minz_Url::display('/scripts/preview.js?' . @filemtime(PUBLIC_PATH . '/scripts/preview.js')) ?>"></script> diff --git a/app/views/helpers/category/update.phtml b/app/views/helpers/category/update.phtml index 68132ad27..36c0abfe8 100644 --- a/app/views/helpers/category/update.phtml +++ b/app/views/helpers/category/update.phtml @@ -1,6 +1,9 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ + if ($this->category === null) { + throw new FreshRSS_Context_Exception('Category not initialised!'); + } ?> <div class="post"> <h2> @@ -28,7 +31,7 @@ <div class="form-group"> <label class="group-name" for="position"><?= _t('sub.category.position') ?></label> <div class="group-controls"> - <input type="number" name="position" id="position" min="1" value="<?= $this->category->attributes('position') ?>" /> + <input type="number" name="position" id="position" min="1" value="<?= $this->category->attributeInt('position') ?>" /> <p class="help"><?= _i('help') ?> <?= _t('sub.category.position_help') ?></p> </div> </div> @@ -46,7 +49,7 @@ <label class="group-name" for="opml_url"><?= _t('sub.category.opml_url') ?></label> <div class="group-controls"> <div class="stick"> - <input id="opml_url" name="opml_url" type="url" autocomplete="off" class="long" data-disable-update="refreshOpml" value="<?= $this->category->attributes('opml_url') ?>" /> + <input id="opml_url" name="opml_url" type="url" autocomplete="off" class="long" data-disable-update="refreshOpml" value="<?= $this->category->attributeString('opml_url') ?>" /> <button type="submit" class="btn" id="refreshOpml" formmethod="post" formaction="<?= _url('category', 'refreshOpml', 'id', $this->category->id()) ?>"> <?= _i('refresh') ?> <?= _t('gen.action.refresh_opml') ?> </button> @@ -90,7 +93,8 @@ <legend><?= _t('sub.category.archiving') ?></legend> <?php - $archiving = $this->category->attributes('archiving'); + $archiving = $this->category->attributeArray('archiving'); + /** @var array<'default'?:bool,'keep_period'?:string,'keep_max'?:int,'keep_min'?:int,'keep_favourites'?:bool,'keep_labels'?:bool,'keep_unreads'?:bool>|null $archiving */ if (empty($archiving)) { $archiving = [ 'default' => true ]; } else { @@ -101,7 +105,7 @@ 'keep_period_count' => '3', 'keep_period_unit' => 'P1M', ]; - if (!empty($archiving['keep_period'])) { + if (!empty($archiving['keep_period']) && is_string($archiving['keep_period'])) { if (preg_match('/^PT?(?P<count>\d+)[YMWDH]$/', $archiving['keep_period'], $matches)) { $volatile['enable_keep_period'] = true; $volatile['keep_period_count'] = $matches['count']; @@ -109,21 +113,21 @@ } } //Defaults - if (!isset($archiving['keep_max'])) { - $archiving['keep_max'] = false; + if (!isset($archiving['keep_max']) || !is_int($archiving['keep_max'])) { + $archiving['keep_max'] = 0; } - if (!isset($archiving['keep_favourites'])) { + if (!isset($archiving['keep_min']) || !is_int($archiving['keep_min'])) { + $archiving['keep_min'] = 50; + } + if (!isset($archiving['keep_favourites']) || !is_bool($archiving['keep_favourites'])) { $archiving['keep_favourites'] = true; } - if (!isset($archiving['keep_labels'])) { + if (!isset($archiving['keep_labels']) || !is_bool($archiving['keep_labels'])) { $archiving['keep_labels'] = true; } - if (!isset($archiving['keep_unreads'])) { + if (!isset($archiving['keep_unreads']) || !is_bool($archiving['keep_unreads'])) { $archiving['keep_unreads'] = false; } - if (!isset($archiving['keep_min'])) { - $archiving['keep_min'] = 50; - } ?> <p class="alert alert-warn"> diff --git a/app/views/helpers/configure/query.phtml b/app/views/helpers/configure/query.phtml index 0f76bc202..145425271 100644 --- a/app/views/helpers/configure/query.phtml +++ b/app/views/helpers/configure/query.phtml @@ -1,6 +1,9 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ + if ($this->query === null) { + throw new FreshRSS_Context_Exception('Query not initialised!'); + } ?> <div class="post"> <h2><?= $this->query->getName() ?></h2> diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml index 2c3e004fc..ce53bfc02 100644 --- a/app/views/helpers/export/opml.phtml +++ b/app/views/helpers/export/opml.phtml @@ -30,7 +30,7 @@ function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array { break; } /** @var array<string,string> */ - $xPathSettings = $feed->attributes('xpath'); + $xPathSettings = $feed->attributeArray('xpath') ?? []; $outline['frss:xPathItem'] = $xPathSettings['item'] ?? null; $outline['frss:xPathItemTitle'] = $xPathSettings['itemTitle'] ?? null; $outline['frss:xPathItemContent'] = $xPathSettings['itemContent'] ?? null; @@ -56,8 +56,8 @@ function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array { $outline['frss:cssFullContent'] = htmlspecialchars_decode($feed->pathEntries(), ENT_QUOTES); } - if ($feed->attributes('path_entries_filter') != '') { - $outline['frss:cssFullContentFilter'] = $feed->attributes('path_entries_filter'); + if ($feed->attributeString('path_entries_filter') != '') { + $outline['frss:cssFullContentFilter'] = $feed->attributeString('path_entries_filter'); } // Remove null attributes @@ -76,7 +76,7 @@ $opml_array = [ 'frss' => FreshRSS_Export_Service::FRSS_NAMESPACE, ], 'head' => [ - 'title' => FreshRSS_Context::$system_conf->title, + 'title' => FreshRSS_Context::systemConf()->title, 'dateCreated' => new DateTime(), ], 'body' => [], @@ -90,7 +90,7 @@ if (!empty($this->categories)) { ]; if ($cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML) { - $outline['frss:opmlUrl'] = $cat->attributes('opml_url'); + $outline['frss:opmlUrl'] = $cat->attributeString('opml_url'); } $opml_array['body'][] = $outline; diff --git a/app/views/helpers/extension/configure.phtml b/app/views/helpers/extension/configure.phtml index 5cf7f9c0a..b714eb553 100644 --- a/app/views/helpers/extension/configure.phtml +++ b/app/views/helpers/extension/configure.phtml @@ -4,6 +4,9 @@ if (!Minz_Request::paramBoolean('ajax')) { $this->partial('aside_configure'); } + if ($this->extension === null) { + throw new FreshRSS_Context_Exception('Extension not initialised!'); + } ?> <div class="post"> <h2> diff --git a/app/views/helpers/extension/details.phtml b/app/views/helpers/extension/details.phtml index b8d0d784f..d1f773020 100644 --- a/app/views/helpers/extension/details.phtml +++ b/app/views/helpers/extension/details.phtml @@ -1,6 +1,9 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ + if ($this->ext_details === null) { + throw new FreshRSS_Context_Exception('Extension not initialised!'); + } $name_encoded = urlencode($this->ext_details->getName()); $ext_enabled = $this->ext_details->isEnabled(); diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index af2503256..5d4f1cc4b 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -1,6 +1,9 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ + if ($this->feed === null) { + throw new FreshRSS_Context_Exception('Feed not initialised!'); + } ?> <div class="post" id="feed_update"> <h1><?= $this->feed->name() ?></h1> @@ -184,9 +187,9 @@ <label class="group-name" for="mark_updated_article_unread"><?= _t('conf.reading.mark_updated_article_unread') ?></label> <div class="group-controls"> <select name="mark_updated_article_unread" id="mark_updated_article_unread" class="w50"> - <option value=""<?= $this->feed->attributes('mark_updated_article_unread') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> - <option value="0"<?= $this->feed->attributes('mark_updated_article_unread') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> - <option value="1"<?= $this->feed->attributes('mark_updated_article_unread') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> + <option value=""<?= $this->feed->attributeBoolean('mark_updated_article_unread') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributeBoolean('mark_updated_article_unread') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributeBoolean('mark_updated_article_unread') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> </div> </div> @@ -195,12 +198,12 @@ <label class="group-name" for="read_when_same_title_in_feed"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <select name="read_when_same_title_in_feed" id="read_when_same_title_in_feed" class="w50"> - <option value=""<?= $this->feed->attributes('read_when_same_title_in_feed') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> - <option value="0"<?= $this->feed->attributes('read_when_same_title_in_feed') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> - <option value="10"<?= $this->feed->attributes('read_when_same_title_in_feed') == 10 ? ' selected="selected"' : '' ?>>10</option> - <option value="25"<?= $this->feed->attributes('read_when_same_title_in_feed') == 25 ? ' selected="selected"' : '' ?>>25</option> - <option value="100"<?= $this->feed->attributes('read_when_same_title_in_feed') == 100 ? ' selected="selected"' : '' ?>>100</option> - <option value="1000"<?= $this->feed->attributes('read_when_same_title_in_feed') == 1000 ? ' selected="selected"' : '' ?>>1 000</option> + <option value=""<?= $this->feed->attributeBoolean('read_when_same_title_in_feed') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributeBoolean('read_when_same_title_in_feed') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="10"<?= $this->feed->attributeInt('read_when_same_title_in_feed') == 10 ? ' selected="selected"' : '' ?>>10</option> + <option value="25"<?= $this->feed->attributeInt('read_when_same_title_in_feed') == 25 ? ' selected="selected"' : '' ?>>25</option> + <option value="100"<?= $this->feed->attributeInt('read_when_same_title_in_feed') == 100 ? ' selected="selected"' : '' ?>>100</option> + <option value="1000"<?= $this->feed->attributeInt('read_when_same_title_in_feed') == 1000 ? ' selected="selected"' : '' ?>>1 000</option> </select> <?= _t('conf.reading.read.when_same_title') ?> </div> @@ -210,9 +213,9 @@ <label class="group-name" for="read_upon_reception"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <select name="read_upon_reception" id="read_upon_reception" class="w50"> - <option value=""<?= $this->feed->attributes('read_upon_reception') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> - <option value="0"<?= $this->feed->attributes('read_upon_reception') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> - <option value="1"<?= $this->feed->attributes('read_upon_reception') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> + <option value=""<?= $this->feed->attributeBoolean('read_upon_reception') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributeBoolean('read_upon_reception') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributeBoolean('read_upon_reception') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> <?= _t('conf.reading.read.upon_reception') ?> </div> @@ -222,9 +225,9 @@ <label class="group-name" for="read_upon_gone"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <select name="read_upon_gone" id="read_upon_gone" class="w50"> - <option value=""<?= $this->feed->attributes('read_upon_gone') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> - <option value="0"<?= $this->feed->attributes('read_upon_gone') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> - <option value="1"<?= $this->feed->attributes('read_upon_gone') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> + <option value=""<?= $this->feed->attributeBoolean('read_upon_gone') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributeBoolean('read_upon_gone') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributeBoolean('read_upon_gone') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> <?= _t('conf.reading.read.upon_gone') ?> </div> @@ -245,7 +248,10 @@ <div class="form-group"> <label class="group-name" for="keep_max_n_unread"><?= _t('conf.reading.read.keep_max_n_unread') ?></label> <div class="group-controls"> - <input type="number" name="keep_max_n_unread" id="keep_max_n_unread" class="w50" min="1" max="10000000" value="<?= $this->feed->attributes('keep_max_n_unread') ?>" placeholder="<?= _t('gen.short.by_default') ?>" /> + <input type="number" name="keep_max_n_unread" id="keep_max_n_unread" class="w50" min="1" max="10000000" + data-leave-validation="<?= $this->feed->attributeInt('keep_max_n_unread') ?>" + value="<?= $this->feed->attributeInt('keep_max_n_unread') ?>" + placeholder="<?= _t('gen.short.by_default') ?>" /> </div> </div> @@ -267,7 +273,8 @@ </div> </div> <?php - $archiving = $this->feed->attributes('archiving'); + $archiving = $this->feed->attributeArray('archiving'); + /** @var array<'default'?:bool,'keep_period'?:string,'keep_max'?:int,'keep_min'?:int,'keep_favourites'?:bool,'keep_labels'?:bool,'keep_unreads'?:bool>|null $archiving */ if (empty($archiving)) { $archiving = [ 'default' => true ]; } else { @@ -278,7 +285,7 @@ 'keep_period_count' => '3', 'keep_period_unit' => 'P1M', ]; - if (!empty($archiving['keep_period'])) { + if (!empty($archiving['keep_period']) && is_string($archiving['keep_period'])) { if (preg_match('/^PT?(?P<count>\d+)[YMWDH]$/', $archiving['keep_period'], $matches)) { $volatile['enable_keep_period'] = true; $volatile['keep_period_count'] = $matches['count']; @@ -286,19 +293,19 @@ } } //Defaults - if (!isset($archiving['keep_max'])) { - $archiving['keep_max'] = false; + if (!isset($archiving['keep_max']) || !is_int($archiving['keep_max'])) { + $archiving['keep_max'] = 0; } - if (!isset($archiving['keep_min'])) { + if (!isset($archiving['keep_min']) || !is_int($archiving['keep_min'])) { $archiving['keep_min'] = 50; } - if (!isset($archiving['keep_favourites'])) { + if (!isset($archiving['keep_favourites']) || !is_bool($archiving['keep_favourites'])) { $archiving['keep_favourites'] = true; } - if (!isset($archiving['keep_labels'])) { + if (!isset($archiving['keep_labels']) || !is_bool($archiving['keep_labels'])) { $archiving['keep_labels'] = true; } - if (!isset($archiving['keep_unreads'])) { + if (!isset($archiving['keep_unreads']) || !is_bool($archiving['keep_unreads'])) { $archiving['keep_unreads'] = false; } ?> @@ -406,7 +413,7 @@ <fieldset id="html_xpath"> <?php - $xpath = Minz_Helper::htmlspecialchars_utf8((array)($this->feed->attributes('xpath'))); + $xpath = Minz_Helper::htmlspecialchars_utf8($this->feed->attributeArray('xpath') ?? []); ?> <p class="help"><?= _i('help') ?> <?= _t('sub.feed.kind.html_xpath.help') ?></p> <div class="form-group"> @@ -521,7 +528,7 @@ <div class="form-group"> <?php - $path_entries_filter = Minz_Helper::htmlspecialchars_utf8((string)($this->feed->attributes('path_entries_filter'))); + $path_entries_filter = Minz_Helper::htmlspecialchars_utf8($this->feed->attributeString('path_entries_filter') ?? ''); ?> <label class="group-name" for="path_entries_filter"><?= _t('sub.feed.css_path_filter') ?></label> <div class="group-controls"> @@ -537,13 +544,13 @@ <label class="group-name" for="curl_params_cookie"><?= _t('sub.feed.css_cookie') ?></label> <div class="group-controls"> <input type="text" name="curl_params_cookie" id="curl_params_cookie" class="w100" value="<?= - is_array($this->feed->attributes('curl_params')) && !empty($this->feed->attributes('curl_params')[CURLOPT_COOKIE]) ? - $this->feed->attributes('curl_params')[CURLOPT_COOKIE] : '' + $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_COOKIE]) ? + $this->feed->attributeArray('curl_params')[CURLOPT_COOKIE] : '' ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" /> <p class="help"><?= _i('help') ?> <?= _t('sub.feed.css_cookie_help') ?></p> <label for="curl_params_cookiefile"> <input type="checkbox" name="curl_params_cookiefile" id="curl_params_cookiefile" value="1"<?= - is_array($this->feed->attributes('curl_params')) && isset($this->feed->attributes('curl_params')[CURLOPT_COOKIEFILE]) ? + $this->feed->attributeArray('curl_params') !== null && isset($this->feed->attributeArray('curl_params')[CURLOPT_COOKIEFILE]) ? ' checked="checked"' : '' ?> /> <?= _t('sub.feed.accept_cookies') ?> @@ -556,8 +563,8 @@ <label class="group-name" for="curl_params_redirects"><?= _t('sub.feed.max_http_redir') ?></label> <div class="group-controls"> <input type="number" name="curl_params_redirects" id="curl_params_redirects" class="w50" min="-1" value="<?= - is_array($this->feed->attributes('curl_params')) && !empty($this->feed->attributes('curl_params')[CURLOPT_MAXREDIRS]) ? - $this->feed->attributes('curl_params')[CURLOPT_MAXREDIRS] : '' + $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_MAXREDIRS]) ? + $this->feed->attributeArray('curl_params')[CURLOPT_MAXREDIRS] : '' ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" /> <p class="help"><?= _i('help') ?> <?= _t('sub.feed.max_http_redir_help') ?></p> </div> @@ -567,9 +574,9 @@ <label class="group-name" for="content_action"><?= _t('sub.feed.content_action') ?></label> <div class="group-controls"> <select name="content_action" id="content_action" class="w50"> - <option value="replace"<?= 'replace' === $this->feed->attributes('content_action') ? ' selected="selected"' : '' ?>><?= _t('sub.feed.content_action.replace') ?></option> - <option value="prepend"<?= 'prepend' === $this->feed->attributes('content_action') ? ' selected="selected"' : '' ?>><?= _t('sub.feed.content_action.prepend') ?></option> - <option value="append"<?= 'append' === $this->feed->attributes('content_action') ? ' selected="selected"' : '' ?>><?= _t('sub.feed.content_action.append') ?></option> + <option value="replace"<?= 'replace' === $this->feed->attributeString('content_action') ? ' selected="selected"' : '' ?>><?= _t('sub.feed.content_action.replace') ?></option> + <option value="prepend"<?= 'prepend' === $this->feed->attributeString('content_action') ? ' selected="selected"' : '' ?>><?= _t('sub.feed.content_action.prepend') ?></option> + <option value="append"<?= 'append' === $this->feed->attributeString('content_action') ? ' selected="selected"' : '' ?>><?= _t('sub.feed.content_action.append') ?></option> </select> </div> </div> @@ -578,8 +585,8 @@ <label class="group-name" for="curl_params_useragent"><?= _t('sub.feed.useragent') ?></label> <div class="group-controls"> <input type="text" name="curl_params_useragent" id="curl_params_useragent" class="w100" value="<?= - is_array($this->feed->attributes('curl_params')) && !empty($this->feed->attributes('curl_params')[CURLOPT_USERAGENT]) ? - $this->feed->attributes('curl_params')[CURLOPT_USERAGENT] : '' + $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_USERAGENT]) ? + $this->feed->attributeArray('curl_params')[CURLOPT_USERAGENT] : '' ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" /> <p class="help"><?= _i('help') ?> <?= _t('sub.feed.useragent_help') ?></p> </div> @@ -590,8 +597,8 @@ <div class="group-controls"> <select name="proxy_type" id="proxy_type"><?php $type = ''; - if (is_array($this->feed->attributes('curl_params')) && isset($this->feed->attributes('curl_params')[CURLOPT_PROXYTYPE])) { - $type = $this->feed->attributes('curl_params')[CURLOPT_PROXYTYPE]; + if ($this->feed->attributeArray('curl_params') !== null && isset($this->feed->attributeArray('curl_params')[CURLOPT_PROXYTYPE])) { + $type = $this->feed->attributeArray('curl_params')[CURLOPT_PROXYTYPE]; } foreach(['' => '', 3 => 'NONE', 0 => 'HTTP', 2 => 'HTTPS', 4 => 'SOCKS4', 6 => 'SOCKS4A', 5 => 'SOCKS5', 7 => 'SOCKS5H'] as $k => $v) { echo '<option value="' . $k . ($type === $k ? '" selected="selected' : '' ) . '">' . $v . '</option>'; @@ -599,8 +606,8 @@ ?> </select> <input type="text" name="curl_params" id="curl_params" value="<?= - is_array($this->feed->attributes('curl_params')) && !empty($this->feed->attributes('curl_params')[CURLOPT_PROXY]) ? - $this->feed->attributes('curl_params')[CURLOPT_PROXY] : '' + $this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_PROXY]) ? + $this->feed->attributeArray('curl_params')[CURLOPT_PROXY] : '' ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" /> <p class="help"><?= _i('help') ?> <?= _t('sub.feed.proxy_help') ?></p> </div> @@ -609,7 +616,7 @@ <div class="form-group"> <label class="group-name" for="timeout"><?= _t('sub.feed.timeout') ?></label> <div class="group-controls"> - <input type="number" name="timeout" id="timeout" class="w50" min="3" max="900" value="<?= $this->feed->attributes('timeout') ?>" placeholder="<?= _t('gen.short.by_default') ?>" /> + <input type="number" name="timeout" id="timeout" class="w50" min="3" max="900" value="<?= $this->feed->attributeInt('timeout') ?>" placeholder="<?= _t('gen.short.by_default') ?>" /> </div> </div> @@ -617,9 +624,9 @@ <label class="group-name" for="ssl_verify"><?= _t('sub.feed.ssl_verify') ?></label> <div class="group-controls"> <select name="ssl_verify" id="ssl_verify" class="w50"> - <option value=""<?= $this->feed->attributes('ssl_verify') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> - <option value="0"<?= $this->feed->attributes('ssl_verify') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> - <option value="1"<?= $this->feed->attributes('ssl_verify') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> + <option value=""<?= $this->feed->attributeBoolean('ssl_verify') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributeBoolean('ssl_verify') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributeBoolean('ssl_verify') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> </div> </div> @@ -627,7 +634,7 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="clear_cache"> - <input type="checkbox" name="clear_cache" id="clear_cache" value="1"<?= $this->feed->attributes('clear_cache') ? ' checked="checked"' : '' ?> /> + <input type="checkbox" name="clear_cache" id="clear_cache" value="1"<?= $this->feed->attributeBoolean('clear_cache') ? ' checked="checked"' : '' ?> /> <?= _t('sub.feed.clear_cache') ?> </label> </div> diff --git a/app/views/helpers/index/normal/entry_bottom.phtml b/app/views/helpers/index/normal/entry_bottom.phtml index dba0e44a3..e5bfd7fd0 100644 --- a/app/views/helpers/index/normal/entry_bottom.phtml +++ b/app/views/helpers/index/normal/entry_bottom.phtml @@ -1,13 +1,13 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ - $bottomline_read = FreshRSS_Context::$user_conf->bottomline_read; - $bottomline_favorite = FreshRSS_Context::$user_conf->bottomline_favorite; - $bottomline_sharing = FreshRSS_Context::$user_conf->bottomline_sharing && (count(FreshRSS_Context::$user_conf->sharing) > 0); - $bottomline_myLabels = FreshRSS_Context::$user_conf->bottomline_myLabels; - $bottomline_tags = FreshRSS_Context::$user_conf->bottomline_tags; - $bottomline_date = FreshRSS_Context::$user_conf->bottomline_date; - $bottomline_link = FreshRSS_Context::$user_conf->bottomline_link; + $bottomline_read = FreshRSS_Context::userConf()->bottomline_read; + $bottomline_favorite = FreshRSS_Context::userConf()->bottomline_favorite; + $bottomline_sharing = FreshRSS_Context::userConf()->bottomline_sharing && (count(FreshRSS_Context::userConf()->sharing) > 0); + $bottomline_myLabels = FreshRSS_Context::userConf()->bottomline_myLabels; + $bottomline_tags = FreshRSS_Context::userConf()->bottomline_tags; + $bottomline_date = FreshRSS_Context::userConf()->bottomline_date; + $bottomline_link = FreshRSS_Context::userConf()->bottomline_link; ?><ul class="horizontal-list bottom"><?php if (FreshRSS_Auth::hasAccess()) { if ($bottomline_read) { @@ -81,8 +81,8 @@ <li class="dropdown-header"><?= _t('index.share') ?> <a href="<?= _url('configure', 'integration') ?>"><?= _i('configure') ?></a></li><?php $id = $this->entry->id(); $link = $this->entry->link(); - $title = $this->entry->title() . ' · ' . $this->feed->name(); - foreach (FreshRSS_Context::$user_conf->sharing as $share_options) { + $title = $this->entry->title() . ' · ' . ($this->feed === null ? '' : $this->feed->name()); + foreach (FreshRSS_Context::userConf()->sharing as $share_options) { $share = FreshRSS_Share::get($share_options['type']); if ($share === null) { continue; diff --git a/app/views/helpers/index/normal/entry_header.phtml b/app/views/helpers/index/normal/entry_header.phtml index 6a693b0a9..b324a5949 100644 --- a/app/views/helpers/index/normal/entry_header.phtml +++ b/app/views/helpers/index/normal/entry_header.phtml @@ -1,15 +1,18 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ - $topline_read = FreshRSS_Context::$user_conf->topline_read; - $topline_favorite = FreshRSS_Context::$user_conf->topline_favorite; - $topline_website = FreshRSS_Context::$user_conf->topline_website; - $topline_thumbnail = FreshRSS_Context::$user_conf->topline_thumbnail; - $topline_summary = FreshRSS_Context::$user_conf->topline_summary; - $topline_display_authors = FreshRSS_Context::$user_conf->topline_display_authors; - $topline_date = FreshRSS_Context::$user_conf->topline_date; - $topline_link = FreshRSS_Context::$user_conf->topline_link; - $lazyload = FreshRSS_Context::$user_conf->lazyload; + if ($this->feed === null) { + throw new FreshRSS_Context_Exception('Feed not initialised!'); + } + $topline_read = FreshRSS_Context::userConf()->topline_read; + $topline_favorite = FreshRSS_Context::userConf()->topline_favorite; + $topline_website = FreshRSS_Context::userConf()->topline_website; + $topline_thumbnail = FreshRSS_Context::userConf()->topline_thumbnail; + $topline_summary = FreshRSS_Context::userConf()->topline_summary; + $topline_display_authors = FreshRSS_Context::userConf()->topline_display_authors; + $topline_date = FreshRSS_Context::userConf()->topline_date; + $topline_link = FreshRSS_Context::userConf()->topline_link; + $lazyload = FreshRSS_Context::userConf()->lazyload; ?><ul class="horizontal-list flux_header website<?= $topline_website ?>"><?php if (FreshRSS_Auth::hasAccess()) { if ($topline_read) { @@ -37,7 +40,7 @@ if ($topline_website !== 'none'): ?><li class="item website <?= $topline_website ?>"> <a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>" class="item-element" title="<?= _t('gen.action.filter') ?>: <?= $this->feed->name() ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons && 'name' !== $topline_website): ?><img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><?php if ('icon' !== $topline_website): ?><span class="websiteName"><?= $this->feed->name() ?></span><?php endif; ?> + <?php if (FreshRSS_Context::userConf()->show_favicons && 'name' !== $topline_website): ?><img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><?php if ('icon' !== $topline_website): ?><span class="websiteName"><?= $this->feed->name() ?></span><?php endif; ?> </a> </li><?php endif; ?> diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 38a7751ee..29f78e5ee 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -1,27 +1,27 @@ <?php declare(strict_types=1); /** @var FreshRSS_View $this */ -$mark = FreshRSS_Context::$user_conf->mark_when; -$s = FreshRSS_Context::$user_conf->shortcuts; +$mark = FreshRSS_Context::userConf()->mark_when; +$s = FreshRSS_Context::userConf()->shortcuts; $extData = Minz_ExtensionManager::callHook('js_vars', []); echo htmlspecialchars(json_encode(array( 'context' => array( 'anonymous' => !FreshRSS_Auth::hasAccess(), 'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(), - 'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'), - 'display_order' => Minz_Request::paramString('order') ?: FreshRSS_Context::$user_conf->sort_order, - 'display_categories' => FreshRSS_Context::$user_conf->display_categories, + 'hide_posts' => !(FreshRSS_Context::userConf()->display_posts || Minz_Request::actionName() === 'reader'), + 'display_order' => Minz_Request::paramString('order') ?: FreshRSS_Context::userConf()->sort_order, + 'display_categories' => FreshRSS_Context::userConf()->display_categories, 'auto_mark_article' => !!$mark['article'], 'auto_mark_site' => !!$mark['site'], 'auto_mark_scroll' => !!$mark['scroll'], 'auto_mark_focus' => !!$mark['focus'], - 'auto_load_more' => !!FreshRSS_Context::$user_conf->auto_load_more, + 'auto_load_more' => !!FreshRSS_Context::userConf()->auto_load_more, 'auto_actualize_feeds' => Minz_Session::paramBoolean('actualize_feeds'), - 'does_lazyload' => !!FreshRSS_Context::$user_conf->lazyload , - 'sides_close_article' => !!FreshRSS_Context::$user_conf->sides_close_article, + 'does_lazyload' => !!FreshRSS_Context::userConf()->lazyload , + 'sides_close_article' => !!FreshRSS_Context::userConf()->sides_close_article, 'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(), - 'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout, - 'auth_type' => FreshRSS_Context::$system_conf->auth_type, + 'html5_notif_timeout' => FreshRSS_Context::userConf()->html5_notif_timeout, + 'auth_type' => FreshRSS_Context::systemConf()->auth_type, 'current_view' => Minz_Request::actionName(), 'csrf' => FreshRSS_Auth::csrfToken(), 'mtime' => [ @@ -69,7 +69,7 @@ echo htmlspecialchars(json_encode(array( 'notif_request_failed' => _t('gen.js.feedback.request_failed'), 'category_empty' => _t('gen.js.category_empty'), 'labels_empty' => _t('gen.js.labels_empty'), - 'language' => FreshRSS_Context::$user_conf->language, + 'language' => FreshRSS_Context::userConf()->language, ), 'icons' => array( 'read' => rawurlencode(_i('read')), diff --git a/app/views/helpers/stream-footer.phtml b/app/views/helpers/stream-footer.phtml index 41f4315b5..0cbab601a 100644 --- a/app/views/helpers/stream-footer.phtml +++ b/app/views/helpers/stream-footer.phtml @@ -32,13 +32,13 @@ <?php } elseif ($hasAccess) { ?> <?= _t('gen.stream.nothing_to_load') ?><br /> <button id="bigMarkAsRead" - class="as-link <?= FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : '' ?>" + class="as-link <?= FreshRSS_Context::userConf()->reading_confirm ? 'confirm" disabled="disabled' : '' ?>" form="stream-footer" formaction="<?= Minz_Url::display($url_mark_read) ?>" type="submit"> <span class="bigTick">✓</span><br /> <span class="markAllRead"><?= _t('gen.stream.mark_all_read') ?></span><br /> - <?php if (FreshRSS_Context::$user_conf->onread_jump_next) { ?> + <?php if (FreshRSS_Context::userConf()->onread_jump_next) { ?> <span class="jumpNext"><?= _t('conf.reading.jump_next') ?></span> <?php } ?> </button> diff --git a/app/views/index/about.phtml b/app/views/index/about.phtml index 5ee1729cd..1c41cf4d6 100644 --- a/app/views/index/about.phtml +++ b/app/views/index/about.phtml @@ -33,8 +33,8 @@ <?= FRESHRSS_VERSION ?> <?php - $env = FreshRSS_Context::$system_conf->environment; - if ($env !== 'production' && FreshRSS_Context::$user_conf->is_admin) { ?> + $env = FreshRSS_Context::systemConf()->environment; + if ($env !== 'production' && FreshRSS_Context::userConf()->is_admin) { ?> <h2>data/config.php</h2> <code>'environment' => '<?= $env; ?>'</code><br /> <?php diff --git a/app/views/index/global.phtml b/app/views/index/global.phtml index 0ddc22e11..6a46ec2ca 100644 --- a/app/views/index/global.phtml +++ b/app/views/index/global.phtml @@ -6,7 +6,7 @@ $class = ''; $state_unread = false; - if (FreshRSS_Context::$user_conf->hide_read_feeds && + if (FreshRSS_Context::userConf()->hide_read_feeds && FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_NOT_READ) && !FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_READ)) { $class = ' state_unread'; @@ -62,7 +62,7 @@ ?> <li id="f_<?= $feed->id() ?>" class="item feed<?= $error_class, $empty_class, $mute_class ?>" title="<?= $error_title, $empty_title ?>" data-unread="<?= $feed->nbNotRead() ?>" data-priority="<?= $feed->priority() ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?><img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?><img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?> <a class="item-title" data-unread="<?= format_number($feed->nbNotRead()) ?>" href="<?= Minz_Url::display($url_base) ?>"><?= $feed->name() ?></a> </li> <?php } ?> @@ -82,6 +82,6 @@ <div id="overlay"> <a class="close" href="#"><?= _i('close') ?></a> - <div id="panel"<?= FreshRSS_Context::$user_conf->display_posts ? '' : ' class="hide_posts"' ?>> + <div id="panel"<?= FreshRSS_Context::userConf()->display_posts ? '' : ' class="hide_posts"' ?>> </div> </div> diff --git a/app/views/index/normal.phtml b/app/views/index/normal.phtml index 0d484f518..42c2e0072 100644 --- a/app/views/index/normal.phtml +++ b/app/views/index/normal.phtml @@ -11,10 +11,10 @@ call_user_func($this->callbackBeforeEntries, $this); $display_today = true; $display_yesterday = true; $display_others = true; -$hidePosts = !FreshRSS_Context::$user_conf->display_posts; -$lazyload = FreshRSS_Context::$user_conf->lazyload; -$content_width = FreshRSS_Context::$user_conf->content_width; -$MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; +$hidePosts = !FreshRSS_Context::userConf()->display_posts; +$lazyload = FreshRSS_Context::userConf()->lazyload; +$content_width = FreshRSS_Context::userConf()->content_width; +$MAX_TAGS_DISPLAYED = FreshRSS_Context::userConf()->show_tags_max; $useKeepUnreadImportant = !FreshRSS_Context::isImportant() && !FreshRSS_Context::isFeed(); $today = @strtotime('today'); @@ -74,12 +74,15 @@ $today = @strtotime('today'); ?>" data-priority="<?= $this->feed->priority() ?>"><?php $this->renderHelper('index/normal/entry_header'); + if ($this->feed === null) { + throw new FreshRSS_Context_Exception('Feed not initialised!'); + } $tags = null; $firstTags = array(); $remainingTags = array(); - if (FreshRSS_Context::$user_conf->show_tags === 'h' || FreshRSS_Context::$user_conf->show_tags === 'f' || FreshRSS_Context::$user_conf->show_tags === 'b') { + if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'f' || FreshRSS_Context::userConf()->show_tags === 'b') { $tags = $this->entry->tags(); if (!empty($tags)) { if ($MAX_TAGS_DISPLAYED > 0) { @@ -93,14 +96,14 @@ $today = @strtotime('today'); ?><article class="flux_content" dir="auto"> <div class="content <?= $content_width ?>"> <header> - <?php if (FreshRSS_Context::$user_conf->show_feed_name === 't') { ?> + <?php if (FreshRSS_Context::userConf()->show_feed_name === 't') { ?> <div class="website"><a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?> <img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><span><?= $this->feed->name() ?></span></a> </div> <?php } ?> - <?php if (FreshRSS_Context::$user_conf->show_tags === 'h' || FreshRSS_Context::$user_conf->show_tags === 'b') { ?> + <?php if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') { ?> <div class="tags"> <?php if (!empty($tags)) { @@ -132,11 +135,11 @@ $today = @strtotime('today'); <?php } ?> <h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $this->entry->link() ?>" title="<?= _t('conf.shortcut.see_on_website')?>"><?= $this->entry->title() ?></a></h1> - <?php if (FreshRSS_Context::$user_conf->show_author_date === 'h' || FreshRSS_Context::$user_conf->show_author_date === 'b') { ?> + <?php if (FreshRSS_Context::userConf()->show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?> <div class="subtitle"> - <?php if (FreshRSS_Context::$user_conf->show_feed_name === 'a') { ?> + <?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?> <div class="website"><a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?> <img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><span><?= $this->feed->name() ?></span></a> </div> @@ -164,8 +167,8 @@ $today = @strtotime('today'); echo $lazyload && $hidePosts ? lazyimg($this->entry->content(true)) : $this->entry->content(true); ?></div> <?php - $display_authors_date = FreshRSS_Context::$user_conf->show_author_date === 'f' || FreshRSS_Context::$user_conf->show_author_date === 'b'; - $display_tags = FreshRSS_Context::$user_conf->show_tags === 'f' || FreshRSS_Context::$user_conf->show_tags === 'b'; + $display_authors_date = FreshRSS_Context::userConf()->show_author_date === 'f' || FreshRSS_Context::userConf()->show_author_date === 'b'; + $display_tags = FreshRSS_Context::userConf()->show_tags === 'f' || FreshRSS_Context::userConf()->show_tags === 'b'; if ($display_authors_date || $display_tags) { ?> @@ -173,9 +176,9 @@ $today = @strtotime('today'); <?php if ($display_authors_date) { ?> <div class="subtitle"> - <?php if (FreshRSS_Context::$user_conf->show_feed_name === 'a') { ?> + <?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?> <div class="website"><a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?> <img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><span><?= $this->feed->name() ?></span></a> </div> @@ -266,4 +269,4 @@ $today = @strtotime('today'); <?= _i('close') ?> </a> -<?php if ($nbEntries > 0 && FreshRSS_Context::$user_conf->show_nav_buttons) $this->partial('nav_entries'); ?> +<?php if ($nbEntries > 0 && FreshRSS_Context::userConf()->show_nav_buttons) $this->partial('nav_entries'); ?> diff --git a/app/views/index/reader.phtml b/app/views/index/reader.phtml index dc90a144a..f2d7ab46b 100644 --- a/app/views/index/reader.phtml +++ b/app/views/index/reader.phtml @@ -8,9 +8,9 @@ if (!Minz_Request::paramBoolean('ajax')) { call_user_func($this->callbackBeforeEntries, $this); -$lazyload = FreshRSS_Context::$user_conf->lazyload; -$content_width = FreshRSS_Context::$user_conf->content_width; -$MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; +$lazyload = FreshRSS_Context::userConf()->lazyload; +$content_width = FreshRSS_Context::userConf()->content_width; +$MAX_TAGS_DISPLAYED = FreshRSS_Context::userConf()->show_tags_max; ?> <main id="stream" class="reader"> <h1 class="title_hidden"><?= _t('conf.reading.view.reader') ?></h1> @@ -35,7 +35,7 @@ $MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; $firstTags = array(); $remainingTags = array(); - if (FreshRSS_Context::$user_conf->show_tags == 'h' || FreshRSS_Context::$user_conf->show_tags == 'f' || FreshRSS_Context::$user_conf->show_tags == 'b') { + if (FreshRSS_Context::userConf()->show_tags == 'h' || FreshRSS_Context::userConf()->show_tags == 'f' || FreshRSS_Context::userConf()->show_tags == 'b') { $tags = $this->entry->tags(); if (!empty($tags)) { if ($MAX_TAGS_DISPLAYED > 0) { @@ -69,15 +69,15 @@ $MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; <a class="read" href="<?= Minz_Url::display($readUrl) ?>" title="<?= _t('conf.shortcut.mark_read') ?>"><?= _i($item->isRead() ? 'read' : 'unread') ?></a> <a class="bookmark" href="<?= Minz_Url::display($favoriteUrl) ?>" title="<?= _t('conf.shortcut.mark_favorite') ?>"><?= _i($item->isFavorite() ? 'starred' : 'non-starred') ?></a> <?php } ?> - <?php if (FreshRSS_Context::$user_conf->show_feed_name === 't') { ?> + <?php if (FreshRSS_Context::userConf()->show_feed_name === 't') { ?> <a class="website" href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?> <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><span><?= $feed->name() ?></span></a> <?php } ?> </div> - <?php if (FreshRSS_Context::$user_conf->show_tags === 'h' || FreshRSS_Context::$user_conf->show_tags === 'b') { ?> + <?php if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') { ?> <div class="tags"> <?php if (!empty($tags)) { @@ -109,11 +109,11 @@ $MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; <?php } ?> <h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $item->link() ?>"><?= $item->title() ?></a></h1> - <?php if (FreshRSS_Context::$user_conf->show_author_date === 'h' || FreshRSS_Context::$user_conf->show_author_date === 'b') { ?> + <?php if (FreshRSS_Context::userConf()->show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?> <div class="subtitle"> - <?php if (FreshRSS_Context::$user_conf->show_feed_name === 'a') { ?> + <?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?> <div class="website"><a href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?> <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><span><?= $feed->name() ?></span></a></div> <?php } ?> @@ -141,17 +141,17 @@ $MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; <?= $item->content(true) ?> </div> <?php - $display_authors_date = FreshRSS_Context::$user_conf->show_author_date === 'f' || FreshRSS_Context::$user_conf->show_author_date === 'b'; - $display_tags = FreshRSS_Context::$user_conf->show_tags === 'f' || FreshRSS_Context::$user_conf->show_tags === 'b'; + $display_authors_date = FreshRSS_Context::userConf()->show_author_date === 'f' || FreshRSS_Context::userConf()->show_author_date === 'b'; + $display_tags = FreshRSS_Context::userConf()->show_tags === 'f' || FreshRSS_Context::userConf()->show_tags === 'b'; if ($display_authors_date || $display_tags) { ?> <footer> <?php if ($display_authors_date) { ?> <div class="subtitle"> - <?php if (FreshRSS_Context::$user_conf->show_feed_name === 'a') { ?> + <?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?> <div class="website"><a href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>"> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?> <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?><span><?= $feed->name() ?></span></a></div> <?php } ?> diff --git a/app/views/index/rss.phtml b/app/views/index/rss.phtml index a10aa806b..0205b0703 100644 --- a/app/views/index/rss.phtml +++ b/app/views/index/rss.phtml @@ -47,7 +47,7 @@ foreach ($this->entries as $item) { echo "\t\t\t", '<media:thumbnail url="' . $thumbnail['url'] . (empty($thumbnail['width']) ? '' : '" width="' . $thumbnail['width']) . (empty($thumbnail['height']) ? '' : '" height="' . $thumbnail['height']) - . (empty($thumbnail['time']) ? '' : '" time="' . $thumbnail['time']) + . (empty($thumbnail['type']) ? '' : '" type="' . $thumbnail['type']) . '" />', "\n"; } $enclosures = $item->enclosures(false); diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index a671bb83e..5390a00a3 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -36,15 +36,15 @@ $empty_class = ''; $empty_title = ''; - if ($feed != null && $feed->nbEntries() == 0) { + if ($feed !== null && $feed->nbEntries() == 0) { $empty_class = ' empty'; $empty_title = _t('sub.feed.empty'); } - $mute_class = $feed->mute() ? ' mute' : ''; + $mute_class = ($feed !== null && $feed->mute()) ? ' mute' : ''; ?> <li class="item feed<?= $error_class, $empty_class, $mute_class ?>" title="<?= $error_title, $empty_title ?>"> <a class="configure open-slider" href="<?= _url('stats', 'feed', 'id', $feedInPeriod['id'], 'sub', 'idle') ?>" title="<?= _t('gen.action.manage') ?>"><?= _i('configure') ?></a> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?><img class="favicon" src="<?= $feedInPeriod['favicon'] ?>" alt="✇" loading="lazy" /><?php endif; ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?><img class="favicon" src="<?= $feedInPeriod['favicon'] ?>" alt="✇" loading="lazy" /><?php endif; ?> <span title="<?= timestamptodate((int)($feedInPeriod['last_date']), false) ?>"><?= $feedInPeriod['name'] ?> (<?= _t('admin.stats.number_entries', $feedInPeriod['nb_articles']) ?>)</span> </li> diff --git a/app/views/subscription/index.phtml b/app/views/subscription/index.phtml index a1a874b31..4bb966e8b 100644 --- a/app/views/subscription/index.phtml +++ b/app/views/subscription/index.phtml @@ -34,8 +34,8 @@ ?> <div class="box"> <div class="box-title"> - <a class="configure open-slider" href="<?= _url('category', 'update', 'id', $cat->id()) ?>" data-cat-position="<?= $cat->attributes('position') ?>"><?= _i('configure') ?></a> - <h2><?= $cat->name() ?><?php if ($cat->kind() == FreshRSS_Category::KIND_DYNAMIC_OPML) { echo " " . _i('opml-dyn'); } ?></h2> + <a class="configure open-slider" href="<?= _url('category', 'update', 'id', $cat->id()) ?>" data-cat-position="<?= $cat->attributeString('position') ?>"><?= _i('configure') ?></a> + <h2><?= $cat->name() ?><?php if ($cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML) { echo " " . _i('opml-dyn'); } ?></h2> </div> <ul class="box-content drop-zone scrollbar-thin" dropzone="move" data-cat-id="<?= $cat->id() ?>"> <?php @@ -63,7 +63,7 @@ <li class="item feed<?= $error_class, $empty_class, $mute_class ?>" title="<?= $error_title, $empty_title ?>" draggable="true" data-feed-id="<?= $feed->id() ?>" data-priority="<?= $feed->priority() ?>"> <a class="configure open-slider" href="<?= _url('subscription', 'feed', 'id', $feed->id()) ?>" title="<?= _t('gen.action.manage') ?>"><?= _i('configure') ?></a> - <?php if (FreshRSS_Context::$user_conf->show_favicons): ?><img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?> + <?php if (FreshRSS_Context::userConf()->show_favicons): ?><img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php endif; ?> <span class="item-title"><?= $feed->name() ?></span> </li> <?php diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml index dcff153d6..12ae3d07d 100644 --- a/app/views/user/manage.phtml +++ b/app/views/user/manage.phtml @@ -21,7 +21,7 @@ <select name="new_user_language" id="new_user_language"> <?php $languages = Minz_Translate::availableLanguages(); ?> <?php foreach ($languages as $lang) { ?> - <option value="<?= $lang ?>"<?= FreshRSS_Context::$user_conf->language === $lang ? + <option value="<?= $lang ?>"<?= FreshRSS_Context::userConf()->language === $lang ? ' selected="selected"' : '' ?>><?= _t('gen.lang.' . $lang) ?></option> <?php } ?> </select> diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml index 0a41d4dc4..576e821b2 100644 --- a/app/views/user/profile.phtml +++ b/app/views/user/profile.phtml @@ -36,7 +36,7 @@ <div class="form-group"> <label class="group-name" for="email"><?= _t('conf.profile.email') ?></label> <div class="group-controls"> - <input id="email" name="email" type="email" value="<?= FreshRSS_Context::$user_conf->mail_login ?>" /> + <input id="email" name="email" type="email" value="<?= FreshRSS_Context::userConf()->mail_login ?>" /> </div> </div> @@ -56,12 +56,12 @@ <?php if (FreshRSS_Auth::accessNeedsAction()) { ?> <div class="form-group"> <label class="group-name" for="token"><?= _t('admin.auth.token') ?></label> - <?php $token = FreshRSS_Context::$user_conf->token; ?> + <?php $token = FreshRSS_Context::userConf()->token; ?> <div class="group-controls"> <input type="text" id="token" name="token" value="<?= $token ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" data-leave-validation="<?= $token ?>"/> <p class="help"><?= _i('help') ?> <?= _t('admin.auth.token_help') ?></p> <kbd><?= Minz_Url::display(array('a' => 'rss', 'params' => array('user' => Minz_User::name(), - 'token' => $token, 'hours' => FreshRSS_Context::$user_conf->since_hours_posts_per_rss)), 'html', true) ?></kbd> + 'token' => $token, 'hours' => FreshRSS_Context::userConf()->since_hours_posts_per_rss)), 'html', true) ?></kbd> </div> </div> <?php } ?> @@ -74,7 +74,7 @@ </div> </form> - <?php if (FreshRSS_Context::$system_conf->api_enabled) { ?> + <?php if (FreshRSS_Context::systemConf()->api_enabled) { ?> <form method="post" action="<?= _url('api', 'updatePassword') ?>"> <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <legend><?= _t('conf.profile.api') ?></legend> diff --git a/app/views/user/validateEmail.phtml b/app/views/user/validateEmail.phtml index 8f822fe6a..a2f486d86 100644 --- a/app/views/user/validateEmail.phtml +++ b/app/views/user/validateEmail.phtml @@ -4,11 +4,11 @@ ?> <main class="post"> <p> - <?= _t('user.email.validation.need_to', FreshRSS_Context::$system_conf->title) ?> + <?= _t('user.email.validation.need_to', FreshRSS_Context::systemConf()->title) ?> </p> <p> - <?= _t('user.email.validation.email_sent_to', FreshRSS_Context::$user_conf->mail_login) ?> + <?= _t('user.email.validation.email_sent_to', FreshRSS_Context::userConf()->mail_login) ?> </p> <form action="<?= _url('user', 'sendValidationEmail') ?>" method="post"> diff --git a/cli/_cli.php b/cli/_cli.php index eb2152082..91fa8de36 100644 --- a/cli/_cli.php +++ b/cli/_cli.php @@ -35,11 +35,12 @@ function cliInitUser(string $username): string { fail('FreshRSS error: user not found: ' . $username . "\n"); } - if (!FreshRSS_Context::initUser($username)) { + FreshRSS_Context::initUser($username); + if (!FreshRSS_Context::hasUserConf()) { fail('FreshRSS error: invalid configuration for user: ' . $username . "\n"); } - $ext_list = FreshRSS_Context::$user_conf->extensions_enabled; + $ext_list = FreshRSS_Context::userConf()->extensions_enabled; Minz_ExtensionManager::enableByList($ext_list, 'user'); return $username; @@ -80,10 +81,10 @@ function performRequirementCheck(string $databaseType): void { */ function getLongOptions(array $options, string $regex): array { $longOptions = array_filter($options, static function (string $a) use ($regex) { - return preg_match($regex, $a); + return preg_match($regex, $a) === 1; }); return array_map(static function (string $a) use ($regex) { - return preg_replace($regex, '', $a); + return preg_replace($regex, '', $a) ?? ''; }, $longOptions); } diff --git a/cli/_update-or-create-user.php b/cli/_update-or-create-user.php index 69e6ce589..385bbd549 100644 --- a/cli/_update-or-create-user.php +++ b/cli/_update-or-create-user.php @@ -2,7 +2,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', diff --git a/cli/actualize-user.php b/cli/actualize-user.php index 870eda9b2..99b48116d 100755 --- a/cli/actualize-user.php +++ b/cli/actualize-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', @@ -17,7 +17,7 @@ if (!validateOptions($argv, $params) || empty($options['user']) || !is_string($o $username = cliInitUser($options['user']); -Minz_ExtensionManager::callHook('freshrss_user_maintenance'); +Minz_ExtensionManager::callHookVoid('freshrss_user_maintenance'); fwrite(STDERR, 'FreshRSS actualizing user “' . $username . "”…\n"); diff --git a/cli/create-user.php b/cli/create-user.php index 286befe85..c360a88a5 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -38,7 +38,7 @@ if (!empty($options['api_password'])) { } } -invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); +invalidateHttpCache(FreshRSS_Context::systemConf()->default_user); echo 'ℹ️ Remember to refresh the feeds of the user: ', $username , "\t", './cli/actualize-user.php --user ', $username, "\n"; diff --git a/cli/db-optimize.php b/cli/db-optimize.php index 2561e7cb2..82f545420 100755 --- a/cli/db-optimize.php +++ b/cli/db-optimize.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', diff --git a/cli/delete-user.php b/cli/delete-user.php index 2ad84b7dc..d8d3033cc 100755 --- a/cli/delete-user.php +++ b/cli/delete-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', @@ -24,7 +24,7 @@ if (!preg_grep("/^$username$/i", $usernames)) { fail('FreshRSS error: username not found “' . $username . '”'); } -if (strcasecmp($username, FreshRSS_Context::$system_conf->default_user) === 0) { +if (strcasecmp($username, FreshRSS_Context::systemConf()->default_user) === 0) { fail('FreshRSS error: default user must not be deleted: “' . $username . '”'); } @@ -32,6 +32,6 @@ echo 'FreshRSS deleting user “', $username, "”…\n"; $ok = FreshRSS_user_Controller::deleteUser($username); -invalidateHttpCache(FreshRSS_Context::$system_conf->default_user); +invalidateHttpCache(FreshRSS_Context::systemConf()->default_user); done($ok); diff --git a/cli/do-install.php b/cli/do-install.php index 1b391a458..c69d5f081 100755 --- a/cli/do-install.php +++ b/cli/do-install.php @@ -44,7 +44,7 @@ fwrite(STDERR, 'FreshRSS install…' . "\n"); $config = array( 'salt' => generateSalt(), - 'db' => FreshRSS_Context::$system_conf->db, + 'db' => FreshRSS_Context::systemConf()->db, ); $customConfigPath = DATA_PATH . '/config.custom.php'; @@ -62,7 +62,7 @@ foreach ($params as $param) { } } -if ((!empty($config['base_url'])) && Minz_Request::serverIsPublic($config['base_url'])) { +if ((!empty($config['base_url'])) && is_string($config['base_url']) && Minz_Request::serverIsPublic($config['base_url'])) { $config['pubsubhubbub_enabled'] = true; } diff --git a/cli/export-opml-for-user.php b/cli/export-opml-for-user.php index a28bddced..51beb12f3 100755 --- a/cli/export-opml-for-user.php +++ b/cli/export-opml-for-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', diff --git a/cli/export-sqlite-for-user.php b/cli/export-sqlite-for-user.php index e1dc83987..ee8e183f6 100755 --- a/cli/export-sqlite-for-user.php +++ b/cli/export-sqlite-for-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = [ 'user:', diff --git a/cli/export-zip-for-user.php b/cli/export-zip-for-user.php index dedcb6c11..5458784bf 100755 --- a/cli/export-zip-for-user.php +++ b/cli/export-zip-for-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', diff --git a/cli/i18n/I18nData.php b/cli/i18n/I18nData.php index 6e87e1d7e..f6bc7fb5d 100644 --- a/cli/i18n/I18nData.php +++ b/cli/i18n/I18nData.php @@ -330,7 +330,7 @@ class I18nData { } private function getFilenamePrefix(string $key): string { - return preg_replace('/\..*/', '.php', $key); + return preg_replace('/\..*/', '.php', $key) ?? ''; } } diff --git a/cli/i18n/I18nValue.php b/cli/i18n/I18nValue.php index 22317e1ae..88d0ea494 100644 --- a/cli/i18n/I18nValue.php +++ b/cli/i18n/I18nValue.php @@ -21,7 +21,7 @@ class I18nValue { } $data = explode(' -> ', $data); - $this->value = array_shift($data); + $this->value = (string)(array_shift($data) ?? ''); if (count($data) === 0) { return; } diff --git a/cli/import-for-user.php b/cli/import-for-user.php index 212be7653..b4731b13b 100755 --- a/cli/import-for-user.php +++ b/cli/import-for-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = array( 'user:', diff --git a/cli/import-sqlite-for-user.php b/cli/import-sqlite-for-user.php index 62a9d86c4..b61a73523 100755 --- a/cli/import-sqlite-for-user.php +++ b/cli/import-sqlite-for-user.php @@ -3,7 +3,7 @@ declare(strict_types=1); require(__DIR__ . '/_cli.php'); -performRequirementCheck(FreshRSS_Context::$system_conf->db['type'] ?? ''); +performRequirementCheck(FreshRSS_Context::systemConf()->db['type'] ?? ''); $params = [ 'user:', diff --git a/cli/list-users.php b/cli/list-users.php index 276239b25..f9e63f4f6 100755 --- a/cli/list-users.php +++ b/cli/list-users.php @@ -5,9 +5,9 @@ require(__DIR__ . '/_cli.php'); $users = listUsers(); sort($users); -if (FreshRSS_Context::$system_conf->default_user !== '' - && in_array(FreshRSS_Context::$system_conf->default_user, $users, true)) { - array_unshift($users, FreshRSS_Context::$system_conf->default_user); +if (FreshRSS_Context::systemConf()->default_user !== '' + && in_array(FreshRSS_Context::systemConf()->default_user, $users, true)) { + array_unshift($users, FreshRSS_Context::systemConf()->default_user); $users = array_unique($users); } diff --git a/cli/reconfigure.php b/cli/reconfigure.php index 7d502a17a..84cb42d60 100755 --- a/cli/reconfigure.php +++ b/cli/reconfigure.php @@ -41,10 +41,56 @@ fwrite(STDERR, 'Reconfiguring FreshRSS…' . "\n"); foreach ($params as $param) { $param = rtrim($param, ':'); if (isset($options[$param])) { - FreshRSS_Context::$system_conf->$param = $options[$param] === false ? true : $options[$param]; + switch ($param) { + case 'allow_anonymous_refresh': + FreshRSS_Context::systemConf()->allow_anonymous_refresh = true; + break; + case 'allow_anonymous': + FreshRSS_Context::systemConf()->allow_anonymous = true; + break; + case 'allow_robots': + FreshRSS_Context::systemConf()->allow_robots = true; + break; + case 'api_enabled': + FreshRSS_Context::systemConf()->api_enabled = true; + break; + case 'auth_type': + if (in_array($options[$param], ['form', 'http_auth', 'none'], true)) { + FreshRSS_Context::systemConf()->auth_type = $options[$param]; + } else { + fail('FreshRSS invalid authentication method! auth_type must be one of { form, http_auth, none }'); + } + break; + case 'base_url': + FreshRSS_Context::systemConf()->base_url = $options[$param]; + break; + case 'default_user': + if (FreshRSS_user_Controller::checkUsername($options[$param])) { + FreshRSS_Context::systemConf()->default_user = $options[$param]; + } else { + fail('FreshRSS invalid default username! default_user must be ASCII alphanumeric'); + } + break; + case 'disable_update': + FreshRSS_Context::systemConf()->disable_update = true; + break; + case 'environment': + if (in_array($options[$param], ['development', 'production', 'silent'], true)) { + FreshRSS_Context::systemConf()->environment = $options[$param]; + } else { + fail('FreshRSS invalid environment! environment must be one of { development, production, silent }'); + } + break; + case 'language': + FreshRSS_Context::systemConf()->language = $options[$param]; + break; + case 'title': + FreshRSS_Context::systemConf()->title = $options[$param]; + break; + } } } -$db = FreshRSS_Context::$system_conf->db; +$db = FreshRSS_Context::systemConf()->db; foreach ($dBparams as $dBparam) { $dBparam = rtrim($dBparam, ':'); if (isset($options[$dBparam])) { @@ -52,19 +98,10 @@ foreach ($dBparams as $dBparam) { $db[$param] = $options[$dBparam]; } } -FreshRSS_Context::$system_conf->db = $db; +/** @var array{'type':string,'host':string,'user':string,'password':string,'base':string,'prefix':string, + * 'connection_uri_params':string,'pdo_options':array<int,int|string|bool>} $db */ +FreshRSS_Context::systemConf()->db = $db; -if (!FreshRSS_user_Controller::checkUsername(FreshRSS_Context::$system_conf->default_user)) { - fail('FreshRSS invalid default username (must be ASCII alphanumeric): ' . - FreshRSS_Context::$system_conf->default_user); -} - -if (isset(FreshRSS_Context::$system_conf->auth_type) && - !in_array(FreshRSS_Context::$system_conf->auth_type, ['form', 'http_auth', 'none'], true)) { - fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' - . FreshRSS_Context::$system_conf->auth_type); -} - -FreshRSS_Context::$system_conf->save(); +FreshRSS_Context::systemConf()->save(); done(); diff --git a/cli/user-info.php b/cli/user-info.php index f24f81078..fbf60482a 100755 --- a/cli/user-info.php +++ b/cli/user-info.php @@ -19,8 +19,10 @@ if (!validateOptions($argv, $params)) { if (empty($options['user'])) { $users = listUsers(); } elseif (is_array($options['user'])) { + /** @var array<string> $users */ $users = $options['user']; } else { + /** @var array<string> $users */ $users = array($options['user']); } @@ -67,10 +69,10 @@ foreach ($users as $username) { $feedList = $feedDAO->listFeedsIds(); $data = array( - 'default' => $username === FreshRSS_Context::$system_conf->default_user ? '*' : '', + 'default' => $username === FreshRSS_Context::systemConf()->default_user ? '*' : '', 'user' => $username, - 'admin' => FreshRSS_Context::$user_conf->is_admin ? '*' : '', - 'enabled' => FreshRSS_Context::$user_conf->enabled ? '*' : '', + 'admin' => FreshRSS_Context::userConf()->is_admin ? '*' : '', + 'enabled' => FreshRSS_Context::userConf()->enabled ? '*' : '', 'last_user_activity' => FreshRSS_UserDAO::mtime($username), 'database_size' => $databaseDAO->size(), 'categories' => $catDAO->count(), @@ -79,8 +81,8 @@ foreach ($users as $username) { 'unreads' => (int)$nbEntries['unread'], 'favourites' => (int)$nbFavorites['all'], 'tags' => $tagDAO->count(), - 'lang' => FreshRSS_Context::$user_conf->language, - 'mail_login' => FreshRSS_Context::$user_conf->mail_login, + 'lang' => FreshRSS_Context::userConf()->language, + 'mail_login' => FreshRSS_Context::userConf()->mail_login, ); if (isset($options['h'])) { //Human format $data['last_user_activity'] = date('c', $data['last_user_activity']); diff --git a/composer.json b/composer.json index 5ca412826..e1c96e3c6 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "phpcs": "phpcs . -s", "phpcbf": "phpcbf . -p -s", "phpstan": "phpstan analyse --memory-limit 512M .", - "phpstan-next": "phpstan analyse --level 8 --memory-limit 512M $(find . -type d -name 'vendor' -prune -o -name '*.php' -o -name '*.phtml' | grep -Fxvf ./tests/phpstan-next.txt | sort | paste -s -)", + "phpstan-next": "phpstan analyse --level 9 --memory-limit 512M $(find . -type d -name 'vendor' -prune -o -name '*.php' -o -name '*.phtml' | grep -Fxvf ./tests/phpstan-next.txt | sort | paste -s -)", "phpunit": "phpunit --bootstrap ./tests/bootstrap.php --verbose ./tests", "translations": "cli/manipulate.translation.php -a format", "test": [ diff --git a/composer.lock b/composer.lock index 731faa89c..ef46040ff 100644 --- a/composer.lock +++ b/composer.lock @@ -138,16 +138,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.17.1", + "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", "shasum": "" }, "require": { @@ -188,9 +188,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" }, - "time": "2023-08-13T19:53:39+00:00" + "time": "2023-12-10T21:03:43+00:00" }, { "name": "phar-io/manifest", @@ -305,16 +305,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.39", + "version": "1.10.50", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "d9dedb0413f678b4d03cbc2279a48f91592c97c4" + "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d9dedb0413f678b4d03cbc2279a48f91592c97c4", - "reference": "d9dedb0413f678b4d03cbc2279a48f91592c97c4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4", + "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4", "shasum": "" }, "require": { @@ -363,7 +363,7 @@ "type": "tidelift" } ], - "time": "2023-10-17T15:46:26+00:00" + "time": "2023-12-13T10:59:42+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -419,21 +419,21 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.5.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6" + "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/b21c03d4f6f3a446e4311155f4be9d65048218e6", - "reference": "b21c03d4f6f3a446e4311155f4be9d65048218e6", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/7a50e9662ee9f3942e4aaaf3d603653f60282542", + "reference": "7a50e9662ee9f3942e4aaaf3d603653f60282542", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10.34" }, "require-dev": { "nikic/php-parser": "^4.13.0", @@ -462,9 +462,9 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.1" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.5.2" }, - "time": "2023-03-29T14:47:40+00:00" + "time": "2023-10-30T14:35:06+00:00" }, { "name": "phpunit/php-code-coverage", @@ -787,16 +787,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.13", + "version": "9.6.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be" + "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f3d767f7f9e191eab4189abe41ab37797e30b1be", - "reference": "f3d767f7f9e191eab4189abe41ab37797e30b1be", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", + "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", "shasum": "" }, "require": { @@ -870,7 +870,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.13" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" }, "funding": [ { @@ -886,7 +886,7 @@ "type": "tidelift" } ], - "time": "2023-09-19T05:39:22+00:00" + "time": "2023-12-01T16:55:19+00:00" }, { "name": "sebastian/cli-parser", @@ -1854,16 +1854,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.2", + "version": "3.8.0", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5805f7a4e4958dbb5e944ef1e6edae0a303765e7", + "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7", "shasum": "" }, "require": { @@ -1873,7 +1873,7 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/phpcs", @@ -1892,35 +1892,58 @@ "authors": [ { "name": "Greg Sherwood", - "role": "lead" + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", "standards", "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, - "time": "2023-02-22T23:07:41+00:00" + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2023-12-08T12:32:31+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -1949,7 +1972,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -1957,7 +1980,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" } ], "aliases": [], diff --git a/config-user.default.php b/config-user.default.php index 733701a3a..7bf5485f1 100644 --- a/config-user.default.php +++ b/config-user.default.php @@ -121,5 +121,8 @@ return array ( ), 'html5_notif_timeout' => 0, 'show_nav_buttons' => true, - 'extensions_enabled' => array(), + # List of enabled FreshRSS extensions. + 'extensions_enabled' => [], + # Extensions configurations + 'extensions' => [], ); diff --git a/config.default.php b/config.default.php index f7c4e1315..872975546 100644 --- a/config.default.php +++ b/config.default.php @@ -190,6 +190,8 @@ return array( # List of enabled FreshRSS extensions. 'extensions_enabled' => [ ], + # Extensions configurations + 'extensions' => [], # Disable self-update, 'disable_update' => false, diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 1aa4cae86..18fa6e7df 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -6,8 +6,8 @@ declare(strict_types=1); * @property string $base_url * @property array{'type':string,'host':string,'user':string,'password':string,'base':string,'prefix':string, * 'connection_uri_params':string,'pdo_options':array<int,int|string|bool>} $db - * @property-read string $disable_update - * @property-read string $environment + * @property bool $disable_update + * @property string $environment * @property array<string,bool> $extensions_enabled * @property-read string $mailer * @property-read array{'hostname':string,'host':string,'auth':bool,'username':string,'password':string,'secure':string,'port':int,'from':string} $smtp diff --git a/lib/Minz/Extension.php b/lib/Minz/Extension.php index a8f883eb6..5386a64e7 100644 --- a/lib/Minz/Extension.php +++ b/lib/Minz/Extension.php @@ -19,8 +19,6 @@ abstract class Minz_Extension { private $version; /** @var 'system'|'user' */ private $type; - /** @var string */ - private $config_key = 'extensions'; /** @var array<string,mixed>|null */ private $user_configuration; /** @var array<string,mixed>|null */ @@ -175,8 +173,11 @@ abstract class Minz_Extension { $mtime = @filemtime("{$this->path}/static/{$filename}"); } else { $username = Minz_User::name(); - $path = USERS_PATH . "/{$username}/{$this->config_key}/{$this->getName()}/{$filename}"; - $file_name_url = urlencode("{$username}/{$this->config_key}/{$this->getName()}/{$filename}"); + if ($username == null) { + return ''; + } + $path = USERS_PATH . "/{$username}/extensions/{$this->getName()}/{$filename}"; + $file_name_url = urlencode("{$username}/extensions/{$this->getName()}/{$filename}"); $mtime = @filemtime($path); } @@ -224,8 +225,8 @@ abstract class Minz_Extension { } switch ($type) { - case 'system': return FreshRSS_Context::$system_conf !== null; - case 'user': return FreshRSS_Context::$user_conf !== null; + case 'system': return FreshRSS_Context::hasSystemConf(); + case 'user': return FreshRSS_Context::hasUserConf(); } } @@ -233,50 +234,40 @@ abstract class Minz_Extension { private function isExtensionConfigured(string $type): bool { switch ($type) { case 'user': - $conf = FreshRSS_Context::$user_conf; + $conf = FreshRSS_Context::userConf(); break; case 'system': - $conf = FreshRSS_Context::$system_conf; + $conf = FreshRSS_Context::systemConf(); break; + default: + return false; } - if ($conf === null || !$conf->hasParam($this->config_key)) { + if (!$conf->hasParam('extensions')) { return false; } - $extensions = $conf->{$this->config_key}; - return array_key_exists($this->getName(), $extensions); - } - - /** - * @phpstan-param 'system'|'user' $type - * @return array<string,mixed> - */ - private function getConfiguration(string $type): array { - if (!$this->isConfigurationEnabled($type)) { - return []; - } - - if (!$this->isExtensionConfigured($type)) { - return []; - } - - $conf = "{$type}_conf"; - return FreshRSS_Context::$$conf->{$this->config_key}[$this->getName()]; + return array_key_exists($this->getName(), $conf->extensions); } /** * @return array<string,mixed> */ public final function getSystemConfiguration(): array { - return $this->getConfiguration('system'); + if ($this->isConfigurationEnabled('system') && $this->isExtensionConfigured('system')) { + return FreshRSS_Context::systemConf()->extensions[$this->getName()]; + } + return []; } /** * @return array<string,mixed> */ public final function getUserConfiguration(): array { - return $this->getConfiguration('user'); + if ($this->isConfigurationEnabled('user') && $this->isExtensionConfigured('user')) { + return FreshRSS_Context::userConf()->extensions[$this->getName()]; + } + return []; } /** @@ -314,17 +305,26 @@ abstract class Minz_Extension { * @param array<string,mixed> $configuration */ private function setConfiguration(string $type, array $configuration): void { - $conf = "{$type}_conf"; + switch ($type) { + case 'system': + $conf = FreshRSS_Context::systemConf(); + break; + case 'user': + $conf = FreshRSS_Context::userConf(); + break; + default: + return; + } - if (FreshRSS_Context::$$conf->hasParam($this->config_key)) { - $extensions = FreshRSS_Context::$$conf->{$this->config_key}; + if ($conf->hasParam('extensions')) { + $extensions = $conf->extensions; } else { $extensions = []; } $extensions[$this->getName()] = $configuration; - FreshRSS_Context::$$conf->{$this->config_key} = $extensions; - FreshRSS_Context::$$conf->save(); + $conf->extensions = $extensions; + $conf->save(); } /** @param array<string,mixed> $configuration */ @@ -341,23 +341,28 @@ abstract class Minz_Extension { /** @phpstan-param 'system'|'user' $type */ private function removeConfiguration(string $type): void { - if (!$this->isConfigurationEnabled($type)) { + if (!$this->isConfigurationEnabled($type) || !$this->isExtensionConfigured($type)) { return; } - if (!$this->isExtensionConfigured($type)) { - return; + switch ($type) { + case 'system': + $conf = FreshRSS_Context::systemConf(); + break; + case 'user': + $conf = FreshRSS_Context::userConf(); + break; + default: + return; } - $conf = "{$type}_conf"; - $extensions = FreshRSS_Context::$$conf->{$this->config_key}; + $extensions = $conf->extensions; unset($extensions[$this->getName()]); if (empty($extensions)) { - $extensions = null; + $extensions = []; } - - FreshRSS_Context::$$conf->{$this->config_key} = $extensions; - FreshRSS_Context::$$conf->save(); + $conf->extensions = $extensions; + $conf->save(); } public final function removeSystemConfiguration(): void { @@ -372,7 +377,7 @@ abstract class Minz_Extension { public final function saveFile(string $filename, string $content): void { $username = Minz_User::name(); - $path = USERS_PATH . "/{$username}/{$this->config_key}/{$this->getName()}"; + $path = USERS_PATH . "/{$username}/extensions/{$this->getName()}"; if (!file_exists($path)) { mkdir($path, 0777, true); @@ -383,7 +388,10 @@ abstract class Minz_Extension { public final function removeFile(string $filename): void { $username = Minz_User::name(); - $path = USERS_PATH . "/{$username}/{$this->config_key}/{$this->getName()}/{$filename}"; + if ($username == null) { + return; + } + $path = USERS_PATH . "/{$username}/extensions/{$this->getName()}/{$filename}"; if (file_exists($path)) { unlink($path); diff --git a/lib/Minz/ExtensionManager.php b/lib/Minz/ExtensionManager.php index e1443b9c3..9d8b94cbd 100644 --- a/lib/Minz/ExtensionManager.php +++ b/lib/Minz/ExtensionManager.php @@ -147,7 +147,7 @@ final class Minz_ExtensionManager { $meta_raw_content = file_get_contents($metadata_filename) ?: ''; /** @var array{'name':string,'entrypoint':string,'path':string,'author'?:string,'description'?:string,'version'?:string,'type'?:'system'|'user'}|null $meta_json */ $meta_json = json_decode($meta_raw_content, true); - if (!$meta_json || !self::isValidMetadata($meta_json)) { + if (!is_array($meta_json) || !self::isValidMetadata($meta_json)) { // metadata.json is not a json file? Invalid! // or metadata.json is invalid (no required information), invalid! Minz_Log::warning('`' . $metadata_filename . '` is not a valid metadata file'); @@ -347,9 +347,9 @@ final class Minz_ExtensionManager { call_user_func($function, ...$args); } } elseif ($signature === 'NoneToString') { - return self::callNoneToString($hook_name); + return self::callHookString($hook_name); } elseif ($signature === 'NoneToNone') { - self::callNoneToNone($hook_name); + self::callHookVoid($hook_name); } return; } @@ -391,9 +391,9 @@ final class Minz_ExtensionManager { * @param string $hook_name is the hook to call. * @return string concatenated result of the call to all the hooks. */ - private static function callNoneToString(string $hook_name): string { + public static function callHookString(string $hook_name): string { $result = ''; - foreach (self::$hook_list[$hook_name]['list'] as $function) { + foreach (self::$hook_list[$hook_name]['list'] ?? [] as $function) { $result = $result . call_user_func($function); } return $result; @@ -407,8 +407,8 @@ final class Minz_ExtensionManager { * * @param string $hook_name is the hook to call. */ - private static function callNoneToNone(string $hook_name): void { - foreach (self::$hook_list[$hook_name]['list'] as $function) { + public static function callHookVoid(string $hook_name): void { + foreach (self::$hook_list[$hook_name]['list'] ?? [] as $function) { call_user_func($function); } } diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php index 642b9304c..04539ec40 100644 --- a/lib/Minz/Helper.php +++ b/lib/Minz/Helper.php @@ -19,12 +19,12 @@ class Minz_Helper { * @phpstan-param T $var * @phpstan-return T * - * @param string|array<string> $var - * @return string|array<string> + * @param string|array<mixed> $var + * @return string|array<mixed> */ public static function htmlspecialchars_utf8($var) { if (is_array($var)) { - return array_map(array('Minz_Helper', 'htmlspecialchars_utf8'), $var); + return array_map(['Minz_Helper', 'htmlspecialchars_utf8'], $var); } elseif (is_string($var)) { return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); } else { diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 69b2357d2..b107d3df9 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -129,7 +129,7 @@ class Minz_ModelPdo { $db = Minz_Configuration::get('system')->db; throw new Minz_PDOConnectionException( - $ex->getMessage(), + $ex === null ? '' : $ex->getMessage(), $db['user'], Minz_Exception::ERROR ); } diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 0ac1a9fe3..f39982be1 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -58,7 +58,7 @@ class Minz_Request { } } - /** @return array<string|int,string|array<string,string>> */ + /** @return array<string|int,string|array<string,string|int>> */ public static function paramArray(string $key, bool $specialchars = false): array { if (empty(self::$params[$key]) || !is_array(self::$params[$key])) { return []; @@ -89,8 +89,8 @@ class Minz_Request { } public static function paramInt(string $key): int { - if (!empty(self::$params[$key])) { - return intval(self::$params[$key]); + if (!empty(self::$params[$key]) && is_numeric(self::$params[$key])) { + return (int)self::$params[$key]; } return 0; } @@ -119,7 +119,7 @@ class Minz_Request { * @return array<string> */ public static function paramTextToArray(string $key, array $default = []): array { - if (isset(self::$params[$key])) { + if (isset(self::$params[$key]) && is_string(self::$params[$key])) { return preg_split('/\R/', self::$params[$key]) ?: []; } return $default; @@ -431,7 +431,7 @@ class Minz_Request { if ($ORIGINAL_INPUT == false) { return; } - if (null === $json = json_decode($ORIGINAL_INPUT, true)) { + if (!is_array($json = json_decode($ORIGINAL_INPUT, true))) { return; } diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index 4553d3083..99b7fef45 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -159,7 +159,7 @@ class Minz_Session { * @param bool $force if false, does not clear the language parameter */ public static function unset_session(bool $force = false): void { - $language = self::param('language'); + $language = self::paramString('language'); if (!self::$volatile) { session_destroy(); diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index 8f2e2527a..183fa48ca 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -186,7 +186,7 @@ class Minz_Translate { /** * Translate a key into its corresponding value based on selected language. * @param string $key the key to translate. - * @param mixed ...$args additional parameters for variable keys. + * @param bool|float|int|string ...$args additional parameters for variable keys. * @return string value corresponding to the key. * If no value is found, return the key itself. */ @@ -211,6 +211,9 @@ class Minz_Translate { // Go through the i18n keys to get the correct translation value. $translates = self::$translates[$top_level]; + if (!is_array($translates)) { + $translates = []; + } $size_group = count($group); $level_processed = 0; $translation_value = $key; @@ -253,7 +256,7 @@ class Minz_Translate { /** * Alias for Minz_Translate::t() * @param string $key - * @param mixed ...$args + * @param bool|float|int|string ...$args */ function _t(string $key, ...$args): string { return Minz_Translate::t($key, ...$args); diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php index 0c17e3ec7..2104759cd 100644 --- a/lib/Minz/Url.php +++ b/lib/Minz/Url.php @@ -72,7 +72,7 @@ class Minz_Url { $and = '&'; } - if (!empty($url['params']['#'])) { + if (!empty($url['params']) && is_array($url['params']) && !empty($url['params']['#'])) { $anchor = '#' . ($encodage === 'html' ? htmlspecialchars($url['params']['#'], ENT_QUOTES, 'UTF-8') : $url['params']['#']); unset($url['params']['#']); } @@ -89,7 +89,7 @@ class Minz_Url { $separator = $and; } - if (isset($url['params'])) { + if (isset($url['params']) && is_array($url['params'])) { unset($url['params']['c']); unset($url['params']['a']); foreach ($url['params'] as $key => $param) { @@ -101,7 +101,7 @@ class Minz_Url { } } - if (!empty($url['#'])) { + if (!empty($url['#']) && is_string($url['#'])) { $uri .= '#' . ($encodage === 'html' ? htmlspecialchars($url['#'], ENT_QUOTES, 'UTF-8') : $url['#']); } @@ -141,7 +141,9 @@ class Minz_Url { */ public static function unserialize(string $url = ''): array { try { - return json_decode(base64_decode($url, true) ?: '', true, JSON_THROW_ON_ERROR) ?? []; + $result = json_decode(base64_decode($url, true) ?: '', true, JSON_THROW_ON_ERROR) ?? []; + /** @var array{'c'?:string,'a'?:string,'params'?:array<string,mixed>} $result */ + return $result; } catch (\Throwable $exception) { return []; } diff --git a/lib/Minz/View.php b/lib/Minz/View.php index f67cf6277..c215ecdab 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -345,6 +345,8 @@ class Minz_View { public function attributeParams(): void { foreach (Minz_View::$params as $key => $value) { + // TODO: Do not use variable variable (noVariableVariables) + /** @phpstan-ignore-next-line */ $this->$key = $value; } } diff --git a/lib/favicons.php b/lib/favicons.php index 5cf4295f5..df893b309 100644 --- a/lib/favicons.php +++ b/lib/favicons.php @@ -45,9 +45,8 @@ function downloadHttp(string &$url, array $curlOptions = []): string { ]); FreshRSS_Context::initSystem(); - $system_conf = FreshRSS_Context::$system_conf; - if (isset($system_conf)) { - curl_setopt_array($ch, $system_conf->curl_options); + if (FreshRSS_Context::hasSystemConf()) { + curl_setopt_array($ch, FreshRSS_Context::systemConf()->curl_options); } curl_setopt_array($ch, $curlOptions); diff --git a/lib/lib_install.php b/lib/lib_install.php index 720c4bf77..f36d2eaa6 100644 --- a/lib/lib_install.php +++ b/lib/lib_install.php @@ -77,13 +77,12 @@ function generateSalt(): string { } function initDb(): string { - $conf = FreshRSS_Context::$system_conf; - $db = $conf->db; + $db = FreshRSS_Context::systemConf()->db; if (empty($db['pdo_options'])) { $db['pdo_options'] = []; } $db['pdo_options'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION; - $conf->db = $db; //TODO: Remove this Minz limitation "Indirect modification of overloaded property" + FreshRSS_Context::systemConf()->db = $db; //TODO: Remove this Minz limitation "Indirect modification of overloaded property" if (empty($db['type'])) { $db['type'] = 'sqlite'; @@ -95,7 +94,7 @@ function initDb(): string { $dbBase = $db['base'] ?? ''; //For first connection, use default database for PostgreSQL, empty database for MySQL / MariaDB: $db['base'] = $db['type'] === 'pgsql' ? 'postgres' : ''; - $conf->db = $db; + FreshRSS_Context::systemConf()->db = $db; try { //First connection without database name to create the database $databaseDAO = FreshRSS_Factory::createDatabaseDAO(); @@ -104,7 +103,7 @@ function initDb(): string { } //Restore final database parameters for auto-creation and for future connections $db['base'] = $dbBase; - $conf->db = $db; + FreshRSS_Context::systemConf()->db = $db; if ($databaseDAO != null) { //Perform database auto-creation $databaseDAO->create(); diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 0ab49e25e..e01e8fa81 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -249,22 +249,19 @@ function sensitive_log($log) { * @throws FreshRSS_Context_Exception */ function customSimplePie(array $attributes = array()): SimplePie { - if (FreshRSS_Context::$system_conf === null) { - throw new FreshRSS_Context_Exception('System configuration not initialised!'); - } - $limits = FreshRSS_Context::$system_conf->limits; + $limits = FreshRSS_Context::systemConf()->limits; $simplePie = new SimplePie(); $simplePie->set_useragent(FRESHRSS_USERAGENT); - $simplePie->set_syslog(FreshRSS_Context::$system_conf->simplepie_syslog_enabled); + $simplePie->set_syslog(FreshRSS_Context::systemConf()->simplepie_syslog_enabled); $simplePie->set_cache_name_function('sha1'); $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration($limits['cache_duration']); $simplePie->enable_order_by_date(false); - $feed_timeout = empty($attributes['timeout']) ? 0 : (int)$attributes['timeout']; + $feed_timeout = empty($attributes['timeout']) || !is_numeric($attributes['timeout']) ? 0 : (int)$attributes['timeout']; $simplePie->set_timeout($feed_timeout > 0 ? $feed_timeout : $limits['timeout']); - $curl_options = FreshRSS_Context::$system_conf->curl_options; + $curl_options = FreshRSS_Context::systemConf()->curl_options; if (isset($attributes['ssl_verify'])) { $curl_options[CURLOPT_SSL_VERIFYHOST] = $attributes['ssl_verify'] ? 2 : 0; $curl_options[CURLOPT_SSL_VERIFYPEER] = (bool)$attributes['ssl_verify']; @@ -408,11 +405,8 @@ function enforceHttpEncoding(string $html, string $contentType = ''): string { * @param array<string,mixed> $attributes */ function httpGet(string $url, string $cachePath, string $type = 'html', array $attributes = []): string { - if (FreshRSS_Context::$system_conf === null) { - throw new FreshRSS_Context_Exception('System configuration not initialised!'); - } - $limits = FreshRSS_Context::$system_conf->limits; - $feed_timeout = empty($attributes['timeout']) ? 0 : intval($attributes['timeout']); + $limits = FreshRSS_Context::systemConf()->limits; + $feed_timeout = empty($attributes['timeout']) || !is_numeric($attributes['timeout']) ? 0 : intval($attributes['timeout']); $cacheMtime = @filemtime($cachePath); if ($cacheMtime !== false && $cacheMtime > time() - intval($limits['cache_duration'])) { @@ -427,7 +421,7 @@ function httpGet(string $url, string $cachePath, string $type = 'html', array $a cleanCache(CLEANCACHE_HOURS); } - if (FreshRSS_Context::$system_conf->simplepie_syslog_enabled) { + if (FreshRSS_Context::systemConf()->simplepie_syslog_enabled) { syslog(LOG_INFO, 'FreshRSS GET ' . $type . ' ' . SimplePie_Misc::url_remove_credentials($url)); } @@ -462,7 +456,7 @@ function httpGet(string $url, string $cachePath, string $type = 'html', array $a CURLOPT_ENCODING => '', //Enable all encodings ]); - curl_setopt_array($ch, FreshRSS_Context::$system_conf->curl_options); + curl_setopt_array($ch, FreshRSS_Context::systemConf()->curl_options); if (isset($attributes['curl_params']) && is_array($attributes['curl_params'])) { curl_setopt_array($ch, $attributes['curl_params']); @@ -571,10 +565,7 @@ function listUsers(): array { * @return bool true if number of users >= max registrations, false else. */ function max_registrations_reached(): bool { - if (FreshRSS_Context::$system_conf === null) { - throw new FreshRSS_Context_Exception('System configuration not initialised!'); - } - $limit_registrations = FreshRSS_Context::$system_conf->limits['max_registrations']; + $limit_registrations = FreshRSS_Context::systemConf()->limits['max_registrations']; $number_accounts = count(listUsers()); return $limit_registrations > 0 && $number_accounts >= $limit_registrations; @@ -671,10 +662,10 @@ function connectionRemoteAddress(): string { * Check if the client (e.g. last proxy) is allowed to send unsafe headers. * This uses the `TRUSTED_PROXY` environment variable or the `trusted_sources` configuration option to get an array of the authorized ranges, * The connection IP is obtained from the `CONN_REMOTE_ADDR` (if available, to be robust even when using Apache mod_remoteip) or `REMOTE_ADDR` environment variables. - * @return bool, true if the sender’s IP is in one of the ranges defined in the configuration, else false + * @return bool true if the sender’s IP is in one of the ranges defined in the configuration, else false */ function checkTrustedIP(): bool { - if (FreshRSS_Context::$system_conf === null) { + if (!FreshRSS_Context::hasSystemConf()) { return false; } $remoteIp = connectionRemoteAddress(); @@ -686,7 +677,7 @@ function checkTrustedIP(): bool { $trusted = preg_split('/\s+/', $trusted, -1, PREG_SPLIT_NO_EMPTY); } if (!is_array($trusted) || empty($trusted)) { - $trusted = FreshRSS_Context::$system_conf->trusted_sources; + $trusted = FreshRSS_Context::systemConf()->trusted_sources; } foreach ($trusted as $cidr) { if (checkCIDR($remoteIp, $cidr)) { diff --git a/p/api/fever.php b/p/api/fever.php index cc5778e9f..8cf3dfc21 100644 --- a/p/api/fever.php +++ b/p/api/fever.php @@ -19,7 +19,7 @@ require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader FreshRSS_Context::initSystem(); // check if API is enabled globally -if (FreshRSS_Context::$system_conf == null || !FreshRSS_Context::$system_conf->api_enabled) { +if (!FreshRSS_Context::hasSystemConf() || !FreshRSS_Context::systemConf()->api_enabled) { Minz_Log::warning('Fever API: service unavailable!'); Minz_Log::debug('Fever API: serviceUnavailable() ' . debugInfo(), API_LOG); header('HTTP/1.1 503 Service Unavailable'); @@ -149,20 +149,17 @@ final class FeverAPI * your FreshRSS "username:your-api-password" combination */ private function authenticate(): bool { - if (FreshRSS_Context::$system_conf === null) { - throw new FreshRSS_Context_Exception('System configuration not initialised!'); - } - FreshRSS_Context::$user_conf = null; + FreshRSS_Context::clearUserConf(); Minz_User::change(); $feverKey = empty($_POST['api_key']) ? '' : substr(trim($_POST['api_key']), 0, 128); if (ctype_xdigit($feverKey)) { $feverKey = strtolower($feverKey); - $username = @file_get_contents(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::$system_conf->salt) . '-' . $feverKey . '.txt', false); + $username = @file_get_contents(DATA_PATH . '/fever/.key-' . sha1(FreshRSS_Context::systemConf()->salt) . '-' . $feverKey . '.txt', false); if ($username != false) { $username = trim($username); - FreshRSS_Context::$user_conf = FreshRSS_Context::initUser($username); // Assignment to help PHPStan - if (FreshRSS_Context::$user_conf != null && $feverKey === FreshRSS_Context::$user_conf->feverKey && FreshRSS_Context::$user_conf->enabled) { - Minz_Translate::init(FreshRSS_Context::$user_conf->language); + FreshRSS_Context::initUser($username); + if ($feverKey === FreshRSS_Context::userConf()->feverKey && FreshRSS_Context::userConf()->enabled) { + Minz_Translate::init(FreshRSS_Context::userConf()->language); $this->entryDAO = FreshRSS_Factory::createEntryDao(); $this->feedDAO = FreshRSS_Factory::createFeedDao(); return true; @@ -180,7 +177,7 @@ final class FeverAPI public function isAuthenticatedApiUser(): bool { $this->authenticate(); - return FreshRSS_Context::$user_conf !== null; + return FreshRSS_Context::hasUserConf(); } /** @@ -350,11 +347,11 @@ final class FeverAPI /** @return array<array<string,int|string>> */ private function getFavicons(): array { - if (FreshRSS_Context::$system_conf == null) { + if (!FreshRSS_Context::hasSystemConf()) { return []; } $favicons = array(); - $salt = FreshRSS_Context::$system_conf->salt; + $salt = FreshRSS_Context::systemConf()->salt; $myFeeds = $this->feedDAO->listFeeds(); foreach ($myFeeds as $feed) { diff --git a/p/api/greader.php b/p/api/greader.php index 91a7b5d13..5f351195c 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -179,15 +179,15 @@ final class GReaderAPI { $user = $headerAuthX[0]; if (FreshRSS_user_Controller::checkUsername($user)) { FreshRSS_Context::initUser($user); - if (FreshRSS_Context::$user_conf == null || FreshRSS_Context::$system_conf == null) { + if (!FreshRSS_Context::hasUserConf() || !FreshRSS_Context::hasSystemConf()) { Minz_Log::warning('Invalid API user ' . $user . ': configuration cannot be found.'); self::unauthorized(); } - if (!FreshRSS_Context::$user_conf->enabled) { + if (!FreshRSS_Context::userConf()->enabled) { Minz_Log::warning('Invalid API user ' . $user . ': configuration cannot be found.'); self::unauthorized(); } - if ($headerAuthX[1] === sha1(FreshRSS_Context::$system_conf->salt . $user . FreshRSS_Context::$user_conf->apiPasswordHash)) { + if ($headerAuthX[1] === sha1(FreshRSS_Context::systemConf()->salt . $user . FreshRSS_Context::userConf()->apiPasswordHash)) { return $user; } else { Minz_Log::warning('Invalid API authorisation for user ' . $user); @@ -206,14 +206,14 @@ final class GReaderAPI { //https://web.archive.org/web/20130604091042/http://undoc.in/clientLogin.html if (FreshRSS_user_Controller::checkUsername($email)) { FreshRSS_Context::initUser($email); - if (FreshRSS_Context::$user_conf == null || FreshRSS_Context::$system_conf == null) { + if (!FreshRSS_Context::hasUserConf() || !FreshRSS_Context::hasSystemConf()) { Minz_Log::warning('Invalid API user ' . $email . ': configuration cannot be found.'); self::unauthorized(); } - if (FreshRSS_Context::$user_conf->apiPasswordHash != '' && password_verify($pass, FreshRSS_Context::$user_conf->apiPasswordHash)) { + if (FreshRSS_Context::userConf()->apiPasswordHash != '' && password_verify($pass, FreshRSS_Context::userConf()->apiPasswordHash)) { header('Content-Type: text/plain; charset=UTF-8'); - $auth = $email . '/' . sha1(FreshRSS_Context::$system_conf->salt . $email . FreshRSS_Context::$user_conf->apiPasswordHash); + $auth = $email . '/' . sha1(FreshRSS_Context::systemConf()->salt . $email . FreshRSS_Context::userConf()->apiPasswordHash); echo 'SID=', $auth, "\n", 'LSID=null', "\n", //Vienna RSS 'Auth=', $auth, "\n"; @@ -234,11 +234,11 @@ final class GReaderAPI { //http://blog.martindoms.com/2009/08/15/using-the-google-reader-api-part-1/ //https://github.com/ericmann/gReader-Library/blob/master/greader.class.php $user = Minz_User::name(); - if ($user === null || $conf === null || FreshRSS_Context::$system_conf === null) { + if ($user === null || $conf === null || !FreshRSS_Context::hasSystemConf()) { self::unauthorized(); } //Minz_Log::debug('token('. $user . ')', API_LOG); //TODO: Implement real token that expires - $token = str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z'); //Must have 57 characters + $token = str_pad(sha1(FreshRSS_Context::systemConf()->salt . $user . $conf->apiPasswordHash), 57, 'Z'); //Must have 57 characters echo $token, "\n"; exit(); } @@ -246,7 +246,7 @@ final class GReaderAPI { private static function checkToken(?FreshRSS_UserConfiguration $conf, string $token): bool { //http://code.google.com/p/google-reader-api/wiki/ActionToken $user = Minz_User::name(); - if ($user === null || $conf === null || FreshRSS_Context::$system_conf === null) { + if ($user === null || $conf === null || !FreshRSS_Context::hasSystemConf()) { self::unauthorized(); } if ($user !== Minz_User::INTERNAL_USER && ( //TODO: Check security consequences @@ -254,7 +254,7 @@ final class GReaderAPI { $token === 'x')) { //Reeder return true; } - if ($token === str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) { + if ($token === str_pad(sha1(FreshRSS_Context::systemConf()->salt . $user . $conf->apiPasswordHash), 57, 'Z')) { return true; } Minz_Log::warning('Invalid POST token: ' . $token, API_LOG); @@ -264,7 +264,7 @@ final class GReaderAPI { /** @return never */ private static function userInfo() { //https://github.com/theoldreader/api#user-info - if (FreshRSS_Context::$user_conf == null) { + if (!FreshRSS_Context::hasUserConf()) { self::unauthorized(); } $user = Minz_User::name(); @@ -272,7 +272,7 @@ final class GReaderAPI { 'userId' => $user, 'userName' => $user, 'userProfileId' => $user, - 'userEmail' => FreshRSS_Context::$user_conf->mail_login, + 'userEmail' => FreshRSS_Context::userConf()->mail_login, ), JSON_OPTIONS)); } @@ -340,11 +340,11 @@ final class GReaderAPI { /** @return never */ private static function subscriptionList() { - if (FreshRSS_Context::$system_conf == null) { + if (!FreshRSS_Context::hasSystemConf()) { self::internalServerError(); } header('Content-Type: application/json; charset=UTF-8'); - $salt = FreshRSS_Context::$system_conf->salt; + $salt = FreshRSS_Context::systemConf()->salt; $faviconsUrl = Minz_Url::display('/f.php?', '', true); $faviconsUrl = str_replace('/api/greader.php/reader/api/0/subscription', '', $faviconsUrl); //Security if base_url is not set properly $subscriptions = array(); @@ -1003,7 +1003,7 @@ final class GReaderAPI { //Minz_Log::debug('----------------------------------------------------------------', API_LOG); //Minz_Log::debug(debugInfo(), API_LOG); - if (FreshRSS_Context::$system_conf == null || !FreshRSS_Context::$system_conf->api_enabled) { + if (!FreshRSS_Context::hasSystemConf() || !FreshRSS_Context::systemConf()->api_enabled) { self::serviceUnavailable(); } elseif ($pathInfos[1] === 'check' && $pathInfos[2] === 'compatibility') { self::checkCompatibility(); @@ -1014,10 +1014,10 @@ final class GReaderAPI { if ($pathInfos[1] !== 'accounts') { self::authorizationToUser(); } - if (FreshRSS_Context::$user_conf != null) { - Minz_Translate::init(FreshRSS_Context::$user_conf->language); + if (FreshRSS_Context::hasUserConf()) { + Minz_Translate::init(FreshRSS_Context::userConf()->language); Minz_ExtensionManager::init(); - Minz_ExtensionManager::enableByList(FreshRSS_Context::$user_conf->extensions_enabled, 'user'); + Minz_ExtensionManager::enableByList(FreshRSS_Context::userConf()->extensions_enabled, 'user'); } else { Minz_Translate::init(); } @@ -1163,7 +1163,7 @@ final class GReaderAPI { // Always exits case 'edit-tag': //http://blog.martindoms.com/2010/01/20/using-the-google-reader-api-part-3/ $token = isset($_POST['T']) ? trim($_POST['T']) : ''; - self::checkToken(FreshRSS_Context::$user_conf, $token); + self::checkToken(FreshRSS_Context::userConf(), $token); $a = $_POST['a'] ?? ''; //Add: user/-/state/com.google/read user/-/state/com.google/starred $r = $_POST['r'] ?? ''; //Remove: user/-/state/com.google/read user/-/state/com.google/starred $e_ids = multiplePosts('i'); //item IDs @@ -1171,14 +1171,14 @@ final class GReaderAPI { // Always exits case 'rename-tag': //https://github.com/theoldreader/api $token = isset($_POST['T']) ? trim($_POST['T']) : ''; - self::checkToken(FreshRSS_Context::$user_conf, $token); + self::checkToken(FreshRSS_Context::userConf(), $token); $s = $_POST['s'] ?? ''; //user/-/label/Folder $dest = $_POST['dest'] ?? ''; //user/-/label/NewFolder self::renameTag($s, $dest); // Always exits case 'disable-tag': //https://github.com/theoldreader/api $token = isset($_POST['T']) ? trim($_POST['T']) : ''; - self::checkToken(FreshRSS_Context::$user_conf, $token); + self::checkToken(FreshRSS_Context::userConf(), $token); $s_s = multiplePosts('s'); foreach ($s_s as $s) { self::disableTag($s); //user/-/label/Folder @@ -1186,7 +1186,7 @@ final class GReaderAPI { // Always exits case 'mark-all-as-read': $token = isset($_POST['T']) ? trim($_POST['T']) : ''; - self::checkToken(FreshRSS_Context::$user_conf, $token); + self::checkToken(FreshRSS_Context::userConf(), $token); $streamId = trim($_POST['s'] ?? ''); $ts = trim($_POST['ts'] ?? '0'); //Older than timestamp in nanoseconds if (!ctype_digit($ts)) { @@ -1195,7 +1195,7 @@ final class GReaderAPI { self::markAllAsRead($streamId, $ts); // Always exits case 'token': - self::token(FreshRSS_Context::$user_conf); + self::token(FreshRSS_Context::userConf()); // Always exits case 'user-info': self::userInfo(); diff --git a/p/api/pshb.php b/p/api/pshb.php index d48ece0aa..b4d553430 100644 --- a/p/api/pshb.php +++ b/p/api/pshb.php @@ -11,11 +11,11 @@ header('X-Content-Type-Options: nosniff'); $ORIGINAL_INPUT = file_get_contents('php://input', false, null, 0, MAX_PAYLOAD) ?: ''; FreshRSS_Context::initSystem(); -if (FreshRSS_Context::$system_conf == null) { +if (!FreshRSS_Context::hasSystemConf()) { header('HTTP/1.1 500 Internal Server Error'); die('Invalid system init!'); } -FreshRSS_Context::$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!) +FreshRSS_Context::systemConf()->auth_type = 'none'; // avoid necessity to be logged in (not saved!) //Minz_Log::debug(print_r(array('_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, 'INPUT' => $ORIGINAL_INPUT), true), PSHB_LOG); @@ -126,12 +126,12 @@ foreach ($users as $userFilename) { try { FreshRSS_Context::initUser($username); - if (FreshRSS_Context::$user_conf == null || !FreshRSS_Context::$user_conf->enabled) { + if (!FreshRSS_Context::hasUserConf() || !FreshRSS_Context::userConf()->enabled) { Minz_Log::warning('FreshRSS skip disabled user ' . $username); continue; } - Minz_ExtensionManager::enableByList(FreshRSS_Context::$user_conf->extensions_enabled, 'user'); - Minz_Translate::reset(FreshRSS_Context::$user_conf->language); + Minz_ExtensionManager::enableByList(FreshRSS_Context::userConf()->extensions_enabled, 'user'); + Minz_Translate::reset(FreshRSS_Context::userConf()->language); [$updated_feeds, , $nb_new_articles] = FreshRSS_feed_Controller::actualizeFeeds(null, $self, null, $simplePie); if ($nb_new_articles > 0) { @@ -95,8 +95,8 @@ function sendNotFoundResponse() { die(); } -if (!isset($_GET['f']) || - !isset($_GET['t'])) { +if (!isset($_GET['f']) || !is_string($_GET['f']) || + !isset($_GET['t']) || !is_string($_GET['t'])) { sendBadRequestResponse('Query string is incomplete.'); } diff --git a/phpstan.neon b/phpstan.neon index e262ad984..2335f6ac9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,6 @@ parameters: # TODO: Increase rule-level https://phpstan.org/user-guide/rule-levels - level: 7 - treatPhpDocTypesAsCertain: false + level: 8 fileExtensions: - php - phtml @@ -36,6 +35,7 @@ parameters: - TMP_PATH - USERS_PATH reportMaybesInPropertyPhpDocTypes: false + treatPhpDocTypesAsCertain: false strictRules: allRules: false booleansInConditions: false # TODO pass @@ -43,14 +43,17 @@ parameters: disallowedConstructs: false disallowedLooseComparison: false matchingInheritedMethodNames: true - noVariableVariables: false # TODO pass + noVariableVariables: true numericOperandsInArithmeticOperators: true overwriteVariablesWithLoop: true requireParentConstructorCall: true strictCalls: true switchConditionsMatchingType: true uselessCast: true + ignoreErrors: + # - '#Only booleans are allowed in (a negated boolean|a ternary operator condition|an elseif condition|an if condition|&&|\|\|), (bool|false|int(<[0-9, max]+>)?|true|null|\|)+ given.*#' includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon - vendor/phpstan/phpstan-strict-rules/rules.neon + # - vendor/phpstan/phpstan/conf/bleedingEdge.neon diff --git a/tests/phpstan-next.txt b/tests/phpstan-next.txt index f1d6be340..8e28e66a8 100644 --- a/tests/phpstan-next.txt +++ b/tests/phpstan-next.txt @@ -1,73 +1,15 @@ -# List of files, which are not yet passing PHPStan level 8 https://phpstan.org/user-guide/rule-levels +# List of files, which are not yet passing PHPStan level 9 https://phpstan.org/user-guide/rule-levels # Used for automated tests to avoid regressions in files already passing that level. # Can be regenerated with something like: -# find . -type d -name 'vendor' -prune -o -name '*.php' -exec sh -c 'vendor/bin/phpstan analyse --level 8 --memory-limit 512M {} >/dev/null 2>/dev/null || echo {}' \; +# find . -type d -name 'vendor' -prune -o -name '*.php' -exec sh -c 'vendor/bin/phpstan analyse --level 9 --memory-limit 512M {} >/dev/null 2>/dev/null || echo {}' \; -./app/Controllers/apiController.php -./app/Controllers/authController.php -./app/Controllers/categoryController.php ./app/Controllers/configureController.php -./app/Controllers/feedController.php ./app/Controllers/importExportController.php -./app/Controllers/indexController.php -./app/Controllers/javascriptController.php -./app/Controllers/tagController.php -./app/Controllers/updateController.php -./app/Controllers/userController.php -./app/install.php -./app/layout/aside_configure.phtml -./app/layout/aside_feed.phtml -./app/layout/header.phtml -./app/layout/layout.phtml -./app/layout/nav_menu.phtml -./app/layout/simple.phtml -./app/Mailers/UserMailer.php -./app/Models/Auth.php -./app/Models/BooleanSearch.php -./app/Models/Context.php ./app/Models/DatabaseDAO.php -./app/Models/DatabaseDAOPGSQL.php ./app/Models/Entry.php ./app/Models/Feed.php -./app/Models/FeedDAO.php -./app/Models/FormAuth.php -./app/Models/StatsDAO.php ./app/Services/ImportService.php -./app/views/auth/index.phtml ./app/views/configure/archiving.phtml -./app/views/configure/display.phtml -./app/views/configure/integration.phtml -./app/views/configure/reading.phtml -./app/views/configure/shortcut.phtml -./app/views/configure/system.phtml -./app/views/feed/contentSelectorPreview.phtml -./app/views/helpers/category/update.phtml -./app/views/helpers/configure/query.phtml -./app/views/helpers/export/opml.phtml -./app/views/helpers/extension/configure.phtml -./app/views/helpers/extension/details.phtml ./app/views/helpers/feed/update.phtml -./app/views/helpers/index/normal/entry_bottom.phtml -./app/views/helpers/index/normal/entry_header.phtml -./app/views/helpers/javascript_vars.phtml -./app/views/helpers/stream-footer.phtml -./app/views/index/about.phtml -./app/views/index/global.phtml -./app/views/index/normal.phtml -./app/views/index/reader.phtml -./app/views/stats/idle.phtml -./app/views/subscription/index.phtml -./app/views/user/manage.phtml -./app/views/user/profile.phtml -./app/views/user/validateEmail.phtml -./cli/_cli.php -./cli/create-user.php -./cli/delete-user.php -./cli/do-install.php -./cli/i18n/I18nData.php -./cli/i18n/I18nValue.php -./cli/list-users.php -./cli/reconfigure.php -./cli/user-info.php -./lib/lib_install.php -./lib/Minz/ModelPdo.php +./lib/Minz/Helper.php +./lib/Minz/Request.php |
