diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index ccc32ec0d..937c0759d 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -27,10 +27,10 @@ class FreshRSS_auth_Controller extends Minz_ActionController { if (Minz_Request::isPost()) { $ok = true; - $current_token = FreshRSS_Context::$conf->token; + $current_token = FreshRSS_Context::$user_conf->token; $token = Minz_Request::param('token', $current_token); - FreshRSS_Context::$conf->_token($token); - $ok &= FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->token = $token; + $ok &= FreshRSS_Context::$user_conf->save(); $anon = Minz_Request::param('anon_access', false); $anon = ((bool)$anon) && ($anon !== 'no'); @@ -39,18 +39,20 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $auth_type = Minz_Request::param('auth_type', 'none'); $unsafe_autologin = Minz_Request::param('unsafe_autologin', false); $api_enabled = Minz_Request::param('api_enabled', false); - if ($anon != Minz_Configuration::allowAnonymous() || - $auth_type != Minz_Configuration::authType() || - $anon_refresh != Minz_Configuration::allowAnonymousRefresh() || - $unsafe_autologin != Minz_Configuration::unsafeAutologinEnabled() || - $api_enabled != Minz_Configuration::apiEnabled()) { + 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) { - Minz_Configuration::_authType($auth_type); - Minz_Configuration::_allowAnonymous($anon); - Minz_Configuration::_allowAnonymousRefresh($anon_refresh); - Minz_Configuration::_enableAutologin($unsafe_autologin); - Minz_Configuration::_enableApi($api_enabled); - $ok &= Minz_Configuration::writeFile(); + // 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(); } invalidateHttpCache(); @@ -76,7 +78,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } - $auth_type = Minz_Configuration::authType(); + $auth_type = FreshRSS_Context::$system_conf->auth_type; switch ($auth_type) { case 'form': Minz_Request::forward(array('c' => 'auth', 'a' => 'formLogin')); @@ -118,11 +120,9 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); $challenge = Minz_Request::param('challenge', ''); - try { - $conf = new FreshRSS_Configuration($username); - } catch(Minz_Exception $e) { - // $username is not a valid user, nor the configuration file! - Minz_Log::warning('Login failure: ' . $e->getMessage()); + + $conf = get_user_configuration($username); + if (is_null($conf)) { Minz_Request::bad(_t('feedback.auth.login.invalid'), array('c' => 'auth', 'a' => 'login')); } @@ -154,7 +154,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { Minz_Request::bad(_t('feedback.auth.login.invalid'), array('c' => 'auth', 'a' => 'login')); } - } elseif (Minz_Configuration::unsafeAutologinEnabled()) { + } elseif (FreshRSS_Context::$system_conf->unsafe_autologin_enabled) { $username = Minz_Request::param('u', ''); $password = Minz_Request::param('p', ''); Minz_Request::_param('p'); @@ -163,11 +163,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController { return; } - try { - $conf = new FreshRSS_Configuration($username); - } catch(Minz_Exception $e) { - // $username is not a valid user, nor the configuration file! - Minz_Log::warning('Login failure: ' . $e->getMessage()); + $conf = get_user_configuration($username); + if (is_null($conf)) { return; } @@ -235,13 +232,12 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $persona_file = DATA_PATH . '/persona/' . $email . '.txt'; if (($current_user = @file_get_contents($persona_file)) !== false) { $current_user = trim($current_user); - try { - $conf = new FreshRSS_Configuration($current_user); + $conf = get_user_configuration($current_user); + if (!is_null($conf)) { $login_ok = strcasecmp($email, $conf->mail_login) === 0; - } catch (Minz_Exception $e) { - //Permission denied or conf file does not exist + } else { $reason = 'Invalid configuration for user ' . - '[' . $current_user . '] ' . $e->getMessage(); + '[' . $current_user . ']'; } } } else { @@ -293,7 +289,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $this->view->no_form = false; // Enable changement of auth only if Persona! - if (Minz_Configuration::authType() != 'persona') { + if (FreshRSS_Context::$system_conf->auth_type != 'persona') { $this->view->message = array( 'status' => 'bad', 'title' => _t('gen.short.damn'), @@ -303,7 +299,11 @@ class FreshRSS_auth_Controller extends Minz_ActionController { return; } - $conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser()); + $conf = get_user_configuration(FreshRSS_Context::$system_conf->default_user); + if (is_null($conf)) { + return; + } + // Admin user must have set its master password. if (!$conf->passwordHash) { $this->view->message = array( @@ -327,8 +327,8 @@ class FreshRSS_auth_Controller extends Minz_ActionController { ); if ($ok) { - Minz_Configuration::_authType('form'); - $ok = Minz_Configuration::writeFile(); + FreshRSS_Context::$system_conf->auth_type = 'form'; + $ok = FreshRSS_Context::$system_conf->save(); if ($ok) { Minz_Request::good(_t('feedback.auth.form.set')); diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index 5f1beae90..e65c146de 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -30,7 +30,7 @@ class FreshRSS_category_Controller extends Minz_ActionController { $catDAO = new FreshRSS_CategoryDAO(); $url_redirect = array('c' => 'subscription', 'a' => 'index'); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->limits; $this->view->categories = $catDAO->listCategories(false); if (count($this->view->categories) >= $limits['max_categories']) { @@ -141,8 +141,9 @@ class FreshRSS_category_Controller extends Minz_ActionController { } // Remove related queries. - FreshRSS_Context::$conf->remove_query_by_get('c_' . $id); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->queries = remove_query_by_get( + 'c_' . $id, FreshRSS_Context::$user_conf->queries); + FreshRSS_Context::$user_conf->save(); Minz_Request::good(_t('feedback.sub.category.deleted'), $url_redirect); } @@ -177,9 +178,10 @@ class FreshRSS_category_Controller extends Minz_ActionController { // Remove related queries foreach ($feeds as $feed) { - FreshRSS_Context::$conf->remove_query_by_get('f_' . $feed->id()); + FreshRSS_Context::$user_conf->queries = remove_query_by_get( + 'f_' . $feed->id(), FreshRSS_Context::$user_conf->queries); } - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->save(); Minz_Request::good(_t('feedback.sub.category.emptied'), $url_redirect); } else { diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index feb5483fb..38ccd2b2d 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -41,24 +41,24 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function displayAction() { if (Minz_Request::isPost()) { - FreshRSS_Context::$conf->_language(Minz_Request::param('language', 'en')); - FreshRSS_Context::$conf->_theme(Minz_Request::param('theme', FreshRSS_Themes::$defaultTheme)); - FreshRSS_Context::$conf->_content_width(Minz_Request::param('content_width', 'thin')); - FreshRSS_Context::$conf->_topline_read(Minz_Request::param('topline_read', false)); - FreshRSS_Context::$conf->_topline_favorite(Minz_Request::param('topline_favorite', false)); - FreshRSS_Context::$conf->_topline_date(Minz_Request::param('topline_date', false)); - FreshRSS_Context::$conf->_topline_link(Minz_Request::param('topline_link', false)); - FreshRSS_Context::$conf->_bottomline_read(Minz_Request::param('bottomline_read', false)); - FreshRSS_Context::$conf->_bottomline_favorite(Minz_Request::param('bottomline_favorite', false)); - FreshRSS_Context::$conf->_bottomline_sharing(Minz_Request::param('bottomline_sharing', false)); - FreshRSS_Context::$conf->_bottomline_tags(Minz_Request::param('bottomline_tags', false)); - FreshRSS_Context::$conf->_bottomline_date(Minz_Request::param('bottomline_date', false)); - FreshRSS_Context::$conf->_bottomline_link(Minz_Request::param('bottomline_link', false)); - FreshRSS_Context::$conf->_html5_notif_timeout(Minz_Request::param('html5_notif_timeout', 0)); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->language = Minz_Request::param('language', 'en'); + FreshRSS_Context::$user_conf->theme = Minz_Request::param('theme', FreshRSS_Themes::$defaultTheme); + FreshRSS_Context::$user_conf->content_width = Minz_Request::param('content_width', 'thin'); + FreshRSS_Context::$user_conf->topline_read = Minz_Request::param('topline_read', false); + FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::param('topline_favorite', false); + FreshRSS_Context::$user_conf->topline_date = Minz_Request::param('topline_date', false); + FreshRSS_Context::$user_conf->topline_link = Minz_Request::param('topline_link', false); + FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::param('bottomline_read', false); + FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::param('bottomline_favorite', false); + FreshRSS_Context::$user_conf->bottomline_sharing = Minz_Request::param('bottomline_sharing', false); + FreshRSS_Context::$user_conf->bottomline_tags = Minz_Request::param('bottomline_tags', false); + FreshRSS_Context::$user_conf->bottomline_date = Minz_Request::param('bottomline_date', false); + FreshRSS_Context::$user_conf->bottomline_link = Minz_Request::param('bottomline_link', false); + FreshRSS_Context::$user_conf->html5_notif_timeout = Minz_Request::param('html5_notif_timeout', 0); + FreshRSS_Context::$user_conf->save(); - Minz_Session::_param('language', FreshRSS_Context::$conf->language); - Minz_Translate::reset(); + Minz_Session::_param('language', FreshRSS_Context::$user_conf->language); + Minz_Translate::reset(FreshRSS_Context::$user_conf->language); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), @@ -100,29 +100,26 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function readingAction() { if (Minz_Request::isPost()) { - FreshRSS_Context::$conf->_posts_per_page(Minz_Request::param('posts_per_page', 10)); - FreshRSS_Context::$conf->_view_mode(Minz_Request::param('view_mode', 'normal')); - FreshRSS_Context::$conf->_default_view(Minz_Request::param('default_view', 'adaptive')); - FreshRSS_Context::$conf->_auto_load_more(Minz_Request::param('auto_load_more', false)); - FreshRSS_Context::$conf->_display_posts(Minz_Request::param('display_posts', false)); - FreshRSS_Context::$conf->_display_categories(Minz_Request::param('display_categories', false)); - FreshRSS_Context::$conf->_hide_read_feeds(Minz_Request::param('hide_read_feeds', false)); - FreshRSS_Context::$conf->_onread_jump_next(Minz_Request::param('onread_jump_next', false)); - FreshRSS_Context::$conf->_lazyload(Minz_Request::param('lazyload', false)); - FreshRSS_Context::$conf->_sticky_post(Minz_Request::param('sticky_post', false)); - FreshRSS_Context::$conf->_reading_confirm(Minz_Request::param('reading_confirm', false)); - FreshRSS_Context::$conf->_auto_remove_article(Minz_Request::param('auto_remove_article', false)); - FreshRSS_Context::$conf->_sort_order(Minz_Request::param('sort_order', 'DESC')); - FreshRSS_Context::$conf->_mark_when(array( + FreshRSS_Context::$user_conf->posts_per_page = Minz_Request::param('posts_per_page', 10); + FreshRSS_Context::$user_conf->view_mode = Minz_Request::param('view_mode', 'normal'); + FreshRSS_Context::$user_conf->default_view = Minz_Request::param('default_view', 'adaptive'); + FreshRSS_Context::$user_conf->auto_load_more = Minz_Request::param('auto_load_more', false); + FreshRSS_Context::$user_conf->display_posts = Minz_Request::param('display_posts', false); + FreshRSS_Context::$user_conf->display_categories = Minz_Request::param('display_categories', false); + FreshRSS_Context::$user_conf->hide_read_feeds = Minz_Request::param('hide_read_feeds', false); + FreshRSS_Context::$user_conf->onread_jump_next = Minz_Request::param('onread_jump_next', false); + FreshRSS_Context::$user_conf->lazyload = Minz_Request::param('lazyload', false); + FreshRSS_Context::$user_conf->sticky_post = Minz_Request::param('sticky_post', false); + FreshRSS_Context::$user_conf->reading_confirm = Minz_Request::param('reading_confirm', false); + FreshRSS_Context::$user_conf->auto_remove_article = Minz_Request::param('auto_remove_article', false); + FreshRSS_Context::$user_conf->sort_order = Minz_Request::param('sort_order', 'DESC'); + FreshRSS_Context::$user_conf->mark_when = array( 'article' => Minz_Request::param('mark_open_article', false), 'site' => Minz_Request::param('mark_open_site', false), 'scroll' => Minz_Request::param('mark_scroll', false), 'reception' => Minz_Request::param('mark_upon_reception', false), - )); - FreshRSS_Context::$conf->save(); - - Minz_Session::_param('language', FreshRSS_Context::$conf->language); - Minz_Translate::reset(); + ); + FreshRSS_Context::$user_conf->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), @@ -142,8 +139,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function sharingAction() { if (Minz_Request::isPost()) { $params = Minz_Request::params(); - FreshRSS_Context::$conf->_sharing($params['share']); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->sharing = $params['share']; + FreshRSS_Context::$user_conf->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), @@ -184,8 +181,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } } - FreshRSS_Context::$conf->_shortcuts($shortcuts_ok); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->shortcuts = $shortcuts_ok; + FreshRSS_Context::$user_conf->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.shortcuts_updated'), @@ -212,10 +209,10 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function archivingAction() { if (Minz_Request::isPost()) { - FreshRSS_Context::$conf->_old_entries(Minz_Request::param('old_entries', 3)); - FreshRSS_Context::$conf->_keep_history_default(Minz_Request::param('keep_history_default', 0)); - FreshRSS_Context::$conf->_ttl_default(Minz_Request::param('ttl_default', -2)); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->old_entries = Minz_Request::param('old_entries', 3); + FreshRSS_Context::$user_conf->keep_history_default = Minz_Request::param('keep_history_default', 0); + FreshRSS_Context::$user_conf->ttl_default = Minz_Request::param('ttl_default', -2); + FreshRSS_Context::$user_conf->save(); invalidateHttpCache(); Minz_Request::good(_t('feedback.conf.updated'), @@ -252,8 +249,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $query['name'] = _t('conf.query.number', $key + 1); } } - FreshRSS_Context::$conf->_queries($queries); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->queries = $queries; + FreshRSS_Context::$user_conf->save(); Minz_Request::good(_t('feedback.conf.updated'), array('c' => 'configure', 'a' => 'queries')); @@ -261,7 +258,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->query_get = array(); $cat_dao = new FreshRSS_CategoryDAO(); $feed_dao = FreshRSS_Factory::createFeedDao(); - foreach (FreshRSS_Context::$conf->queries as $key => $query) { + foreach (FreshRSS_Context::$user_conf->queries as $key => $query) { if (!isset($query['get'])) { continue; } @@ -329,7 +326,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function addQueryAction() { $whitelist = array('get', 'order', 'name', 'search', 'state'); - $queries = FreshRSS_Context::$conf->queries; + $queries = FreshRSS_Context::$user_conf->queries; $query = Minz_Request::params(); $query['name'] = _t('conf.query.number', count($queries) + 1); foreach ($query as $key => $value) { @@ -338,8 +335,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } } $queries[] = $query; - FreshRSS_Context::$conf->_queries($queries); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->queries = $queries; + FreshRSS_Context::$user_conf->save(); Minz_Request::good(_t('feedback.conf.query_created', $query['name']), array('c' => 'configure', 'a' => 'queries')); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index aae08c413..1d9989f40 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -154,7 +154,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { public function purgeAction() { @set_time_limit(300); - $nb_month_old = max(FreshRSS_Context::$conf->old_entries, 1); + $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); $date_min = time() - (3600 * 24 * 30 * $nb_month_old); $feedDAO = FreshRSS_Factory::createFeedDao(); @@ -168,7 +168,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { if ($feed_history == -2) { // TODO: -2 must be a constant! // -2 means we take the default value from configuration - $feed_history = FreshRSS_Context::$conf->keep_history_default; + $feed_history = FreshRSS_Context::$user_conf->keep_history_default; } if ($feed_history >= 0) { diff --git a/app/Controllers/extensionController.php b/app/Controllers/extensionController.php index adb3e1864..3eedcd949 100644 --- a/app/Controllers/extensionController.php +++ b/app/Controllers/extensionController.php @@ -81,26 +81,25 @@ class FreshRSS_extension_Controller extends Minz_ActionController { $url_redirect); } + $conf = null; if ($ext->getType() === 'system' && FreshRSS_Auth::hasAccess('admin')) { - $ext->install(); - - Minz_Configuration::addExtension($ext_name); - Minz_Configuration::writeFile(); - - Minz_Request::good(_t('feedback.extensions.enabled', $ext_name), - $url_redirect); + $conf = FreshRSS_Context::$system_conf; } elseif ($ext->getType() === 'user') { - $ext->install(); - - FreshRSS_Context::$conf->addExtension($ext_name); - FreshRSS_Context::$conf->save(); - - Minz_Request::good(_t('feedback.extensions.enabled', $ext_name), - $url_redirect); + $conf = FreshRSS_Context::$user_conf; } else { Minz_Request::bad(_t('feedback.extensions.no_access', $ext_name), $url_redirect); } + + $ext->install(); + + $ext_list = $conf->extensions_enabled; + array_push_unique($ext_list, $ext_name); + $conf->extensions_enabled = $ext_list; + $conf->save(); + + Minz_Request::good(_t('feedback.extensions.enabled', $ext_name), + $url_redirect); } Minz_Request::forward($url_redirect, true); @@ -132,26 +131,25 @@ class FreshRSS_extension_Controller extends Minz_ActionController { $url_redirect); } + $conf = null; if ($ext->getType() === 'system' && FreshRSS_Auth::hasAccess('admin')) { - $ext->uninstall(); - - Minz_Configuration::removeExtension($ext_name); - Minz_Configuration::writeFile(); - - Minz_Request::good(_t('feedback.extensions.disabled', $ext_name), - $url_redirect); + $conf = FreshRSS_Context::$system_conf; } elseif ($ext->getType() === 'user') { - $ext->uninstall(); - - FreshRSS_Context::$conf->removeExtension($ext_name); - FreshRSS_Context::$conf->save(); - - Minz_Request::good(_t('feedback.extensions.disabled', $ext_name), - $url_redirect); + $conf = FreshRSS_Context::$user_conf; } else { Minz_Request::bad(_t('feedback.extensions.no_access', $ext_name), $url_redirect); } + + $ext->uninstall(); + + $ext_list = $conf->extensions_enabled; + array_remove($ext_list, $ext_name); + $conf->extensions_enabled = $ext_list; + $conf->save(); + + Minz_Request::good(_t('feedback.extensions.disabled', $ext_name), + $url_redirect); } Minz_Request::forward($url_redirect, true); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index cca1dad8f..6f544d834 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -14,12 +14,13 @@ class FreshRSS_feed_Controller extends Minz_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::$conf->token; + $token = FreshRSS_Context::$user_conf->token; $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token == $token_param); $action = Minz_Request::actionName(); + $allow_anonymous_refresh = FreshRSS_Context::$system_conf->allow_anonymous_refresh; if ($action !== 'actualize' || - !(Minz_Configuration::allowAnonymousRefresh() || $token_is_ok)) { + !($allow_anonymous_refresh || $token_is_ok)) { Minz_Error::error(403); } } @@ -65,7 +66,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { 'params' => array(), ); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->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']), @@ -168,14 +169,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed->_id($id); $feed->faviconPrepare(); - $is_read = FreshRSS_Context::$conf->mark_when['reception'] ? 1 : 0; + $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; $entryDAO = FreshRSS_Factory::createEntryDao(); // We want chronological order and SimplePie uses reverse order. $entries = array_reverse($feed->entries()); // Calculate date of oldest entries we accept in DB. - $nb_month_old = FreshRSS_Context::$conf->old_entries; + $nb_month_old = FreshRSS_Context::$user_conf->old_entries; $date_min = time() - (3600 * 24 * 30 * $nb_month_old); // Use a shared statement and a transaction to improve a LOT the @@ -286,15 +287,15 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feeds[] = $feed; } } else { - $feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$conf->ttl_default); + $feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); } // Calculate date of oldest entries we accept in DB. - $nb_month_old = max(FreshRSS_Context::$conf->old_entries, 1); + $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); $date_min = time() - (3600 * 24 * 30 * $nb_month_old); $updated_feeds = 0; - $is_read = FreshRSS_Context::$conf->mark_when['reception'] ? 1 : 0; + $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; foreach ($feeds as $feed) { if (!$feed->lock()) { Minz_Log::notice('Feed already being actualized: ' . $feed->url()); @@ -316,7 +317,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ($feed_history == -2) { // TODO: -2 must be a constant! // -2 means we take the default value from configuration - $feed_history = FreshRSS_Context::$conf->keep_history_default; + $feed_history = FreshRSS_Context::$user_conf->keep_history_default; } // We want chronological order and SimplePie uses reverse order. @@ -497,8 +498,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // TODO: Delete old favicon // Remove related queries - FreshRSS_Context::$conf->remove_query_by_get('f_' . $id); - FreshRSS_Context::$conf->save(); + FreshRSS_Context::$user_conf->queries = remove_query_by_get( + 'f_' . $id, FreshRSS_Context::$user_conf->queries); + FreshRSS_Context::$user_conf->save(); Minz_Request::good(_t('feedback.sub.feed.deleted'), $redirect_url); } else { diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 7b63b913d..db9db66a7 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -174,7 +174,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $nb_feeds = count($this->feedDAO->listFeeds()); $nb_cats = count($this->catDAO->listCategories(false)); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->limits; foreach ($opml_elements as $elt) { $is_error = false; @@ -321,7 +321,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { return true; } - $is_read = FreshRSS_Context::$conf->mark_when['reception'] ? 1 : 0; + $is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0; $google_compliant = strpos($article_object['id'], 'com.google') !== false; @@ -329,7 +329,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $article_to_feed = array(); $nb_feeds = count($this->feedDAO->listFeeds()); - $limits = Minz_Configuration::limits(); + $limits = FreshRSS_Context::$system_conf->limits; // First, we check feeds of articles are in DB (and add them if needed). foreach ($article_object['items'] as $item) { @@ -548,7 +548,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->view->type = 'feed/' . $feed->id(); $this->view->entries = $this->entryDAO->listWhere( 'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', - FreshRSS_Context::$conf->posts_per_page + FreshRSS_Context::$user_conf->posts_per_page ); $this->view->feed = $feed; } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 33cd2843c..c53d3223e 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -9,7 +9,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action only redirect on the default view mode (normal or global) */ public function indexAction() { - $prefered_output = FreshRSS_Context::$conf->view_mode; + $prefered_output = FreshRSS_Context::$user_conf->view_mode; Minz_Request::forward(array( 'c' => 'index', 'a' => $prefered_output @@ -20,7 +20,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the normal view of FreshRSS. */ public function normalAction() { - if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { + $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; + if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; } @@ -82,7 +83,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the global view of FreshRSS. */ public function globalAction() { - if (!FreshRSS_Auth::hasAccess() && !Minz_Configuration::allowAnonymous()) { + $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; + if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; } @@ -109,13 +111,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { * This action displays the RSS feed of FreshRSS. */ public function rssAction() { - $token = FreshRSS_Context::$conf->token; + $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; + $token = FreshRSS_Context::$user_conf->token; $token_param = Minz_Request::param('token', ''); $token_is_ok = ($token != '' && $token === $token_param); // Check if user has access. if (!FreshRSS_Auth::hasAccess() && - !Minz_Configuration::allowAnonymous() && + !$allow_anonymous && !$token_is_ok) { Minz_Error::error(403); } @@ -160,10 +163,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { FreshRSS_Context::_get(Minz_Request::param('get', 'a')); FreshRSS_Context::$state = Minz_Request::param( - 'state', FreshRSS_Context::$conf->default_state + 'state', FreshRSS_Context::$user_conf->default_state ); $state_forced_by_user = Minz_Request::param('state', false) !== false; - if (FreshRSS_Context::$conf->default_view === 'adaptive' && + if (FreshRSS_Context::$user_conf->default_view === 'adaptive' && FreshRSS_Context::$get_unread <= 0 && !FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_READ) && !$state_forced_by_user) { @@ -172,10 +175,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { FreshRSS_Context::$search = Minz_Request::param('search', ''); FreshRSS_Context::$order = Minz_Request::param( - 'order', FreshRSS_Context::$conf->sort_order + 'order', FreshRSS_Context::$user_conf->sort_order ); FreshRSS_Context::$number = Minz_Request::param( - 'nb', FreshRSS_Context::$conf->posts_per_page + 'nb', FreshRSS_Context::$user_conf->posts_per_page ); FreshRSS_Context::$first_id = Minz_Request::param('next', ''); } diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index 113f58ea9..421cf6f72 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -8,7 +8,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { public function actualizeAction() { header('Content-Type: text/javascript; charset=UTF-8'); $feedDAO = FreshRSS_Factory::createFeedDao(); - $this->view->feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$conf->ttl_default); + $this->view->feeds = $feedDAO->listFeedsOrderUpdate(FreshRSS_Context::$user_conf->ttl_default); } public function nbUnreadsPerFeedAction() { @@ -28,11 +28,12 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { $user = isset($_GET['user']) ? $_GET['user'] : ''; if (ctype_alnum($user)) { try { - $conf = new FreshRSS_Configuration($user); + $salt = FreshRSS_Context::$system_conf->salt; + $conf = get_user_configuration($user); $s = $conf->passwordHash; if (strlen($s) >= 60) { $this->view->salt1 = substr($s, 0, 29); //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". - $this->view->nonce = sha1(Minz_Configuration::salt() . uniqid(mt_rand(), true)); + $this->view->nonce = sha1($salt . uniqid(mt_rand(), true)); Minz_Session::_param('nonce', $this->view->nonce); return; //Success } diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 3b40e42dc..ed01b83c5 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -39,9 +39,9 @@ class FreshRSS_user_Controller extends Minz_ActionController { $passwordPlain = ''; $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js $ok &= ($passwordHash != ''); - FreshRSS_Context::$conf->_passwordHash($passwordHash); + FreshRSS_Context::$user_conf->passwordHash = $passwordHash; } - Minz_Session::_param('passwordHash', FreshRSS_Context::$conf->passwordHash); + Minz_Session::_param('passwordHash', FreshRSS_Context::$user_conf->passwordHash); $passwordPlain = Minz_Request::param('apiPasswordPlain', '', true); if ($passwordPlain != '') { @@ -52,17 +52,17 @@ class FreshRSS_user_Controller extends Minz_ActionController { $passwordPlain = ''; $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js $ok &= ($passwordHash != ''); - FreshRSS_Context::$conf->_apiPasswordHash($passwordHash); + FreshRSS_Context::$user_conf->apiPasswordHash = $passwordHash; } // TODO: why do we need of hasAccess here? if (FreshRSS_Auth::hasAccess('admin')) { - FreshRSS_Context::$conf->_mail_login(Minz_Request::param('mail_login', '', true)); + FreshRSS_Context::$user_conf->mail_login = Minz_Request::param('mail_login', '', true); } - $email = FreshRSS_Context::$conf->mail_login; + $email = FreshRSS_Context::$user_conf->mail_login; Minz_Session::_param('mail', $email); - $ok &= FreshRSS_Context::$conf->save(); + $ok &= FreshRSS_Context::$user_conf->save(); if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; @@ -105,27 +105,28 @@ class FreshRSS_user_Controller extends Minz_ActionController { public function createAction() { if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$conf->language); - if (!in_array($new_user_language, FreshRSS_Context::$conf->availableLanguages())) { - $new_user_language = FreshRSS_Context::$conf->language; + $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); + $languages = Minz_Translate::availableLanguages(); + if (!isset($languages[$new_user_language])) { + $new_user_language = FreshRSS_Context::$user_conf->language; } $new_user_name = Minz_Request::param('new_user_name'); $ok = ($new_user_name != '') && ctype_alnum($new_user_name); if ($ok) { - $ok &= (strcasecmp($new_user_name, Minz_Configuration::defaultUser()) !== 0); //It is forbidden to alter the default user + $default_user = FreshRSS_Context::$system_conf->default_user; + $ok &= (strcasecmp($new_user_name, $default_user) !== 0); //It is forbidden to alter the default user $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive - $configPath = DATA_PATH . '/' . $new_user_name . '_user.php'; + $configPath = join_path(DATA_PATH, 'users', $new_user_name, 'config.php'); $ok &= !file_exists($configPath); } if ($ok) { - $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); $passwordHash = ''; if ($passwordPlain != '') { @@ -147,12 +148,13 @@ class FreshRSS_user_Controller extends Minz_ActionController { if (empty($new_user_email)) { $new_user_email = ''; } else { - $personaFile = DATA_PATH . '/persona/' . $new_user_email . '.txt'; + $personaFile = join_path(DATA_PATH, 'persona', $new_user_email . '.txt'); @unlink($personaFile); $ok &= (file_put_contents($personaFile, $new_user_name) !== false); } } if ($ok) { + mkdir(join_path(DATA_PATH, 'users', $new_user_name)); $config_array = array( 'language' => $new_user_language, 'passwordHash' => $passwordHash, @@ -178,23 +180,24 @@ class FreshRSS_user_Controller extends Minz_ActionController { public function deleteAction() { if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); $username = Minz_Request::param('username'); $ok = ctype_alnum($username); + $user_data = join_path(DATA_PATH, 'users', $username); if ($ok) { - $ok &= (strcasecmp($username, Minz_Configuration::defaultUser()) !== 0); //It is forbidden to delete the default user + $default_user = FreshRSS_Context::$system_conf->default_user; + $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user } if ($ok) { - $configPath = DATA_PATH . '/' . $username . '_user.php'; - $ok &= file_exists($configPath); + $ok &= is_dir($user_data); } if ($ok) { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); - $ok &= unlink($configPath); + $ok &= recursive_unlink($user_data); //TODO: delete Persona file } invalidateHttpCache(); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 88fe60850..453cc2d69 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -8,11 +8,34 @@ class FreshRSS extends Minz_FrontController { // Load list of extensions and enable the "system" ones. Minz_ExtensionManager::init(); + $this->initConfiguration(); + $this->initAuth(); + FreshRSS_Context::init(); + $this->initI18n(); + FreshRSS_Share::load(join_path(DATA_PATH, 'shares.php')); + $this->loadStylesAndScripts(); + $this->loadNotifications(); + // Enable extensions for the current (logged) user. + if (FreshRSS_Auth::hasAccess()) { + $ext_list = FreshRSS_Context::$user_conf->extensions_enabled; + Minz_ExtensionManager::enable_by_list($ext_list); + } + } - // Need to be called just after session init because it initializes - // current user. + private function initConfiguration() { + $configuration_setter = new FreshRSS_ConfigurationSetter(); + $current_user = Minz_Session::param('currentUser', '_'); + + Minz_Configuration::register('user', + join_path(USERS_PATH, $current_user, 'config.php'), + join_path(USERS_PATH, '_', 'config.default.php'), + $configuration_setter); + $system_conf = Minz_Configuration::get('system'); + $system_conf->_configurationSetter($configuration_setter); + } + + private function initAuth() { FreshRSS_Auth::init(); - if (Minz_Request::isPost() && !is_referer_from_same_domain()) { // Basic protection against XSRF attacks FreshRSS_Auth::removeAccess(); @@ -25,26 +48,15 @@ class FreshRSS extends Minz_FrontController { )) ); } + } - // Load context and configuration. - FreshRSS_Context::init(); - - // Init i18n. - Minz_Session::_param('language', FreshRSS_Context::$conf->language); - Minz_Translate::init(); - - $this->loadStylesAndScripts(); - $this->loadNotifications(); - - // Enable extensions for the current (logged) user. - if (FreshRSS_Auth::hasAccess()) { - $ext_list = FreshRSS_Context::$conf->extensions_enabled; - Minz_ExtensionManager::enable_by_list($ext_list); - } + private function initI18n() { + Minz_Session::_param('language', FreshRSS_Context::$user_conf->language); + Minz_Translate::init(FreshRSS_Context::$user_conf->language); } private function loadStylesAndScripts() { - $theme = FreshRSS_Themes::load(FreshRSS_Context::$conf->theme); + $theme = FreshRSS_Themes::load(FreshRSS_Context::$user_conf->theme); if ($theme) { foreach($theme['files'] as $file) { if ($file[0] === '_') { @@ -65,7 +77,7 @@ class FreshRSS extends Minz_FrontController { Minz_View::appendScript(Minz_Url::display('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); Minz_View::appendScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); - if (Minz_Configuration::authType() === 'persona') { + if (FreshRSS_Context::$system_conf->auth_type === 'persona') { // TODO move it in a plugin // Needed for login AND logout with Persona. Minz_View::appendScript('https://login.persona.org/include.js'); diff --git a/app/Models/Auth.php b/app/Models/Auth.php index 2971d65c8..4e7a71947 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -16,7 +16,8 @@ class FreshRSS_Auth { self::$login_ok = Minz_Session::param('loginOk', false); $current_user = Minz_Session::param('currentUser', ''); if ($current_user === '') { - $current_user = Minz_Configuration::defaultUser(); + $conf = Minz_Configuration::get('system'); + $current_user = $conf->default_user; Minz_Session::_param('currentUser', $current_user); } @@ -40,7 +41,9 @@ class FreshRSS_Auth { * @return boolean true if user can be connected, false else. */ private static function accessControl() { - switch (Minz_Configuration::authType()) { + $conf = Minz_Configuration::get('system'); + $auth_type = $conf->auth_type; + switch ($auth_type) { case 'form': $credentials = FreshRSS_FormAuth::getCredentialsFromCookie(); $current_user = ''; @@ -80,21 +83,18 @@ class FreshRSS_Auth { */ public static function giveAccess() { $current_user = Minz_Session::param('currentUser'); - try { - $conf = new FreshRSS_Configuration($current_user); - } catch(Minz_Exception $e) { - die($e->getMessage()); - } + $user_conf = get_user_configuration($current_user); + $system_conf = Minz_Configuration::get('system'); - switch (Minz_Configuration::authType()) { + switch ($system_conf->auth_type) { case 'form': - self::$login_ok = Minz_Session::param('passwordHash') === $conf->passwordHash; + self::$login_ok = Minz_Session::param('passwordHash') === $user_conf->passwordHash; break; case 'http_auth': self::$login_ok = strcasecmp($current_user, httpAuthUser()) === 0; break; case 'persona': - self::$login_ok = strcasecmp(Minz_Session::param('mail'), $conf->mail_login) === 0; + self::$login_ok = strcasecmp(Minz_Session::param('mail'), $user_conf->mail_login) === 0; break; case 'none': self::$login_ok = true; @@ -114,12 +114,14 @@ class FreshRSS_Auth { * @return boolean true if user has corresponding access, false else. */ public static function hasAccess($scope = 'general') { + $conf = Minz_Configuration::get('system'); + $default_user = $conf->default_user; $ok = self::$login_ok; switch ($scope) { case 'general': break; case 'admin': - $ok &= Minz_Session::param('currentUser') === Minz_Configuration::defaultUser(); + $ok &= Minz_Session::param('currentUser') === $default_user; break; default: $ok = false; @@ -133,9 +135,10 @@ class FreshRSS_Auth { public static function removeAccess() { Minz_Session::_param('loginOk'); self::$login_ok = false; - Minz_Session::_param('currentUser', Minz_Configuration::defaultUser()); + $conf = Minz_Configuration::get('system'); + Minz_Session::_param('currentUser', $conf->default_user); - switch (Minz_Configuration::authType()) { + switch ($conf->auth_type) { case 'form': Minz_Session::_param('passwordHash'); FreshRSS_FormAuth::deleteCookie(); @@ -151,6 +154,24 @@ class FreshRSS_Auth { // TODO: extensions } } + + /** + * Return if authentication is enabled on this instance of FRSS. + */ + public static function accessNeedsLogin() { + $conf = Minz_Configuration::get('system'); + $auth_type = $conf->auth_type; + return $auth_type !== 'none'; + } + + /** + * Return if authentication requires a PHP action. + */ + public static function accessNeedsAction() { + $conf = Minz_Configuration::get('system'); + $auth_type = $conf->auth_type; + return $auth_type === 'form' || $auth_type === 'persona'; + } } @@ -194,7 +215,8 @@ class FreshRSS_FormAuth { public static function makeCookie($username, $password_hash) { do { - $token = sha1(Minz_Configuration::salt() . $username . uniqid(mt_rand(), true)); + $conf = Minz_Configuration::get('system'); + $token = sha1($conf->salt . $username . uniqid(mt_rand(), true)); $token_file = DATA_PATH . '/tokens/' . $token . '.txt'; } while (file_exists($token_file)); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php deleted file mode 100644 index 83a00d4bb..000000000 --- a/app/Models/Configuration.php +++ /dev/null @@ -1,365 +0,0 @@ - 'en', - 'old_entries' => 3, - 'keep_history_default' => 0, - 'ttl_default' => 3600, - 'mail_login' => '', - 'token' => '', - 'passwordHash' => '', //CRYPT_BLOWFISH - 'apiPasswordHash' => '', //CRYPT_BLOWFISH - 'posts_per_page' => 20, - 'view_mode' => 'normal', - 'default_view' => 'adaptive', - 'default_state' => FreshRSS_Entry::STATE_NOT_READ, - 'auto_load_more' => true, - 'display_posts' => false, - 'display_categories' => false, - 'hide_read_feeds' => true, - 'onread_jump_next' => true, - 'lazyload' => true, - 'sticky_post' => true, - 'reading_confirm' => false, - 'auto_remove_article' => false, - 'sort_order' => 'DESC', - 'anon_access' => false, - 'mark_when' => array( - 'article' => true, - 'site' => true, - 'scroll' => false, - 'reception' => false, - ), - 'theme' => 'Origine', - 'content_width' => 'thin', - 'shortcuts' => array( - 'mark_read' => 'r', - 'mark_favorite' => 'f', - 'go_website' => 'space', - 'next_entry' => 'j', - 'prev_entry' => 'k', - 'first_entry' => 'home', - 'last_entry' => 'end', - 'collapse_entry' => 'c', - 'load_more' => 'm', - 'auto_share' => 's', - 'focus_search' => 'a', - 'user_filter' => 'u', - 'help' => 'f1', - 'close_dropdown' => 'escape', - ), - 'topline_read' => true, - 'topline_favorite' => true, - 'topline_date' => true, - 'topline_link' => true, - 'bottomline_read' => true, - 'bottomline_favorite' => true, - 'bottomline_sharing' => true, - 'bottomline_tags' => true, - 'bottomline_date' => true, - 'bottomline_link' => true, - 'sharing' => array(), - 'queries' => array(), - 'html5_notif_timeout' => 0, - 'extensions_enabled' => array(), - ); - - private $available_languages = array( - 'en' => 'English', - 'fr' => 'Français', - ); - - private $shares; - - public function __construct($user) { - $this->filename = DATA_PATH . DIRECTORY_SEPARATOR . $user . '_user.php'; - - $data = @include($this->filename); - if (!is_array($data)) { - throw new Minz_PermissionDeniedException($this->filename); - } - - foreach ($data as $key => $value) { - if (isset($this->data[$key])) { - $function = '_' . $key; - $this->$function($value); - } - } - $this->data['user'] = $user; - - $this->shares = DATA_PATH . DIRECTORY_SEPARATOR . 'shares.php'; - - $shares = @include($this->shares); - if (!is_array($shares)) { - throw new Minz_PermissionDeniedException($this->shares); - } - - $this->data['shares'] = $shares; - } - - public function save() { - @rename($this->filename, $this->filename . '.bak.php'); - unset($this->data['shares']); // Remove shares because it is not intended to be stored in user configuration - if (file_put_contents($this->filename, "data, true) . ';', LOCK_EX) === false) { - throw new Minz_PermissionDeniedException($this->filename); - } - if (function_exists('opcache_invalidate')) { - opcache_invalidate($this->filename); //Clear PHP 5.5+ cache for include - } - invalidateHttpCache(); - return true; - } - - public function __get($name) { - if (array_key_exists($name, $this->data)) { - return $this->data[$name]; - } else { - $trace = debug_backtrace(); - trigger_error('Undefined FreshRSS_Configuration->' . $name . 'in ' . $trace[0]['file'] . ' line ' . $trace[0]['line'], E_USER_NOTICE); //TODO: Use Minz exceptions - return null; - } - } - - public function availableLanguages() { - return $this->available_languages; - } - - public function remove_query_by_get($get) { - $final_queries = array(); - foreach ($this->queries as $key => $query) { - if (empty($query['get']) || $query['get'] !== $get) { - $final_queries[$key] = $query; - } - } - $this->_queries($final_queries); - } - - public function _language($value) { - if (!isset($this->available_languages[$value])) { - $value = 'en'; - } - $this->data['language'] = $value; - } - public function _posts_per_page($value) { - $value = intval($value); - $this->data['posts_per_page'] = $value > 0 ? $value : 10; - } - public function _view_mode($value) { - if ($value === 'global' || $value === 'reader') { - $this->data['view_mode'] = $value; - } else { - $this->data['view_mode'] = 'normal'; - } - } - public function _default_view($value) { - switch ($value) { - case 'all': - $this->data['default_view'] = $value; - $this->data['default_state'] = (FreshRSS_Entry::STATE_READ + - FreshRSS_Entry::STATE_NOT_READ); - break; - case 'adaptive': - case 'unread': - default: - $this->data['default_view'] = $value; - $this->data['default_state'] = FreshRSS_Entry::STATE_NOT_READ; - } - } - public function _default_state($value) { - $this->data['default_state'] = (int)$value; - } - - public function _display_posts($value) { - $this->data['display_posts'] = ((bool)$value) && $value !== 'no'; - } - public function _display_categories($value) { - $this->data['display_categories'] = ((bool)$value) && $value !== 'no'; - } - public function _hide_read_feeds($value) { - $this->data['hide_read_feeds'] = (bool)$value; - } - public function _onread_jump_next($value) { - $this->data['onread_jump_next'] = ((bool)$value) && $value !== 'no'; - } - public function _lazyload($value) { - $this->data['lazyload'] = ((bool)$value) && $value !== 'no'; - } - public function _sticky_post($value) { - $this->data['sticky_post'] = ((bool)$value) && $value !== 'no'; - } - public function _reading_confirm($value) { - $this->data['reading_confirm'] = ((bool)$value) && $value !== 'no'; - } - public function _auto_remove_article($value) { - $this->data['auto_remove_article'] = ((bool)$value) && $value !== 'no'; - } - public function _sort_order($value) { - $this->data['sort_order'] = $value === 'ASC' ? 'ASC' : 'DESC'; - } - public function _old_entries($value) { - $value = intval($value); - $this->data['old_entries'] = $value > 0 ? $value : 3; - } - public function _keep_history_default($value) { - $value = intval($value); - $this->data['keep_history_default'] = $value >= -1 ? $value : 0; - } - public function _ttl_default($value) { - $value = intval($value); - $this->data['ttl_default'] = $value >= -1 ? $value : 3600; - } - public function _shortcuts($values) { - foreach ($values as $key => $value) { - if (isset($this->data['shortcuts'][$key])) { - $this->data['shortcuts'][$key] = $value; - } - } - } - public function _passwordHash($value) { - $this->data['passwordHash'] = ctype_graph($value) && (strlen($value) >= 60) ? $value : ''; - } - public function _apiPasswordHash($value) { - $this->data['apiPasswordHash'] = ctype_graph($value) && (strlen($value) >= 60) ? $value : ''; - } - public function _mail_login($value) { - $value = filter_var($value, FILTER_VALIDATE_EMAIL); - if ($value) { - $this->data['mail_login'] = $value; - } else { - $this->data['mail_login'] = ''; - } - } - public function _anon_access($value) { - $this->data['anon_access'] = ((bool)$value) && $value !== 'no'; - } - public function _mark_when($values) { - foreach ($values as $key => $value) { - if (isset($this->data['mark_when'][$key])) { - $this->data['mark_when'][$key] = ((bool)$value) && $value !== 'no'; - } - } - } - public function _sharing($values) { - $this->data['sharing'] = array(); - $unique = array(); - foreach ($values as $value) { - if (!is_array($value)) { - continue; - } - - // Verify URL and add default value when needed - if (isset($value['url'])) { - $is_url = ( - filter_var($value['url'], FILTER_VALIDATE_URL) || - (version_compare(PHP_VERSION, '5.3.3', '<') && - (strpos($value, '-') > 0) && - ($value === filter_var($value, FILTER_SANITIZE_URL))) - ); //PHP bug #51192 - if (!$is_url) { - continue; - } - } else { - $value['url'] = null; - } - - // Add a default name - if (empty($value['name'])) { - $value['name'] = $value['type']; - } - - $json_value = json_encode($value); - if (!in_array($json_value, $unique)) { - $unique[] = $json_value; - $this->data['sharing'][] = $value; - } - } - } - public function _queries($values) { - $this->data['queries'] = array(); - foreach ($values as $value) { - $value = array_filter($value); - $params = $value; - unset($params['name']); - unset($params['url']); - $value['url'] = Minz_Url::display(array('params' => $params)); - - $this->data['queries'][] = $value; - } - } - public function _theme($value) { - $this->data['theme'] = $value; - } - public function _content_width($value) { - if ($value === 'medium' || - $value === 'large' || - $value === 'no_limit') { - $this->data['content_width'] = $value; - } else { - $this->data['content_width'] = 'thin'; - } - } - - public function _html5_notif_timeout($value) { - $value = intval($value); - $this->data['html5_notif_timeout'] = $value >= 0 ? $value : 0; - } - - public function _token($value) { - $this->data['token'] = $value; - } - public function _auto_load_more($value) { - $this->data['auto_load_more'] = ((bool)$value) && $value !== 'no'; - } - public function _topline_read($value) { - $this->data['topline_read'] = ((bool)$value) && $value !== 'no'; - } - public function _topline_favorite($value) { - $this->data['topline_favorite'] = ((bool)$value) && $value !== 'no'; - } - public function _topline_date($value) { - $this->data['topline_date'] = ((bool)$value) && $value !== 'no'; - } - public function _topline_link($value) { - $this->data['topline_link'] = ((bool)$value) && $value !== 'no'; - } - public function _bottomline_read($value) { - $this->data['bottomline_read'] = ((bool)$value) && $value !== 'no'; - } - public function _bottomline_favorite($value) { - $this->data['bottomline_favorite'] = ((bool)$value) && $value !== 'no'; - } - public function _bottomline_sharing($value) { - $this->data['bottomline_sharing'] = ((bool)$value) && $value !== 'no'; - } - public function _bottomline_tags($value) { - $this->data['bottomline_tags'] = ((bool)$value) && $value !== 'no'; - } - public function _bottomline_date($value) { - $this->data['bottomline_date'] = ((bool)$value) && $value !== 'no'; - } - public function _bottomline_link($value) { - $this->data['bottomline_link'] = ((bool)$value) && $value !== 'no'; - } - - public function _extensions_enabled($value) { - if (!is_array($value)) { - $value = array($value); - } - $this->data['extensions_enabled'] = $value; - } - public function removeExtension($ext_name) { - $this->data['extensions_enabled'] = array_diff( - $this->data['extensions_enabled'], - array($ext_name) - ); - } - public function addExtension($ext_name) { - $found = array_search($ext_name, $this->data['extensions_enabled']) !== false; - if (!$found) { - $this->data['extensions_enabled'][] = $ext_name; - } - } -} diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php new file mode 100644 index 000000000..9830fed28 --- /dev/null +++ b/app/Models/ConfigurationSetter.php @@ -0,0 +1,368 @@ += 60) ? $value : ''; + } + + private function _content_width(&$data, $value) { + $value = strtolower($value); + if (!in_array($value, array('thin', 'medium', 'large', 'no_limit'))) { + $value = 'thin'; + } + + $data['content_width'] = $value; + } + + private function _default_state(&$data, $value) { + $data['default_state'] = (int)$value; + } + + private function _default_view(&$data, $value) { + switch ($value) { + case 'all': + $data['default_view'] = $value; + $data['default_state'] = (FreshRSS_Entry::STATE_READ + + FreshRSS_Entry::STATE_NOT_READ); + break; + case 'adaptive': + case 'unread': + default: + $data['default_view'] = $value; + $data['default_state'] = FreshRSS_Entry::STATE_NOT_READ; + } + } + + private function _html5_notif_timeout(&$data, $value) { + $value = intval($value); + $data['html5_notif_timeout'] = $value >= 0 ? $value : 0; + } + + private function _keep_history_default(&$data, $value) { + $value = intval($value); + $data['keep_history_default'] = $value >= -1 ? $value : 0; + } + + // It works for system config too! + private function _language(&$data, $value) { + $value = strtolower($value); + $languages = Minz_Translate::availableLanguages(); + if (!isset($languages[$value])) { + $value = 'en'; + } + $data['language'] = $value; + } + + private function _mail_login(&$data, $value) { + $value = filter_var($value, FILTER_VALIDATE_EMAIL); + $data['mail_login'] = $value ? $value : ''; + } + + private function _old_entries(&$data, $value) { + $value = intval($value); + $data['old_entries'] = $value > 0 ? $value : 3; + } + + private function _passwordHash(&$data, $value) { + $data['passwordHash'] = ctype_graph($value) && (strlen($value) >= 60) ? $value : ''; + } + + private function _posts_per_page(&$data, $value) { + $value = intval($value); + $data['posts_per_page'] = $value > 0 ? $value : 10; + } + + private function _queries(&$data, $values) { + $data['queries'] = array(); + foreach ($values as $value) { + $value = array_filter($value); + $params = $value; + unset($params['name']); + unset($params['url']); + $value['url'] = Minz_Url::display(array('params' => $params)); + $data['queries'][] = $value; + } + } + + private function _sharing(&$data, $values) { + $data['sharing'] = array(); + foreach ($values as $value) { + if (!is_array($value)) { + continue; + } + + // Verify URL and add default value when needed + if (isset($value['url'])) { + $is_url = ( + filter_var($value['url'], FILTER_VALIDATE_URL) || + (version_compare(PHP_VERSION, '5.3.3', '<') && + (strpos($value, '-') > 0) && + ($value === filter_var($value, FILTER_SANITIZE_URL))) + ); //PHP bug #51192 + if (!$is_url) { + continue; + } + } else { + $value['url'] = null; + } + + $data['sharing'][] = $value; + } + } + + private function _shortcuts(&$data, $values) { + foreach ($values as $key => $value) { + if (isset($data['shortcuts'][$key])) { + $data['shortcuts'][$key] = $value; + } + } + } + + private function _sort_order(&$data, $value) { + $data['sort_order'] = $value === 'ASC' ? 'ASC' : 'DESC'; + } + + private function _ttl_default(&$data, $value) { + $value = intval($value); + $data['ttl_default'] = $value >= -1 ? $value : 3600; + } + + private function _view_mode(&$data, $value) { + $value = strtolower($value); + if (!in_array($value, array('global', 'normal', 'reader'))) { + $value = 'normal'; + } + $data['view_mode'] = $value; + } + + /** + * A list of boolean setters. + */ + private function _anon_access(&$data, $value) { + $data['anon_access'] = $this->handleBool($value); + } + + private function _auto_load_more(&$data, $value) { + $data['auto_load_more'] = $this->handleBool($value); + } + + private function _auto_remove_article(&$data, $value) { + $data['auto_remove_article'] = $this->handleBool($value); + } + + private function _display_categories(&$data, $value) { + $data['display_categories'] = $this->handleBool($value); + } + + private function _display_posts(&$data, $value) { + $data['display_posts'] = $this->handleBool($value); + } + + private function _hide_read_feeds(&$data, $value) { + $data['hide_read_feeds'] = $this->handleBool($value); + } + + private function _lazyload(&$data, $value) { + $data['lazyload'] = $this->handleBool($value); + } + + private function _mark_when(&$data, $values) { + foreach ($values as $key => $value) { + if (isset($data['mark_when'][$key])) { + $data['mark_when'][$key] = $this->handleBool($value); + } + } + } + + private function _onread_jump_next(&$data, $value) { + $data['onread_jump_next'] = $this->handleBool($value); + } + + private function _reading_confirm(&$data, $value) { + $data['reading_confirm'] = $this->handleBool($value); + } + + private function _sticky_post(&$data, $value) { + $data['sticky_post'] = $this->handleBool($value); + } + + private function _bottomline_date(&$data, $value) { + $data['bottomline_date'] = $this->handleBool($value); + } + private function _bottomline_favorite(&$data, $value) { + $data['bottomline_favorite'] = $this->handleBool($value); + } + private function _bottomline_link(&$data, $value) { + $data['bottomline_link'] = $this->handleBool($value); + } + private function _bottomline_read(&$data, $value) { + $data['bottomline_read'] = $this->handleBool($value); + } + private function _bottomline_sharing(&$data, $value) { + $data['bottomline_sharing'] = $this->handleBool($value); + } + private function _bottomline_tags(&$data, $value) { + $data['bottomline_tags'] = $this->handleBool($value); + } + + private function _topline_date(&$data, $value) { + $data['topline_date'] = $this->handleBool($value); + } + private function _topline_favorite(&$data, $value) { + $data['topline_favorite'] = $this->handleBool($value); + } + private function _topline_link(&$data, $value) { + $data['topline_link'] = $this->handleBool($value); + } + private function _topline_read(&$data, $value) { + $data['topline_read'] = $this->handleBool($value); + } + + /** + * The (not so long) list of setters for system configuration. + */ + private function _allow_anonymous(&$data, $value) { + $data['allow_anonymous'] = $this->handleBool($value) && FreshRSS_Auth::accessNeedsAction(); + } + + private function _allow_anonymous_refresh(&$data, $value) { + $data['allow_anonymous_refresh'] = $this->handleBool($value) && $data['allow_anonymous']; + } + + private function _api_enabled(&$data, $value) { + $data['api_enabled'] = $this->handleBool($value); + } + + private function _auth_type(&$data, $value) { + $value = strtolower($value); + if (!in_array($value, array('form', 'http_auth', 'persona', 'none'))) { + $value = 'none'; + } + $data['auth_type'] = $value; + $this->_allow_anonymous($data, $data['allow_anonymous']); + } + + private function _db(&$data, $value) { + if (!isset($value['type'])) { + return; + } + + switch ($value['type']) { + case 'mysql': + if (empty($value['host']) || + empty($value['user']) || + empty($value['base']) || + !isset($value['password'])) { + return; + } + + $data['db']['type'] = $value['type']; + $data['db']['host'] = $value['host']; + $data['db']['user'] = $value['user']; + $data['db']['base'] = $value['base']; + $data['db']['password'] = $value['password']; + $data['db']['prefix'] = isset($value['prefix']) ? $value['prefix'] : ''; + break; + case 'sqlite': + $data['db']['type'] = $value['type']; + $data['db']['host'] = ''; + $data['db']['user'] = ''; + $data['db']['base'] = ''; + $data['db']['password'] = ''; + $data['db']['prefix'] = ''; + break; + default: + return; + } + } + + private function _default_user(&$data, $value) { + $user_list = listUsers(); + if (in_array($value, $user_list)) { + $data['default_user'] = $value; + } + } + + private function _environment(&$data, $value) { + $value = strtolower($value); + if (!in_array($value, array('silent', 'development', 'production'))) { + $value = 'production'; + } + $data['environment'] = $value; + } + + private function _limits(&$data, $values) { + $max_small_int = 16384; + $limits_keys = array( + 'cache_duration' => array( + 'min' => 0, + ), + 'timeout' => array( + 'min' => 0, + ), + 'max_inactivity' => array( + 'min' => 0, + ), + 'max_feeds' => array( + 'min' => 0, + 'max' => $max_small_int, + ), + 'max_categories' => array( + 'min' => 0, + 'max' => $max_small_int, + ), + ); + + foreach ($values as $key => $value) { + if (!isset($limits_keys[$key])) { + continue; + } + + $limits = $limits_keys[$key]; + if ( + (!isset($limits['min']) || $value > $limits['min']) && + (!isset($limits['max']) || $value < $limits['max']) + ) { + $data['limits'][$key] = $value; + } + } + } + + private function _unsafe_autologin_enabled(&$data, $value) { + $data['unsafe_autologin_enabled'] = $this->handleBool($value); + } +} diff --git a/app/Models/Context.php b/app/Models/Context.php index c8a65063a..1c770c756 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -5,7 +5,8 @@ * useful functions associated to the current view state. */ class FreshRSS_Context { - public static $conf = null; + public static $user_conf = null; + public static $system_conf = null; public static $categories = array(); public static $name = ''; @@ -37,17 +38,12 @@ class FreshRSS_Context { /** * Initialize the context. * - * Set the correct $conf and $categories variables. + * Set the correct configurations and $categories variables. */ public static function init() { // Init configuration. - $current_user = Minz_Session::param('currentUser'); - try { - self::$conf = new FreshRSS_Configuration($current_user); - } catch(Minz_Exception $e) { - Minz_Log::error('Cannot load configuration file of user `' . $current_user . '`'); - die($e->getMessage()); - } + self::$system_conf = Minz_Configuration::get('system'); + self::$user_conf = Minz_Configuration::get('user'); $catDAO = new FreshRSS_CategoryDAO(); self::$categories = $catDAO->listCategories(); @@ -198,7 +194,7 @@ class FreshRSS_Context { // By default, $next_get == $get self::$next_get = $get; - if (self::$conf->onread_jump_next && strlen($get) > 2) { + if (self::$user_conf->onread_jump_next && strlen($get) > 2) { $another_unread_id = ''; $found_current_get = false; switch ($get[0]) { @@ -276,7 +272,7 @@ class FreshRSS_Context { * @return boolean */ public static function isAutoRemoveAvailable() { - if (!self::$conf->auto_remove_article) { + if (!self::$user_conf->auto_remove_article) { return false; } if (self::isStateEnabled(FreshRSS_Entry::STATE_READ)) { @@ -297,7 +293,7 @@ class FreshRSS_Context { * @return boolean */ public static function isStickyPostEnabled() { - if (self::$conf->sticky_post) { + if (self::$user_conf->sticky_post) { return true; } if (self::isAutoRemoveAvailable()) { diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 4d06ac028..61beeea13 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -586,7 +586,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { } public function size($all = false) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL $values = array($db['base']); if (!$all) { diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index bb1539e0c..ffe0f037c 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -169,6 +169,6 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } public function size($all = false) { - return @filesize(DATA_PATH . '/' . $this->current_user . '.sqlite'); + return @filesize(join_path(DATA_PATH, 'users', $this->current_user, 'db.sqlite')); } } diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 91cb84998..db09d155d 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -3,8 +3,8 @@ class FreshRSS_Factory { public static function createFeedDao($username = null) { - $db = Minz_Configuration::dataBase(); - if ($db['type'] === 'sqlite') { + $conf = Minz_Configuration::get('system'); + if ($conf->db['type'] === 'sqlite') { return new FreshRSS_FeedDAOSQLite($username); } else { return new FreshRSS_FeedDAO($username); @@ -12,8 +12,8 @@ class FreshRSS_Factory { } public static function createEntryDao($username = null) { - $db = Minz_Configuration::dataBase(); - if ($db['type'] === 'sqlite') { + $conf = Minz_Configuration::get('system'); + if ($conf->db['type'] === 'sqlite') { return new FreshRSS_EntryDAOSQLite($username); } else { return new FreshRSS_EntryDAO($username); @@ -21,8 +21,8 @@ class FreshRSS_Factory { } public static function createStatsDAO($username = null) { - $db = Minz_Configuration::dataBase(); - if ($db['type'] === 'sqlite') { + $conf = Minz_Configuration::get('system'); + if ($conf->db['type'] === 'sqlite') { return new FreshRSS_StatsDAOSQLite($username); } else { return new FreshRSS_StatsDAO($username); @@ -30,8 +30,8 @@ class FreshRSS_Factory { } public static function createDatabaseDAO($username = null) { - $db = Minz_Configuration::dataBase(); - if ($db['type'] === 'sqlite') { + $conf = Minz_Configuration::get('system'); + if ($conf->db['type'] === 'sqlite') { return new FreshRSS_DatabaseDAOSQLite($username); } else { return new FreshRSS_DatabaseDAO($username); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 8f4b60097..86cbb783e 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -40,7 +40,8 @@ class FreshRSS_Feed extends Minz_Model { public function hash() { if ($this->hash === null) { - $this->hash = hash('crc32b', Minz_Configuration::salt() . $this->url); + $salt = FreshRSS_Context::$system_conf->salt; + $this->hash = hash('crc32b', $salt . $this->url); } return $this->hash; } diff --git a/app/Models/LogDAO.php b/app/Models/LogDAO.php index 21593435d..4c56e3150 100644 --- a/app/Models/LogDAO.php +++ b/app/Models/LogDAO.php @@ -3,7 +3,7 @@ class FreshRSS_LogDAO { public static function lines() { $logs = array(); - $handle = @fopen(LOG_PATH . '/' . Minz_Session::param('currentUser', '_') . '.log', 'r'); + $handle = @fopen(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt'), 'r'); if ($handle) { while (($line = fgets($handle)) !== false) { if (preg_match('/^\[([^\[]+)\] \[([^\[]+)\] --- (.*)$/', $line, $matches)) { @@ -20,6 +20,6 @@ class FreshRSS_LogDAO { } public static function truncate() { - file_put_contents(LOG_PATH . '/' . Minz_Session::param('currentUser', '_') . '.log', ''); + file_put_contents(join_path(DATA_PATH, 'users', Minz_Session::param('currentUser', '_'), 'log.txt'), ''); } } diff --git a/app/Models/Share.php b/app/Models/Share.php index b146db722..db6feda19 100644 --- a/app/Models/Share.php +++ b/app/Models/Share.php @@ -1,44 +1,240 @@ $share_options) { + $share_options['type'] = $share_type; + self::register($share_options); + } + } + + /** + * Return the list of sharing options. + * @return an array of FreshRSS_Share objects. + */ + public static function enum() { + return self::$list_sharing; + } + + /** + * Return FreshRSS_Share object related to the given type. + * @param $type the share type, null if $type is not registered. + */ + public static function get($type) { + if (!isset(self::$list_sharing[$type])) { + return null; + } + + return self::$list_sharing[$type]; + } + + /** + * + */ + private $type = ''; + private $name = ''; + private $url_transform = ''; + private $transform = array(); + private $form_type = 'simple'; + private $help_url = ''; + private $custom_name = null; + private $base_url = null; + private $title = null; + private $link = null; + + /** + * Create a FreshRSS_Share object. + * @param $type is a unique string defining the kind of share option. + * @param $url_transform defines the url format to use in order to share. + * @param $transform is an array of transformations to apply on link and title. + * @param $form_type defines which form we have to use to complete. "simple" + * is typically for a centralized service while "advanced" is for + * decentralized ones. + * @param $help_url is an optional url to give help on this option. + */ + private function __construct($type, $url_transform, $transform = array(), + $form_type, $help_url = '') { + $this->type = $type; + $this->name = _t('gen.share.' . $type); + $this->url_transform = $url_transform; + $this->help_url = $help_url; + + if (!is_array($transform)) { + $transform = array(); + } + $this->transform = $transform; + + if (!in_array($form_type, array('simple', 'advanced'))) { + $form_type = 'simple'; + } + $this->form_type = $form_type; + } + + /** + * Update a FreshRSS_Share object with information from an array. + * @param $options is a list of informations to update where keys should be + * in this list: name, url, title, link. + */ + public function update($options) { + $available_options = array( + 'name' => 'custom_name', + 'url' => 'base_url', + 'title' => 'title', + 'link' => 'link', + ); + + foreach ($options as $key => $value) { + if (!isset($available_options[$key])) { + continue; + } + + $this->$available_options[$key] = $value; + } + } + + /** + * Return the current type of the share option. + */ + public function type() { + return $this->type; + } + + /** + * Return the current form type of the share option. + */ + public function formType() { + return $this->form_type; + } + + /** + * Return the current help url of the share option. + */ + public function help() { + return $this->help_url; + } + + /** + * Return the current name of the share option. + */ + public function name($real = false) { + if ($real || is_null($this->custom_name)) { + return $this->name; + } else { + return $this->custom_name; + } + } + + /** + * Return the current base url of the share option. + */ + public function baseUrl() { + return $this->base_url; + } + + /** + * Return the current url by merging url_transform and base_url. + */ + public function url() { $matches = array( '~URL~', '~TITLE~', '~LINK~', ); $replaces = array( - $selected['url'], - self::transformData($title, self::getTransform($share, 'title')), - self::transformData($link, self::getTransform($share, 'link')), + $this->base_url, + $this->title(), + $this->link(), ); - $url = str_replace($matches, $replaces, $share['url']); - return $url; + return str_replace($matches, $replaces, $this->url_transform); } - static private function transformData($data, $transform) { - if (!is_array($transform)) { - return $data; - } - if (count($transform) === 0) { + /** + * Return the title. + * @param $raw true if we should get the title without transformations. + */ + public function title($raw = false) { + if ($raw) { + return $this->title; + } + + return $this->transform($this->title, $this->getTransform('title')); + } + + /** + * Return the link. + * @param $raw true if we should get the link without transformations. + */ + public function link($raw = false) { + if ($raw) { + return $this->link; + } + + return $this->transform($this->link, $this->getTransform('link')); + } + + /** + * Transform a data with the given functions. + * @param $data the data to transform. + * @param $tranform an array containing a list of functions to apply. + * @return the transformed data. + */ + private static function transform($data, $transform) { + if (!is_array($transform) || empty($transform)) { return $data; } + foreach ($transform as $action) { $data = call_user_func($action, $data); } + return $data; } - static private function getTransform($options, $type) { - $transform = $options['transform']; - - if (array_key_exists($type, $transform)) { - return $transform[$type]; + /** + * Get the list of transformations for the given attribute. + * @param $attr the attribute of which we want the transformations. + * @return an array containing a list of transformations to apply. + */ + private function getTransform($attr) { + if (array_key_exists($attr, $this->transform)) { + return $this->transform[$attr]; } - return $transform; + return $this->transform; } - } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index f04ae26bf..b55766ab4 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -2,7 +2,7 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { public function createUser($username) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); $userPDO = new Minz_ModelPdo($username); @@ -34,11 +34,11 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } public function deleteUser($username) { - $db = Minz_Configuration::dataBase(); + $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); if ($db['type'] === 'sqlite') { - return unlink(DATA_PATH . '/' . $username . '.sqlite'); + return unlink(join_path(DATA_PATH, 'users', $username, 'db.sqlite')); } else { $userPDO = new Minz_ModelPdo($username); @@ -55,14 +55,14 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } public static function exist($username) { - return file_exists(DATA_PATH . '/' . $username . '_user.php'); + return is_dir(join_path(DATA_PATH , 'users', $username)); } public static function touch($username) { - return touch(DATA_PATH . '/' . $username . '_user.php'); + return touch(join_path(DATA_PATH , 'users', $username, 'config.php')); } public static function mtime($username) { - return @filemtime(DATA_PATH . '/' . $username . '_user.php'); + return @filemtime(join_path(DATA_PATH , 'users', $username, 'config.php')); } } diff --git a/app/actualize_script.php b/app/actualize_script.php index 6ce4178cd..c7959be82 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -12,58 +12,63 @@ if (defined('STDOUT')) { fwrite(STDOUT, 'Starting feed actualization at ' . $begin_date->format('c') . "\n"); //Unbuffered } -Minz_Configuration::init(); +// Set the header params ($_GET) to call the FRSS application. +$_GET['c'] = 'feed'; +$_GET['a'] = 'actualize'; +$_GET['ajax'] = 1; +$_GET['force'] = true; +$_SERVER['HTTP_HOST'] = ''; + + +$app = new FreshRSS(); +$app->init(); + +$system_conf = Minz_Configuration::get('system'); +$system_conf->auth_type = 'none'; // avoid necessity to be logged in (not saved!) + +// Create the list of users to actualize. +// Users are processed in a random order but always start with admin $users = listUsers(); -shuffle($users); //Process users in random order - -if (Minz_Configuration::defaultUser() !== ''){ - array_unshift($users, Minz_Configuration::defaultUser()); //But always start with admin +shuffle($users); +if ($system_conf->default_user !== ''){ + array_unshift($users, $system_conf->default_user); $users = array_unique($users); } -$limits = Minz_Configuration::limits(); -$minLastActivity = time() - $limits['max_inactivity']; -foreach ($users as $myUser) { - if (($myUser !== Minz_Configuration::defaultUser()) && (FreshRSS_UserDAO::mtime($myUser) < $minLastActivity)) { - syslog(LOG_INFO, 'FreshRSS skip inactive user ' . $myUser); +$limits = $system_conf->limits; +$min_last_activity = time() - $limits['max_inactivity']; +foreach ($users as $user) { + if (($user !== $system_conf->default_user) && + (FreshRSS_UserDAO::mtime($user) < $min_last_activity)) { + syslog(LOG_INFO, 'FreshRSS skip inactive user ' . $user); if (defined('STDOUT')) { - fwrite(STDOUT, 'FreshRSS skip inactive user ' . $myUser . "\n"); //Unbuffered + fwrite(STDOUT, 'FreshRSS skip inactive user ' . $user . "\n"); //Unbuffered } continue; } - syslog(LOG_INFO, 'FreshRSS actualize ' . $myUser); + syslog(LOG_INFO, 'FreshRSS actualize ' . $user); if (defined('STDOUT')) { - fwrite(STDOUT, 'Actualize ' . $myUser . "...\n"); //Unbuffered + fwrite(STDOUT, 'Actualize ' . $user . "...\n"); //Unbuffered } - echo $myUser, ' '; //Buffered + echo $user, ' '; //Buffered - $_GET['c'] = 'feed'; - $_GET['a'] = 'actualize'; - $_GET['ajax'] = 1; - $_GET['force'] = true; - $_SERVER['HTTP_HOST'] = ''; - $freshRSS = new FreshRSS(); + Minz_Session::_param('currentUser', $user); + FreshRSS_Auth::giveAccess(); + $app->run(); - Minz_Configuration::_authType('none'); - - Minz_Session::init('FreshRSS'); - Minz_Session::_param('currentUser', $myUser); - - $freshRSS->init(); - $freshRSS->run(); if (!invalidateHttpCache()) { - syslog(LOG_NOTICE, 'FreshRSS write access problem in ' . LOG_PATH . '/*.log!'); + syslog(LOG_NOTICE, 'FreshRSS write access problem in ' . join_path(USERS_PATH, $user, 'log.txt')); if (defined('STDERR')) { - fwrite(STDERR, 'Write access problem in ' . LOG_PATH . '/*.log!' . "\n"); + fwrite(STDERR, 'Write access problem in ' . join_path(USERS_PATH, $user, 'log.txt') . "\n"); } } - Minz_Session::unset_session(true); - Minz_ModelPdo::clean(); } + + syslog(LOG_INFO, 'FreshRSS actualize done.'); if (defined('STDOUT')) { fwrite(STDOUT, 'Done.' . "\n"); diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php index 4eeccff32..83c06bfb4 100644 --- a/app/i18n/en/admin.php +++ b/app/i18n/en/admin.php @@ -63,10 +63,6 @@ return array( 'nok' => 'You lack JSON (php5-json package).', 'ok' => 'You have JSON extension.', ), - 'logs' => array( - 'nok' => 'Check permissions on ./data/logs directory. HTTP server must have rights to write into', - 'ok' => 'Permissions on logs directory are good.', - ), 'minz' => array( 'nok' => 'You lack the Minz framework.', 'ok' => 'You have the Minz framework.', @@ -97,6 +93,10 @@ return array( 'nok' => 'Check permissions on ./data/tokens directory. HTTP server must have rights to write into', 'ok' => 'Permissions on tokens directory are good.', ), + 'users' => array( + 'nok' => 'Check permissions on ./data/users directory. HTTP server must have rights to write into', + 'ok' => 'Permissions on users directory are good.', + ), 'zip' => array( 'nok' => 'You lack ZIP extension (php5-zip package).', 'ok' => 'You have ZIP extension.', diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index 80d37509e..b3b8d8647 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -129,6 +129,17 @@ return array( 'nothing_to_load' => 'There are no more articles', 'previous' => 'Previous', ), + 'share' => array( + 'blogotext' => 'Blogotext', + 'diaspora' => 'Diaspora*', + 'email' => 'Email', + 'facebook' => 'Facebook', + 'g+' => 'Google+', + 'print' => 'Print', + 'shaarli' => 'Shaarli', + 'twitter' => 'Twitter', + 'wallabag' => 'wallabag', + ), 'short' => array( 'attention' => 'Attention!', 'blank_to_disable' => 'Leave blank to disable', diff --git a/app/i18n/en/index.php b/app/i18n/en/index.php index 941388b68..8e7d81db8 100644 --- a/app/i18n/en/index.php +++ b/app/i18n/en/index.php @@ -55,18 +55,7 @@ return array( 'subscription' => 'Subscriptions management', 'unread' => 'Show only unread', ), - 'share' => array( - '_' => 'Share', - 'blogotext' => 'Blogotext', - 'diaspora' => 'Diaspora*', - 'email' => 'Email', - 'facebook' => 'Facebook', - 'g+' => 'Google+', - 'print' => 'Print', - 'shaarli' => 'Shaarli', - 'twitter' => 'Twitter', - 'wallabag' => 'wallabag', - ), + 'share' => 'Share', 'tag' => array( 'related' => 'Related tags', ), diff --git a/app/i18n/en/install.php b/app/i18n/en/install.php index ed3210190..e8073e8b6 100644 --- a/app/i18n/en/install.php +++ b/app/i18n/en/install.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'finish' => 'Complete installation', + 'fix_errors_before' => 'Fix errors before skip to the next step.', 'next_step' => 'Go to the next step', ), 'auth' => array( @@ -57,10 +58,6 @@ return array( 'nok' => 'Please check that you are not altering your HTTP REFERER.', 'ok' => 'Your HTTP REFERER is known and corresponds to your server.', ), - 'logs' => array( - 'nok' => 'Check permissions on ./data/logs directory. HTTP server must have rights to write into', - 'ok' => 'Permissions on logs directory are good.', - ), 'minz' => array( 'nok' => 'You lack the Minz framework.', 'ok' => 'You have the Minz framework.', @@ -81,6 +78,10 @@ return array( 'nok' => 'Your PHP version is %s but FreshRSS requires at least version %s.', 'ok' => 'Your PHP version is %s, which is compatible with FreshRSS.', ), + 'users' => array( + 'nok' => 'Check permissions on ./data/users directory. HTTP server must have rights to write into', + 'ok' => 'Permissions on users directory are good.', + ), ), 'conf' => array( '_' => 'General configuration', diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php index 6cf49c3f9..b16e2be54 100644 --- a/app/i18n/fr/admin.php +++ b/app/i18n/fr/admin.php @@ -63,10 +63,6 @@ return array( 'nok' => 'Vous ne disposez pas de JSON (paquet php5-json).', 'ok' => 'Vous disposez de l\'extension JSON.', ), - 'logs' => array( - 'nok' => 'Veuillez vérifier les droits sur le répertoire ./data/logs. Le serveur HTTP doit être capable d’écrire dedans', - 'ok' => 'Les droits sur le répertoire des logs sont bons.', - ), 'minz' => array( 'nok' => 'Vous ne disposez pas de la librairie Minz.', 'ok' => 'Vous disposez du framework Minz', @@ -97,6 +93,10 @@ return array( 'nok' => 'Veuillez vérifier les droits sur le répertoire ./data/tokens. Le serveur HTTP doit être capable d’écrire dedans', 'ok' => 'Les droits sur le répertoire des tokens sont bons.', ), + 'users' => array( + 'nok' => 'Veuillez vérifier les droits sur le répertoire ./data/users. Le serveur HTTP doit être capable d’écrire dedans', + 'ok' => 'Les droits sur le répertoire des utilisateurs sont bons.', + ), 'zip' => array( 'nok' => 'Vous ne disposez pas de l\'extension ZIP (paquet php5-zip).', 'ok' => 'Vous disposez de l\'extension ZIP.', diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index 62d6342e9..e91aeb66a 100644 --- a/app/i18n/fr/conf.php +++ b/app/i18n/fr/conf.php @@ -121,7 +121,7 @@ return array( '_' => 'Partage', 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', - 'email' => 'Email', + 'email' => 'Courriel', 'facebook' => 'Facebook', 'g+' => 'Google+', 'more_information' => 'Plus d’informations', diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index 9987dba30..996c81cf5 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -129,6 +129,17 @@ return array( 'nothing_to_load' => 'Fin des articles', 'previous' => 'Précédent', ), + 'share' => array( + 'blogotext' => 'Blogotext', + 'diaspora' => 'Diaspora*', + 'email' => 'Courriel', + 'facebook' => 'Facebook', + 'g+' => 'Google+', + 'print' => 'Imprimer', + 'shaarli' => 'Shaarli', + 'twitter' => 'Twitter', + 'wallabag' => 'wallabag', + ), 'short' => array( 'attention' => 'Attention !', 'blank_to_disable' => 'Laissez vide pour désactiver', diff --git a/app/i18n/fr/index.php b/app/i18n/fr/index.php index 3279543bd..f9975c593 100644 --- a/app/i18n/fr/index.php +++ b/app/i18n/fr/index.php @@ -55,18 +55,7 @@ return array( 'subscription' => 'Gestion des abonnements', 'unread' => 'Afficher les non lus', ), - 'share' => array( - '_' => 'Partager', - 'blogotext' => 'Blogotext', - 'diaspora' => 'Diaspora*', - 'email' => 'Courriel', - 'facebook' => 'Facebook', - 'g+' => 'Google+', - 'print' => 'Imprimer', - 'shaarli' => 'Shaarli', - 'twitter' => 'Twitter', - 'wallabag' => 'wallabag', - ), + 'share' => 'Partager', 'tag' => array( 'related' => 'Tags associés', ), diff --git a/app/i18n/fr/install.php b/app/i18n/fr/install.php index f1ef2ea3f..d1b78ffb6 100644 --- a/app/i18n/fr/install.php +++ b/app/i18n/fr/install.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'finish' => 'Terminer l’installation', + 'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à l’étape suivante.', 'next_step' => 'Passer à l’étape suivante', ), 'auth' => array( @@ -57,10 +58,6 @@ return array( 'nok' => 'Veuillez vérifier que vous ne modifiez pas votre HTTP REFERER.', 'ok' => 'Le HTTP REFERER est connu et semble correspondre à votre serveur.', ), - 'logs' => array( - 'nok' => 'Veuillez vérifier les droits sur le répertoire ./data/logs. Le serveur HTTP doit être capable d’écrire dedans', - 'ok' => 'Les droits sur le répertoire des logs sont bons.', - ), 'minz' => array( 'nok' => 'Vous ne disposez pas de la librairie Minz.', 'ok' => 'Vous disposez du framework Minz', @@ -81,6 +78,10 @@ return array( 'nok' => 'Votre version de PHP est la %s mais FreshRSS requiert au moins la version %s.', 'ok' => 'Votre version de PHP est la %s, qui est compatible avec FreshRSS.', ), + 'users' => array( + 'nok' => 'Veuillez vérifier les droits sur le répertoire ./data/users. Le serveur HTTP doit être capable d’écrire dedans', + 'ok' => 'Les droits sur le répertoire des utilisateurs sont bons.', + ), ), 'conf' => array( '_' => 'Configuration générale', diff --git a/app/install.php b/app/install.php index 8091a9eb0..55817ec68 100644 --- a/app/install.php +++ b/app/install.php @@ -43,30 +43,26 @@ function param($key, $default = false) { // gestion internationalisation function initTranslate() { - if (!isset($_SESSION['language'])) { - $_SESSION['language'] = getBetterLanguage('en'); - } - - Minz_Translate::init(); -} - -function getBetterLanguage($fallback) { - $available = availableLanguages(); - $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE']; - $language = strtolower(substr($accept, 0, 2)); - - if (isset($available[$language])) { - return $language; - } else { - return $fallback; - } -} - -function availableLanguages() { - return array( + $available_languages = array( 'en' => 'English', 'fr' => 'Français' ); + + if (!isset($_SESSION['language'])) { + $best = get_best_language(); + if (!isset($available_languages[$best])) { + $best = 'en'; + } + + $_SESSION['language'] = $best; + } + + Minz_Translate::init($_SESSION['language']); +} + +function get_best_language() { + $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE']; + return strtolower(substr($accept, 0, 2)); } @@ -132,12 +128,17 @@ function saveStep2() { 'token' => $token, ); - $configPath = DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php'; - @unlink($configPath); //To avoid access-rights problems - file_put_contents($configPath, " array( - 'environment' => empty($_SESSION['environment']) ? 'production' : $_SESSION['environment'], - 'salt' => $_SESSION['salt'], - 'base_url' => '', - 'title' => $_SESSION['title'], - 'default_user' => $_SESSION['default_user'], - 'allow_anonymous' => isset($_SESSION['allow_anonymous']) ? $_SESSION['allow_anonymous'] : false, - 'allow_anonymous_refresh' => isset($_SESSION['allow_anonymous_refresh']) ? $_SESSION['allow_anonymous_refresh'] : false, - 'auth_type' => $_SESSION['auth_type'], - 'api_enabled' => isset($_SESSION['api_enabled']) ? $_SESSION['api_enabled'] : false, - 'unsafe_autologin_enabled' => isset($_SESSION['unsafe_autologin_enabled']) ? $_SESSION['unsafe_autologin_enabled'] : false, - ), + $config_array = array( + 'environment' => 'production', + 'salt' => $_SESSION['salt'], + 'title' => $_SESSION['title'], + 'default_user' => $_SESSION['default_user'], + 'auth_type' => $_SESSION['auth_type'], 'db' => array( 'type' => $_SESSION['bd_type'], 'host' => $_SESSION['bd_host'], @@ -193,8 +187,8 @@ function saveStep3() { ), ); - @unlink(DATA_PATH . '/config.php'); //To avoid access-rights problems - file_put_contents(DATA_PATH . '/config.php', " PDO::ERRMODE_EXCEPTION, ); @@ -229,7 +223,7 @@ function newPdo() { } function deleteInstall() { - $res = unlink(DATA_PATH . '/do-install.txt'); + $res = unlink(join_path(DATA_PATH, 'do-install.txt')); if (!$res) { return false; @@ -258,7 +252,7 @@ function checkStep() { } function checkStep0() { - $languages = availableLanguages(); + $languages = Minz_Translate::availableLanguages(); $language = isset($_SESSION['language']) && isset($languages[$_SESSION['language']]); @@ -270,7 +264,7 @@ function checkStep0() { function checkStep1() { $php = version_compare(PHP_VERSION, '5.2.1') >= 0; - $minz = file_exists(LIB_PATH . '/Minz'); + $minz = file_exists(join_path(LIB_PATH, 'Minz')); $curl = extension_loaded('curl'); $pdo_mysql = extension_loaded('pdo_mysql'); $pdo_sqlite = extension_loaded('pdo_sqlite'); @@ -280,9 +274,9 @@ function checkStep1() { $dom = class_exists('DOMDocument'); $data = DATA_PATH && is_writable(DATA_PATH); $cache = CACHE_PATH && is_writable(CACHE_PATH); - $log = LOG_PATH && is_writable(LOG_PATH); - $favicons = is_writable(DATA_PATH . '/favicons'); - $persona = is_writable(DATA_PATH . '/persona'); + $users = USERS_PATH && is_writable(USERS_PATH); + $favicons = is_writable(join_path(DATA_PATH, 'favicons')); + $persona = is_writable(join_path(DATA_PATH, 'persona')); $http_referer = is_referer_from_same_domain(); return array( @@ -297,12 +291,12 @@ function checkStep1() { 'dom' => $dom ? 'ok' : 'ko', 'data' => $data ? 'ok' : 'ko', 'cache' => $cache ? 'ok' : 'ko', - 'log' => $log ? 'ok' : 'ko', + 'users' => $users ? 'ok' : 'ko', 'favicons' => $favicons ? 'ok' : 'ko', 'persona' => $persona ? 'ok' : 'ko', 'http_referer' => $http_referer ? 'ok' : 'ko', 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && - $data && $cache && $log && $favicons && $persona && $http_referer ? + $data && $cache && $users && $favicons && $persona && $http_referer ? 'ok' : 'ko' ); } @@ -327,7 +321,7 @@ function checkStep2() { if ($defaultUser === null) { $defaultUser = empty($_SESSION['default_user']) ? '' : $_SESSION['default_user']; } - $data = is_writable(DATA_PATH . '/' . $defaultUser . '_user.php'); + $data = is_writable(join_path(USERS_PATH, $defaultUser, 'config.php')); return array( 'conf' => $conf ? 'ok' : 'ko', @@ -339,7 +333,7 @@ function checkStep2() { } function checkStep3() { - $conf = is_writable(DATA_PATH . '/config.php'); + $conf = is_writable(join_path(DATA_PATH, 'config.php')); $bd = isset($_SESSION['bd_type']) && isset($_SESSION['bd_host']) && @@ -382,7 +376,7 @@ function checkBD() { $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; break; case 'sqlite': - $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; + $str = 'sqlite:' . join_path(USERS_PATH, $_SESSION['default_user'], 'db.sqlite'); $driver_options = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ); @@ -414,7 +408,7 @@ function checkBD() { } if (!$ok) { - @unlink(DATA_PATH . '/config.php'); + @unlink(join_path(DATA_PATH, 'config.php')); } return $ok; @@ -422,7 +416,8 @@ function checkBD() { /*** AFFICHAGE ***/ function printStep0() { - global $actual; + $actual = Minz_Translate::language(); + $languages = Minz_Translate::availableLanguages(); ?>
@@ -434,7 +429,6 @@ function printStep0() {