From a80a5f48a16e7d232168a7aaa68e9a1804235ce1 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 18 Dec 2023 17:59:16 +0100 Subject: 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 --- app/Controllers/apiController.php | 10 +- app/Controllers/authController.php | 63 ++++---- app/Controllers/categoryController.php | 34 +++-- app/Controllers/configureController.php | 171 +++++++++++----------- app/Controllers/entryController.php | 2 +- app/Controllers/extensionController.php | 10 +- app/Controllers/feedController.php | 43 +++--- app/Controllers/importExportController.php | 8 +- app/Controllers/indexController.php | 16 +- app/Controllers/javascriptController.php | 11 +- app/Controllers/subscriptionController.php | 28 ++-- app/Controllers/tagController.php | 6 +- app/Controllers/updateController.php | 6 +- app/Controllers/userController.php | 61 ++++---- app/FreshRSS.php | 34 ++--- app/Mailers/UserMailer.php | 4 +- app/Models/AttributesTrait.php | 60 +++++--- app/Models/Auth.php | 38 ++--- app/Models/BooleanSearch.php | 12 +- app/Models/Category.php | 4 +- app/Models/CategoryDAO.php | 25 ++-- app/Models/Context.php | 82 ++++++++--- app/Models/DatabaseDAO.php | 4 +- app/Models/DatabaseDAOPGSQL.php | 4 +- app/Models/DatabaseDAOSQLite.php | 6 +- app/Models/Entry.php | 53 ++++--- app/Models/EntryDAO.php | 84 ++++++----- app/Models/EntryDAOSQLite.php | 2 +- app/Models/Factory.php | 12 +- app/Models/Feed.php | 74 +++++----- app/Models/FeedDAO.php | 16 +- app/Models/FilterAction.php | 2 +- app/Models/FilterActionsTrait.php | 18 +-- app/Models/FormAuth.php | 8 +- app/Models/StatsDAO.php | 2 +- app/Models/SystemConfiguration.php | 5 +- app/Models/TagDAO.php | 6 +- app/Models/Themes.php | 5 +- app/Models/UserConfiguration.php | 42 ++++-- app/Services/ImportService.php | 8 +- app/Utils/feverUtil.php | 5 +- app/actualize_script.php | 23 ++- app/install.php | 43 +++--- app/layout/aside_configure.phtml | 6 +- app/layout/aside_feed.phtml | 16 +- app/layout/header.phtml | 14 +- app/layout/layout.phtml | 14 +- app/layout/nav_menu.phtml | 21 +-- app/layout/simple.phtml | 8 +- app/views/auth/index.phtml | 28 ++-- app/views/category/update.phtml | 2 +- app/views/configure/archiving.phtml | 52 +++---- app/views/configure/display.phtml | 94 ++++++------ app/views/configure/integration.phtml | 5 +- app/views/configure/reading.phtml | 148 +++++++++---------- app/views/configure/shortcut.phtml | 2 +- app/views/configure/system.phtml | 40 ++--- app/views/feed/add.phtml | 2 +- app/views/feed/contentSelectorPreview.phtml | 2 +- app/views/helpers/category/update.phtml | 28 ++-- app/views/helpers/configure/query.phtml | 3 + app/views/helpers/export/opml.phtml | 10 +- app/views/helpers/extension/configure.phtml | 3 + app/views/helpers/extension/details.phtml | 3 + app/views/helpers/feed/update.phtml | 97 ++++++------ app/views/helpers/index/normal/entry_bottom.phtml | 18 +-- app/views/helpers/index/normal/entry_header.phtml | 23 +-- app/views/helpers/javascript_vars.phtml | 22 +-- app/views/helpers/stream-footer.phtml | 4 +- app/views/index/about.phtml | 4 +- app/views/index/global.phtml | 6 +- app/views/index/normal.phtml | 35 +++-- app/views/index/reader.phtml | 28 ++-- app/views/index/rss.phtml | 2 +- app/views/stats/idle.phtml | 6 +- app/views/subscription/index.phtml | 6 +- app/views/user/manage.phtml | 2 +- app/views/user/profile.phtml | 8 +- app/views/user/validateEmail.phtml | 4 +- cli/_cli.php | 9 +- cli/_update-or-create-user.php | 2 +- cli/actualize-user.php | 4 +- cli/create-user.php | 2 +- cli/db-optimize.php | 2 +- cli/delete-user.php | 6 +- cli/do-install.php | 4 +- cli/export-opml-for-user.php | 2 +- cli/export-sqlite-for-user.php | 2 +- cli/export-zip-for-user.php | 2 +- cli/i18n/I18nData.php | 2 +- cli/i18n/I18nValue.php | 2 +- cli/import-for-user.php | 2 +- cli/import-sqlite-for-user.php | 2 +- cli/list-users.php | 6 +- cli/reconfigure.php | 67 +++++++-- cli/user-info.php | 12 +- composer.json | 2 +- composer.lock | 107 ++++++++------ config-user.default.php | 5 +- config.default.php | 2 + lib/Minz/Configuration.php | 4 +- lib/Minz/Extension.php | 100 +++++++------ lib/Minz/ExtensionManager.php | 14 +- lib/Minz/Helper.php | 6 +- lib/Minz/ModelPdo.php | 2 +- lib/Minz/Request.php | 10 +- lib/Minz/Session.php | 2 +- lib/Minz/Translate.php | 7 +- lib/Minz/Url.php | 10 +- lib/Minz/View.php | 2 + lib/favicons.php | 5 +- lib/lib_install.php | 9 +- lib/lib_rss.php | 33 ++--- p/api/fever.php | 21 ++- p/api/greader.php | 46 +++--- p/api/pshb.php | 10 +- p/ext.php | 4 +- phpstan.neon | 9 +- tests/phpstan-next.txt | 66 +-------- 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 $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 */ + $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\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> $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 $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 $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 $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 */ + public function attributes(): array { + return $this->attributes; + } + /** - * @phpstan-return ($key is non-empty-string ? mixed : array) - * @return array|mixed|null + * @param non-empty-string $key + * @return array|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 $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|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|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 */ $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 */ $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 $feeds */ + /** @var array $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} $valuesTmp + * @param array{'name':string,'kind':int,'attributes'?:array|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} $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 $res */ + /** @var array $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 $res */ + /** @var array $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 $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 */ @@ -56,22 +54,43 @@ final class FreshRSS_Context { public static int $sinceHours = 0; 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 = <<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('#.*#s', '', $this->content); + return preg_replace('#.*#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}> */ 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('' . $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}|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 $titlesAsRead */ + /** @param array $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 $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}"; $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 $options + * @param array $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} $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 */ 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 $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|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|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|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 */ + /** @return array */ 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 $res */ + /** @var array $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|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 $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 $trusted_sources + * @property array> $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} $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,'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 $volatile + * @property array> $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) - * @return array|mixed|null + * @param non-empty-string $key + * @return array|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|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|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 @@ - + @@ -68,7 +68,7 @@
  • - disable_update) { ?> + disable_update) { ?>
  • @@ -76,7 +76,7 @@
  • - + 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 @@ 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'; ?>
  • @@ -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'; ?>
  • data-unread="nbNotRead() ?>"> @@ -124,11 +124,11 @@ ?>
  • simplify_over_n_feeds): + if ($f_active || $nbFeedsTotal < FreshRSS_Context::userConf()->simplify_over_n_feeds): ?>show_favicons) { ?>✇show_favicons) { ?>✇name() ?>
  • @@ -179,7 +179,7 @@
  • - reading_confirm ? 'confirm" disabled="disabled' : ''; ?> + reading_confirm ? 'confirm" disabled="disabled' : ''; ?>
  • - + @@ -94,10 +94,10 @@
  • - disable_update) { ?> + disable_update) { ?>
  • - + @@ -111,7 +111,7 @@ - + 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(); ?> - class="darkMode === 'no') ? '' : 'darkMode_' . FreshRSS_Context::$user_conf->darkMode ?>"> +?> class="darkMode === 'no') ? '' : 'darkMode_' . FreshRSS_Context::userConf()->darkMode ?>"> @@ -26,10 +26,10 @@ if (_t('gen.dir') === 'rtl') { - + -allow_referrer) { ?> +allow_referrer) { ?> @@ -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; } ?> @@ -50,7 +50,7 @@ if (_t('gen.dir') === 'rtl') { unset($opml_rss['params']['rid']); ?> -allow_robots) { ?> +allow_robots) { ?> 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 @@ - queries as $raw_query): ?> + queries as $raw_query): ?>
  • @@ -97,7 +97,7 @@ queries) > 0) { + if (count(FreshRSS_Context::userConf()->queries) > 0) { $classSeparator = ' separator'; } @@ -140,7 +140,7 @@ - + - allow_anonymous_refresh) { ?> + allow_anonymous_refresh) { ?>
    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(); ?> - + @@ -18,7 +18,7 @@ - + @@ -31,11 +31,11 @@
    - logo_html == '') { ?> + logo_html == '') { ?> logo_html; + echo FreshRSS_Context::systemConf()->logo_html; } ?> 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 @@
    - + auth_type, ['form', 'http_auth', 'none'], true)) { ?> - - - +
    @@ -32,9 +32,9 @@
    @@ -43,8 +43,8 @@
    @@ -54,8 +54,8 @@
    @@ -66,8 +66,8 @@
    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 @@
    - ()
    @@ -46,10 +46,10 @@
    @@ -59,18 +59,18 @@
    @@ -81,8 +81,8 @@
    @@ -92,8 +92,8 @@
    @@ -103,8 +103,8 @@
    @@ -114,8 +114,8 @@
    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 @@
    - - +
    @@ -28,15 +28,15 @@
    - timezone, $timezones, true)) { - FreshRSS_Context::$user_conf->timezone = ''; + if (!in_array(FreshRSS_Context::userConf()->timezone, $timezones, true)) { + FreshRSS_Context::userConf()->timezone = ''; } ?> - @@ -49,16 +49,16 @@
      themes); $i = 1; $themeAvailable = false; ?> - themes as $theme) { ?> - theme === $theme['id']) { + themes as $theme) { ?> + theme === $theme['id']) { $checked = 'checked="checked"'; $themeAvailable = true; } else { $checked = ''; } ?> value="" - data-leave-validation="theme === $theme['id']) ? 1 : 0 ?>" /> + data-leave-validation="theme === $theme['id']) ? 1 : 0 ?>" />
    • @@ -98,7 +98,7 @@
      -
      theme)?>
      +
      theme)?>
    • @@ -109,14 +109,14 @@
      - + +
      - content_width; ?> + content_width; ?>
      @@ -137,7 +137,7 @@
      - topline_website; ?> + topline_website; ?>
      @@ -158,7 +158,7 @@
      - topline_thumbnail; ?> + topline_thumbnail; ?>
      @@ -201,50 +201,50 @@ topline_read ? ' checked="checked"' : '' ?> - data-leave-validation="topline_read ?>" /> + FreshRSS_Context::userConf()->topline_read ? ' checked="checked"' : '' ?> + data-leave-validation="topline_read ?>" /> topline_favorite ? ' checked="checked"' : '' ?> - data-leave-validation="topline_favorite ?>" /> + FreshRSS_Context::userConf()->topline_favorite ? ' checked="checked"' : '' ?> + data-leave-validation="topline_favorite ?>" /> topline_summary ? 'checked="checked"' : '' ?> - data-leave-validation="topline_summary ?>" /> + FreshRSS_Context::userConf()->topline_summary ? 'checked="checked"' : '' ?> + data-leave-validation="topline_summary ?>" /> topline_display_authors ? ' checked="checked"' : '' ?> - data-leave-validation="topline_display_authors ?>" /> + FreshRSS_Context::userConf()->topline_display_authors ? ' checked="checked"' : '' ?> + data-leave-validation="topline_display_authors ?>" /> topline_date ? ' checked="checked"' : '' ?> - data-leave-validation="topline_date ?>" /> - topline_link ? ' checked="checked"' : '' ?> - data-leave-validation="topline_link ?>" /> + FreshRSS_Context::userConf()->topline_date ? ' checked="checked"' : '' ?> + data-leave-validation="topline_date ?>" /> + topline_link ? ' checked="checked"' : '' ?> + data-leave-validation="topline_link ?>" /> bottomline_read ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_read ?>" /> + FreshRSS_Context::userConf()->bottomline_read ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_read ?>" /> bottomline_favorite ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_favorite ?>" /> + FreshRSS_Context::userConf()->bottomline_favorite ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_favorite ?>" /> bottomline_myLabels ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_myLabels ?>" /> + FreshRSS_Context::userConf()->bottomline_myLabels ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_myLabels ?>" /> bottomline_tags ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_tags ?>" /> + FreshRSS_Context::userConf()->bottomline_tags ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_tags ?>" /> bottomline_sharing ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_sharing ?>" /> + FreshRSS_Context::userConf()->bottomline_sharing ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_sharing ?>" /> bottomline_date ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_date ?>" /> + FreshRSS_Context::userConf()->bottomline_date ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_date ?>" /> bottomline_link ? ' checked="checked"' : '' ?> - data-leave-validation="bottomline_link ?>" /> + FreshRSS_Context::userConf()->bottomline_link ? ' checked="checked"' : '' ?> + data-leave-validation="bottomline_link ?>" /> @@ -255,8 +255,8 @@
      + FreshRSS_Context::userConf()->html5_notif_timeout ?>" + data-leave-validation="html5_notif_timeout ?>" />
      @@ -264,8 +264,8 @@
      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 @@ 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); ?> 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 @@
      - + + +
      @@ -28,10 +28,10 @@
      - + + +
      @@ -40,8 +40,8 @@
      + FreshRSS_Context::userConf()->posts_per_page ?>" min="5" max="500" + data-leave-validation="posts_per_page ?>"/>

      @@ -50,8 +50,8 @@
      @@ -61,9 +61,9 @@
      - + +
      @@ -75,14 +75,14 @@
      -
      @@ -92,8 +92,8 @@
      @@ -104,8 +104,8 @@
      @@ -117,32 +117,32 @@
      - + + +
      - + + + +
      - + + + +
      @@ -150,7 +150,7 @@
      - +

      @@ -162,8 +162,8 @@
      @@ -174,8 +174,8 @@
      @@ -186,8 +186,8 @@
      @@ -201,8 +201,8 @@
      @@ -213,8 +213,8 @@
      @@ -226,8 +226,8 @@
      @@ -237,8 +237,8 @@
      @@ -249,8 +249,8 @@
      @@ -261,8 +261,8 @@
      @@ -273,8 +273,8 @@
      @@ -285,8 +285,8 @@
      @@ -297,10 +297,10 @@
      @@ -312,8 +312,8 @@
      @@ -324,8 +324,8 @@
      @@ -335,10 +335,10 @@
      @@ -351,7 +351,7 @@
      @@ -366,8 +366,8 @@
      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 @@ - shortcuts; ?> + shortcuts; ?>

      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 @@

      - +
      - +

      @@ -35,7 +35,7 @@
      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"' : '' ?> />

      @@ -44,32 +44,32 @@
      - +
      - +
      - +
      - +

      @@ -80,9 +80,9 @@
      @@ -91,8 +91,8 @@
      - + (= )(= )
      @@ -124,8 +124,8 @@ name="force-email-validation" id="force-email-validation" value="1" - force_email_validation ? 'checked="checked"' : '' ?> - data-leave-validation="force_email_validation ?>" + force_email_validation ? 'checked="checked"' : '' ?> + data-leave-validation="force_email_validation ?>" /> 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 @@ feed) { + if ($this->feed !== null) { ?>

      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(); ?> - + 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 @@ category === null) { + throw new FreshRSS_Context_Exception('Category not initialised!'); + } ?>

      @@ -28,7 +31,7 @@
      - +

      @@ -46,7 +49,7 @@
      - + @@ -90,7 +93,8 @@ 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\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; - } ?>

      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 @@ query === null) { + throw new FreshRSS_Context_Exception('Query not initialised!'); + } ?>

      query->getName() ?>

      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 */ - $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!'); + } ?>

      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 @@ 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 @@ feed === null) { + throw new FreshRSS_Context_Exception('Feed not initialised!'); + } ?>

      feed->name() ?>

      @@ -184,9 +187,9 @@
      @@ -195,12 +198,12 @@
      @@ -210,9 +213,9 @@
      @@ -222,9 +225,9 @@
      @@ -245,7 +248,10 @@
      - +
      @@ -267,7 +273,8 @@

      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\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 @@
      feed->attributes('xpath'))); + $xpath = Minz_Helper::htmlspecialchars_utf8($this->feed->attributeArray('xpath') ?? []); ?>

      @@ -521,7 +528,7 @@
      feed->attributes('path_entries_filter'))); + $path_entries_filter = Minz_Helper::htmlspecialchars_utf8($this->feed->attributeString('path_entries_filter') ?? ''); ?>
      @@ -537,13 +544,13 @@

      @@ -578,8 +585,8 @@

      @@ -590,8 +597,8 @@

      @@ -609,7 +616,7 @@
      - +
      @@ -617,9 +624,9 @@
      @@ -627,7 +634,7 @@
      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 @@ 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; ?>
        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 @@ 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; ?>
        • - show_favicons && 'name' !== $topline_website): ?>✇feed->name() ?> + show_favicons && 'name' !== $topline_website): ?>✇feed->name() ?>
        • 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 @@ 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 @@
          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 @@ environment; - if ($env !== 'production' && FreshRSS_Context::$user_conf->is_admin) { ?> + $env = FreshRSS_Context::systemConf()->environment; + if ($env !== 'production' && FreshRSS_Context::userConf()->is_admin) { ?>

          data/config.php

          'environment' => ''
          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 @@ ?>
        • - show_favicons): ?>✇ + show_favicons): ?>✇ name() ?>
        • @@ -82,6 +82,6 @@
          -
          display_posts ? '' : ' class="hide_posts"' ?>> +
          display_posts ? '' : ' class="hide_posts"' ?>>
          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="feed->priority() ?>">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'); ?>
          - show_feed_name === 't') { ?> + show_feed_name === 't') { ?> - show_tags === 'h' || FreshRSS_Context::$user_conf->show_tags === 'b') { ?> + show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') { ?>

          entry->title() ?>

          - show_author_date === 'h' || FreshRSS_Context::$user_conf->show_author_date === 'b') { ?> + show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?>
          - show_feed_name === 'a') { ?> + show_feed_name === 'a') { ?> @@ -164,8 +167,8 @@ $today = @strtotime('today'); echo $lazyload && $hidePosts ? lazyimg($this->entry->content(true)) : $this->entry->content(true); ?>
          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');
          - show_feed_name === 'a') { ?> + show_feed_name === 'a') { ?> @@ -266,4 +269,4 @@ $today = @strtotime('today'); - 0 && FreshRSS_Context::$user_conf->show_nav_buttons) $this->partial('nav_entries'); ?> + 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; ?>

          @@ -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; isRead() ? 'read' : 'unread') ?> isFavorite() ? 'starred' : 'non-starred') ?> - show_feed_name === 't') { ?> + show_feed_name === 't') { ?> - show_favicons): ?> + show_favicons): ?> ✇name() ?>
          - show_tags === 'h' || FreshRSS_Context::$user_conf->show_tags === 'b') { ?> + show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') { ?>
          show_tags_max;

          title() ?>

          - show_author_date === 'h' || FreshRSS_Context::$user_conf->show_author_date === 'b') { ?> + show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?>
          - show_feed_name === 'a') { ?> + show_feed_name === 'a') { ?> @@ -141,17 +141,17 @@ $MAX_TAGS_DISPLAYED = FreshRSS_Context::$user_conf->show_tags_max; content(true) ?>
          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) { ?>