diff options
| author | 2015-07-23 11:59:32 +0200 | |
|---|---|---|
| committer | 2015-07-23 11:59:32 +0200 | |
| commit | de607efd6ae1dc68ce56e482ff6e88d08e8242f4 (patch) | |
| tree | 5708d4ddd2768e8df3e7ba2c5cb71ab5131b75ff | |
| parent | cd83002f430d573fc3757de3f4204d911bcb7183 (diff) | |
| parent | d2caf4349c4c5957ddc453795ffe8ea10237231c (diff) | |
Merge branch 'FreshRSS/dev' into dev
60 files changed, 768 insertions, 230 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 605b4256c..68a9d0b73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,28 @@ # Changelog -## 2015-xx-xx FreshRSS 1.1.2 (beta) +## 2015-07-xx FreshRSS 1.1.2 (beta) * Features - * Support for PubSubHubbub for instant notifications from compatible Web sites. + * Support for PubSubHubbub for instant notifications from compatible Web sites. [#312](https://github.com/FreshRSS/FreshRSS/issues/312) + * cURL options to use a proxy for retrieving feeds. [#897](https://github.com/FreshRSS/FreshRSS/issues/897) [#675](https://github.com/FreshRSS/FreshRSS/issues/675) + * Allow anonymous users to create an account. [#679](https://github.com/FreshRSS/FreshRSS/issues/679) +* Security + * cURL options to verify or not SSL/TLS certificates (now enabled by default). [#897](https://github.com/FreshRSS/FreshRSS/issues/897) [#502](https://github.com/FreshRSS/FreshRSS/issues/502) + * Support for SSL connection to MySQL. [#868](https://github.com/FreshRSS/FreshRSS/issues/868) + * Workaround for browsers that have disabled support for `<form autocomplete="off">`. [#880](https://github.com/FreshRSS/FreshRSS/issues/880) +* UI + * Force UTF-8 for responses. [#870](https://github.com/FreshRSS/FreshRSS/issues/870) + * Increased pagination limit to 500 articles. [#872](https://github.com/FreshRSS/FreshRSS/issues/872) + * Improved UI for installation. [#855](https://github.com/FreshRSS/FreshRSS/issues/855) +* Misc. + * PHP 7 officially supported (~70% speed improvements on early tests). [#889](https://github.com/FreshRSS/FreshRSS/issues/889) + * Restore support for PHP 5.2.1+. [#214a5cc](https://github.com/Alkarex/FreshRSS/commit/214a5cc9a4c2b821961bc21f22b4b08e34b5be68) [#894](https://github.com/FreshRSS/FreshRSS/issues/894) + * Support for data-src for images of articles retrieved via the full-content module. [#877](https://github.com/FreshRSS/FreshRSS/issues/877) + * Add a couple of default feeds for fresh installations. [#886](https://github.com/FreshRSS/FreshRSS/issues/886) + * Changed some log visibilities. [#885](https://github.com/FreshRSS/FreshRSS/issues/885) + * Fix broken links for extension script / style files. [#862](https://github.com/FreshRSS/FreshRSS/issues/862) + * Load default configuration during installation to avoid hard-coded values. [#890](https://github.com/FreshRSS/FreshRSS/issues/890) + * Fix non-consistent behaviour in Minz_Request::getBaseUrl() and introduce Minz_Request::guessBaseUrl(). [#906](https://github.com/FreshRSS/FreshRSS/issues/906) ## 2015-05-31 FreshRSS 1.1.1 (beta) diff --git a/README.fr.md b/README.fr.md index de7dd1fb2..729dc9826 100644 --- a/README.fr.md +++ b/README.fr.md @@ -33,7 +33,7 @@ Nous sommes une communauté amicale. * Serveur modeste, par exemple sous Linux ou Windows * Fonctionne même sur un Raspberry Pi avec des temps de réponse < 1s (testé sur 150 flux, 22k articles, soit 32Mo de données partiellement compressées) * Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres) -* PHP 5.2.1+ (PHP 5.3.7+ recommandé) +* PHP 5.2.1+ (PHP 5.3.7+ recommandé, et PHP 5.5+ pour les performances) (support bêta de PHP 7 avec encore meilleures performances) * Requis : [PDO_MySQL](http://php.net/pdo-mysql) ou [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl), [GMP](http://php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](http://php.net/intl.idn) (pour les noms de domaines internationalisés) * Recommandés : [JSON](http://php.net/json), [mbstring](http://php.net/mbstring), [zlib](http://php.net/zlib), [Zip](http://php.net/zip) * MySQL 5.0.3+ (recommandé) ou SQLite 3.7.4+ @@ -33,7 +33,7 @@ We are a friendly community. * Light server running Linux or Windows * It even works on Raspberry Pi with response time under a second (tested with 150 feeds, 22k articles, or 32Mo of compressed data) * A web server: Apache2 (recommended), nginx, lighttpd (not tested on others) -* PHP 5.2.1+ (PHP 5.3.7+ recommended) +* PHP 5.2.1+ (PHP 5.3.7+ recommended, and PHP 5.5+ for performance) (beta support for PHP 7 with even higher performance) * Required extensions: [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl), [GMP](http://php.net/gmp) (for API access on platforms < 64 bits), [IDN](http://php.net/intl.idn) (for Internationalized Domain Names) * Recommended extensions: [JSON](http://php.net/json), [mbstring](http://php.net/mbstring), [zlib](http://php.net/zlib), [Zip](http://php.net/zip) * MySQL 5.0.3+ (recommended) or SQLite 3.7.4+ diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 937c0759d..aff184263 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -253,7 +253,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { FreshRSS_Auth::giveAccess(); invalidateHttpCache(); } else { - Minz_Log::error($reason); + Minz_Log::warning($reason); $res = array(); $res['status'] = 'failure'; @@ -346,4 +346,15 @@ class FreshRSS_auth_Controller extends Minz_ActionController { } } } + + /** + * This action gives possibility to a user to create an account. + */ + public function registerAction() { + if (max_registrations_reached()) { + Minz_Error::error(403); + } + + Minz_View::prependTitle(_t('gen.auth.registration.title') . ' · '); + } } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 957a809cd..488d066a9 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -98,10 +98,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // HTTP information are useful if feed is protected behind a // HTTP authentication - $user = Minz_Request::param('http_user'); - $pass = Minz_Request::param('http_pass'); + $user = trim(Minz_Request::param('http_user', '')); + $pass = Minz_Request::param('http_pass', ''); $http_auth = ''; - if ($user != '' || $pass != '') { + if ($user != '' && $pass != '') { //TODO: Sanitize $http_auth = $user . ':' . $pass; } @@ -322,7 +322,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feed->load(false); } } catch (FreshRSS_Feed_Exception $e) { - Minz_Log::notice($e->getMessage()); + Minz_Log::warning($e->getMessage()); $feedDAO->updateLastUpdate($feed->id(), true); $feed->unlock(); continue; diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 26b163e43..60e467255 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -47,7 +47,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $status_file = $file['error']; if ($status_file !== 0) { - Minz_Log::error('File cannot be uploaded. Error code: ' . $status_file); + Minz_Log::warning('File cannot be uploaded. Error code: ' . $status_file); Minz_Request::bad(_t('feedback.import_export.file_cannot_be_uploaded'), array('c' => 'importExport', 'a' => 'index')); } @@ -69,7 +69,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if (!is_resource($zip)) { // zip_open cannot open file: something is wrong - Minz_Log::error('Zip archive cannot be imported. Error code: ' . $zip); + Minz_Log::warning('Zip archive cannot be imported. Error code: ' . $zip); Minz_Request::bad(_t('feedback.import_export.zip_error'), array('c' => 'importExport', 'a' => 'index')); } @@ -77,7 +77,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { while (($zipfile = zip_read($zip)) !== false) { if (!is_resource($zipfile)) { // zip_entry() can also return an error code! - Minz_Log::error('Zip file cannot be imported. Error code: ' . $zipfile); + Minz_Log::warning('Zip file cannot be imported. Error code: ' . $zipfile); } else { $type_zipfile = $this->guessFileType(zip_entry_name($zipfile)); if ($type_file !== 'unknown') { diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 333565faf..03d3ee15e 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -77,11 +77,11 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('sub.title.feed_management') . ' · ' . $this->view->feed->name() . ' · '); if (Minz_Request::isPost()) { - $user = Minz_Request::param('http_user', ''); - $pass = Minz_Request::param('http_pass', ''); + $user = trim(Minz_Request::param('http_user_feed' . $id, '')); + $pass = Minz_Request::param('http_pass_feed' . $id, ''); $httpAuth = ''; - if ($user != '' || $pass != '') { + if ($user != '' && $pass != '') { //TODO: Sanitize $httpAuth = $user . ':' . $pass; } diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 4797a3486..84a33fe85 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -63,7 +63,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { curl_close($c); if ($c_status !== 200) { - Minz_Log::error( + Minz_Log::warning( 'Error during update (HTTP code ' . $c_status . '): ' . $c_error ); diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index ed01b83c5..428cd145d 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -12,9 +12,14 @@ class FreshRSS_user_Controller extends Minz_ActionController { * This action is called before every other action in that class. It is * the common boiler plate for every action. It is triggered by the * underlying framework. + * + * @todo clean up the access condition. */ public function firstAction() { - if (!FreshRSS_Auth::hasAccess()) { + if (!FreshRSS_Auth::hasAccess() && !( + Minz_Request::actionName() === 'create' && + !max_registrations_reached() + )) { Minz_Error::error(403); } } @@ -25,13 +30,17 @@ class FreshRSS_user_Controller extends Minz_ActionController { public function profileAction() { Minz_View::prependTitle(_t('conf.profile.title') . ' · '); + Minz_View::appendScript(Minz_Url::display( + '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js') + )); + if (Minz_Request::isPost()) { $ok = true; - $passwordPlain = Minz_Request::param('passwordPlain', '', true); + $passwordPlain = Minz_Request::param('newPasswordPlain', '', true); if ($passwordPlain != '') { - Minz_Request::_param('passwordPlain'); //Discard plain-text password ASAP - $_POST['passwordPlain'] = ''; + Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP + $_POST['newPasswordPlain'] = ''; if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } @@ -103,8 +112,24 @@ class FreshRSS_user_Controller extends Minz_ActionController { $this->view->size_user = $entryDAO->size(); } + /** + * This action creates a new user. + * + * Request parameters are: + * - new_user_language + * - new_user_name + * - new_user_passwordPlain + * - new_user_email + * - r (i.e. a redirection url, optional) + * + * @todo clean up this method. Idea: write a method to init a user with basic information. + * @todo handle r redirection in Minz_Request::forward directly? + */ public function createAction() { - if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { + if (Minz_Request::isPost() && ( + FreshRSS_Auth::hasAccess('admin') || + !max_registrations_reached() + )) { $db = FreshRSS_Context::$system_conf->db; require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); @@ -175,15 +200,37 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_Session::_param('notification', $notif); } - Minz_Request::forward(array('c' => 'user', 'a' => 'manage'), true); + $redirect_url = urldecode(Minz_Request::param('r', false, true)); + if (!$redirect_url) { + $redirect_url = array('c' => 'user', 'a' => 'manage'); + } + Minz_Request::forward($redirect_url, true); } + /** + * This action delete an existing user. + * + * Request parameter is: + * - username + * + * @todo clean up this method. Idea: create a User->clean() method. + */ public function deleteAction() { - if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { + $username = Minz_Request::param('username'); + $redirect_url = urldecode(Minz_Request::param('r', false, true)); + if (!$redirect_url) { + $redirect_url = array('c' => 'user', 'a' => 'manage'); + } + + $self_deletion = Minz_Session::param('currentUser', '_') === $username; + + if (Minz_Request::isPost() && ( + FreshRSS_Auth::hasAccess('admin') || + $self_deletion + )) { $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); @@ -191,6 +238,16 @@ class FreshRSS_user_Controller extends Minz_ActionController { $default_user = FreshRSS_Context::$system_conf->default_user; $ok &= (strcasecmp($username, $default_user) !== 0); //It is forbidden to delete the default user } + if ($ok && $self_deletion) { + // We check the password if it's a self-destruction + $nonce = Minz_Session::param('nonce'); + $challenge = Minz_Request::param('challenge', ''); + + $ok &= FreshRSS_FormAuth::checkCredentials( + $username, FreshRSS_Context::$user_conf->passwordHash, + $nonce, $challenge + ); + } if ($ok) { $ok &= is_dir($user_data); } @@ -200,6 +257,10 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok &= recursive_unlink($user_data); //TODO: delete Persona file } + if ($ok && $self_deletion) { + FreshRSS_Auth::removeAccess(); + $redirect_url = array('c' => 'index', 'a' => 'index'); + } invalidateHttpCache(); $notif = array( @@ -209,6 +270,30 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_Session::_param('notification', $notif); } + Minz_Request::forward($redirect_url, true); + } + + /** + * This action updates the max number of registrations. + * + * Request parameter is: + * - max-registrations (int >= 0) + */ + public function setRegistrationAction() { + if (Minz_Request::isPost() && FreshRSS_Auth::hasAccess('admin')) { + $limits = FreshRSS_Context::$system_conf->limits; + $limits['max_registrations'] = Minz_Request::param('max-registrations', 1); + FreshRSS_Context::$system_conf->limits = $limits; + FreshRSS_Context::$system_conf->save(); + + invalidateHttpCache(); + + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => _t('feedback.user.set_registration') + )); + } + Minz_Request::forward(array('c' => 'user', 'a' => 'manage'), true); } } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 189a5f0e4..b5abac519 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -13,7 +13,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable return $this->bd->lastInsertId(); } else { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error addCategory: ' . $info[2] ); + Minz_Log::error('SQL error addCategory: ' . $info[2]); return false; } } diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php index 4bd29ecb0..d7378d4d8 100644 --- a/app/Models/ConfigurationSetter.php +++ b/app/Models/ConfigurationSetter.php @@ -352,6 +352,9 @@ class FreshRSS_ConfigurationSetter { 'min' => 0, 'max' => $max_small_int, ), + 'max_registrations' => array( + 'min' => 0, + ), ); foreach ($values as $key => $value) { @@ -361,8 +364,8 @@ class FreshRSS_ConfigurationSetter { $limits = $limits_keys[$key]; if ( - (!isset($limits['min']) || $value > $limits['min']) && - (!isset($limits['max']) || $value < $limits['max']) + (!isset($limits['min']) || $value >= $limits['min']) && + (!isset($limits['max']) || $value <= $limits['max']) ) { $data['limits'][$key] = $value; } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 9ddcfcfb3..f74055835 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -6,6 +6,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return parent::$sharedDbType !== 'sqlite'; } + public function hasNativeHex() { + return parent::$sharedDbType !== 'sqlite'; + } + protected function addColumn($name) { Minz_Log::debug('FreshRSS_EntryDAO::autoAddColumn: ' . $name); $hasTransaction = false; @@ -64,7 +68,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . ', link, date, lastSeen, hash, is_read, is_favorite, id_feed, tags) ' . 'VALUES(?, ?, ?, ?, ' . ($this->isCompressed() ? 'COMPRESS(?)' : '?') - . ', ?, ?, ?, ?, ?, ?, ?, ?)'; + . ', ?, ?, ?, ' + . ($this->hasNativeHex() ? 'X?' : '?') + . ', ?, ?, ?, ?)'; $this->addEntryPrepared = $this->bd->prepare($sql); } @@ -77,7 +83,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { substr($valuesTmp['link'], 0, 1023), $valuesTmp['date'], time(), - hex2bin($valuesTmp['hash']), // X'09AF' hexadecimal literals do not work with SQLite/PDO + $this->hasNativeHex() ? $valuesTmp['hash'] : pack('H*', $valuesTmp['hash']), // X'09AF' hexadecimal literals do not work with SQLite/PDO //hex2bin() is PHP5.4+ $valuesTmp['is_read'] ? 1 : 0, $valuesTmp['is_favorite'] ? 1 : 0, $valuesTmp['id_feed'], @@ -109,8 +115,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $sql = 'UPDATE `' . $this->prefix . 'entry` ' . 'SET title=?, author=?, ' . ($this->isCompressed() ? 'content_bin=COMPRESS(?)' : 'content=?') - . ', link=?, date=?, lastSeen=?, hash=?, ' - . ($valuesTmp['is_read'] === null ? '' : 'is_read=?, ') + . ', link=?, date=?, lastSeen=?, hash=' + . ($this->hasNativeHex() ? 'X?' : '?') + . ', ' . ($valuesTmp['is_read'] === null ? '' : 'is_read=?, ') . 'tags=? ' . 'WHERE id_feed=? AND guid=?'; $this->updateEntryPrepared = $this->bd->prepare($sql); @@ -123,7 +130,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { substr($valuesTmp['link'], 0, 1023), $valuesTmp['date'], time(), - hex2bin($valuesTmp['hash']), + $this->hasNativeHex() ? $valuesTmp['hash'] : pack('H*', $valuesTmp['hash']), ); if ($valuesTmp['is_read'] !== null) { $values[] = $valuesTmp['is_read'] ? 1 : 0; diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index c5787d25b..0f4e04620 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -57,6 +57,8 @@ CREATE TABLE IF NOT EXISTS `%1$sentry` ( ENGINE = INNODB; INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); +INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400); +INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS @ GitHub", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400); '); define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory'); diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 71bad7311..87d5cf286 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -55,6 +55,8 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX IF NOT EXISTS entry_lastSeen_index ON `%1$sentry`(`lastSeen`);', //v1.1.1 'INSERT OR IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s");', +'INSERT OR IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("http://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "http://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400);', +'INSERT OR IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS releases", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400);', ); define('SQL_DROP_TABLES', 'DROP TABLES %1$sentry, %1$sfeed, %1$scategory'); diff --git a/app/i18n/cz/admin.php b/app/i18n/cz/admin.php index b9ef707cf..bfa67573e 100644 --- a/app/i18n/cz/admin.php +++ b/app/i18n/cz/admin.php @@ -160,8 +160,15 @@ return array( 'create' => 'Vytvořit nového uživatele', 'email_persona' => 'Email pro přihlášení<br /><small>(pro <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'language' => 'Jazyk', + 'number' => 'There is %d account created yet', // TODO: translate + 'numbers' => 'There are %d accounts created yet', // TODO: translate 'password_form' => 'Heslo<br /><small>(pro přihlášení webovým formulářem)</small>', 'password_format' => 'Alespoň 7 znaků', + 'registration' => array( + 'allow' => 'Allow account creation', // TODO: translate + 'help' => '0 means that there is no account limit', // TODO: translate + 'number' => 'Max number of accounts', // TODO: translate + ), 'title' => 'Správa uživatelů', 'user_list' => 'Seznam uživatelů', 'username' => 'Přihlašovací jméno', diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php index 9518df66d..859eeac89 100644 --- a/app/i18n/cz/conf.php +++ b/app/i18n/cz/conf.php @@ -72,6 +72,10 @@ return array( ), 'profile' => array( '_' => 'Správa profilu', + 'delete' => array( + '_' => 'Account deletion', // TODO: translate + 'warn' => 'Your account and all the related data will be deleted.', // TODO: translate + ), 'email_persona' => 'Email pro přihlášení<br /><small>(pro <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'password_api' => 'Password API<br /><small>(tzn. pro mobilní aplikace)</small>', 'password_form' => 'Heslo<br /><small>(pro přihlášení webovým formulářem)</small>', diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php index b75a4a15a..cf2ee518c 100644 --- a/app/i18n/cz/feedback.php +++ b/app/i18n/cz/feedback.php @@ -102,6 +102,7 @@ return array( '_' => 'Uživatel %s byl smazán', 'error' => 'Uživatele %s nelze smazat', ), + 'set_registration' => 'The maximum amount of accounts has been updated.', // TODO: translate ), 'profile' => array( 'error' => 'Váš profil nelze změnit', diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php index 7c333a9c8..53127998f 100644 --- a/app/i18n/cz/gen.php +++ b/app/i18n/cz/gen.php @@ -21,15 +21,27 @@ return array( 'truncate' => 'Smazat všechny články', ), 'auth' => array( + 'email' => 'Email', 'keep_logged_in' => 'Zapamatovat přihlášení <small>(1 měsíc)</small>', 'login' => 'Login', 'login_persona' => 'Přihlášení pomocí Persona', 'login_persona_problem' => 'Problém s připojením k Persona?', 'logout' => 'Odhlášení', - 'password' => 'Heslo', + 'password' => array( + '_' => 'Heslo', + 'format' => '<small>Alespoň 7 znaků</small>', + ), + 'registration' => array( + '_' => 'New account', // TODO: translate + 'ask' => 'Create an account?', // TODO: translate + 'title' => 'Account creation', // TODO: translate + ), 'reset' => 'Reset přihlášení', - 'username' => 'Uživatel', - 'username_admin' => 'Název administrátorského účtu', + 'username' => array( + '_' => 'Uživatel', + 'admin' => 'Název administrátorského účtu', + 'format' => '<small>maximálně 16 alfanumerických znaků</small>', + ), 'will_reset' => 'Přihlašovací systém bude vyresetován: místo sytému Persona bude použito přihlášení formulářem.', ), 'date' => array( diff --git a/app/i18n/cz/install.php b/app/i18n/cz/install.php index 53257c84f..cca717513 100644 --- a/app/i18n/cz/install.php +++ b/app/i18n/cz/install.php @@ -4,7 +4,9 @@ return array( 'action' => array( 'finish' => 'Dokončit instalaci', 'fix_errors_before' => 'Chyby prosím před přechodem na další krok opravte.', + 'keep_install' => 'Keep previous installation', // TODO: translate 'next_step' => 'Přejít na další krok', + 'reinstall' => 'Reinstall FreshRSS', // TODO: translate ), 'auth' => array( 'email_persona' => 'Email pro přihlášení<br /><small>(pro <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', @@ -31,6 +33,7 @@ return array( ), 'check' => array( '_' => 'Kontrola', + 'already_installed' => 'We have detected that FreshRSS is already installed!', // TODO: translate 'cache' => array( 'nok' => 'Zkontrolujte oprávnění adresáře <em>./data/cache</em>. HTTP server musí mít do tohoto adresáře práva zápisu', 'ok' => 'Oprávnění adresáře cache jsou v pořádku.', @@ -93,6 +96,9 @@ return array( 'delete_articles_after' => 'Smazat články starší než', 'fix_errors_before' => 'Chyby prosím před přechodem na další krok opravte.', 'javascript_is_better' => 'Práce s FreshRSS je příjemnější se zapnutým JavaScriptem', + 'js' => array( + 'confirm_reinstall' => 'You will lose your previous configuration by reinstalling FreshRSS. Are you sure you want to continue?', // TODO: translate + ), 'language' => array( '_' => 'Jazyk', 'choose' => 'Vyberte jazyk FreshRSS', diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php index c0cbf6787..667f7af8d 100644 --- a/app/i18n/de/admin.php +++ b/app/i18n/de/admin.php @@ -160,8 +160,15 @@ return array( 'create' => 'Neuen Benutzer erstellen', 'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'language' => 'Sprache', + 'number' => 'There is %d account created yet', // TODO: translate + 'numbers' => 'There are %d accounts created yet', // TODO: translate 'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>', 'password_format' => 'mindestens 7 Zeichen', + 'registration' => array( + 'allow' => 'Allow account creation', // TODO: translate + 'help' => '0 means that there is no account limit', // TODO: translate + 'number' => 'Max number of accounts', // TODO: translate + ), 'title' => 'Benutzer verwalten', 'user_list' => 'Liste der Benutzer', 'username' => 'Nutzername', diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index 4a0a77ddd..5313ec3fd 100644 --- a/app/i18n/de/conf.php +++ b/app/i18n/de/conf.php @@ -72,6 +72,10 @@ return array( ), 'profile' => array( '_' => 'Profil-Verwaltung', + 'delete' => array( + '_' => 'Account deletion', // TODO: translate + 'warn' => 'Your account and all the related data will be deleted.', // TODO: translate + ), 'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'password_api' => 'Passwort-API<br /><small>(z. B. für mobile Anwendungen)</small>', 'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>', diff --git a/app/i18n/de/feedback.php b/app/i18n/de/feedback.php index 4c15aadc3..61a7d9d61 100644 --- a/app/i18n/de/feedback.php +++ b/app/i18n/de/feedback.php @@ -102,6 +102,7 @@ return array( '_' => 'Der Benutzer %s ist gelöscht worden', 'error' => 'Der Benutzer %s kann nicht gelöscht werden', ), + 'set_registration' => 'The maximum amount of accounts has been updated.', // TODO: translate ), 'profile' => array( 'error' => 'Ihr Profil kann nicht geändert werden', diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php index f24a52c2e..f8f4823a6 100644 --- a/app/i18n/de/gen.php +++ b/app/i18n/de/gen.php @@ -21,15 +21,27 @@ return array( 'truncate' => 'Alle Artikel löschen', ), 'auth' => array( + 'email' => 'E-Mail-Adresse', 'keep_logged_in' => 'Eingeloggt bleiben <small>(1 Monat)</small>', 'login' => 'Anmelden', 'login_persona' => 'Anmelden mit Persona', 'login_persona_problem' => 'Verbindungsproblem mit Persona?', 'logout' => 'Abmelden', - 'password' => 'Passwort', + 'password' => array( + '_' => 'Passwort', + 'format' => '<small>mindestens 7 Zeichen</small>', + ), + 'registration' => array( + '_' => 'New account', // TODO: translate + 'ask' => 'Create an account?', // TODO: translate + 'title' => 'Account creation', // TODO: translate + ), 'reset' => 'Zurücksetzen der Authentifizierung', - 'username' => 'Nutzername', - 'username_admin' => 'Administrator-Nutzername', + 'username' => array( + '_' => 'Nutzername', + 'admin' => 'Administrator-Nutzername', + 'format' => '<small>maximal 16 alphanumerische Zeichen</small>', + ), 'will_reset' => 'Authentifikationssystem wird zurückgesetzt: ein Formular wird anstelle von Persona benutzt.', ), 'date' => array( diff --git a/app/i18n/de/install.php b/app/i18n/de/install.php index a5899bb52..222c65b32 100644 --- a/app/i18n/de/install.php +++ b/app/i18n/de/install.php @@ -4,7 +4,9 @@ return array( 'action' => array( 'finish' => 'Installation fertigstellen', 'fix_errors_before' => 'Bitte Fehler korrigieren, bevor zum nächsten Schritt gesprungen wird.', + 'keep_install' => 'Keep previous installation', // TODO: translate 'next_step' => 'Zum nächsten Schritt springen', + 'reinstall' => 'Reinstall FreshRSS', // TODO: translate ), 'auth' => array( 'email_persona' => 'Anmelde-E-Mail-Adresse<br /><small>(für <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', @@ -31,6 +33,7 @@ return array( ), 'check' => array( '_' => 'Überprüfungen', + 'already_installed' => 'We have detected that FreshRSS is already installed!', // TODO: translate 'cache' => array( 'nok' => 'Überprüfen Sie die Berechtigungen des Verzeichnisses <em>./data/cache</em>. Der HTTP-Server muss Schreibrechte besitzen.', 'ok' => 'Die Berechtigungen des Verzeichnisses <em>./data/cache</em> sind in Ordnung.', @@ -93,6 +96,9 @@ return array( 'delete_articles_after' => 'Entferne Artikel nach', 'fix_errors_before' => 'Bitte den Fehler korrigieren, bevor zum nächsten Schritt gesprungen wird.', 'javascript_is_better' => 'FreshRSS ist ansprechender mit aktiviertem JavaScript', + 'js' => array( + 'confirm_reinstall' => 'You will lose your previous configuration by reinstalling FreshRSS. Are you sure you want to continue?', // TODO: translate + ), 'language' => array( '_' => 'Sprache', 'choose' => 'Wählen Sie eine Sprache für FreshRSS', diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php index 155384afd..aeea61631 100644 --- a/app/i18n/en/admin.php +++ b/app/i18n/en/admin.php @@ -160,8 +160,15 @@ return array( 'create' => 'Create new user', 'email_persona' => 'Login mail address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'language' => 'Language', + 'number' => 'There is %d account created yet', + 'numbers' => 'There are %d accounts created yet', 'password_form' => 'Password<br /><small>(for the Web-form login method)</small>', 'password_format' => 'At least 7 characters', + 'registration' => array( + 'allow' => 'Allow account creation', + 'help' => '0 means that there is no account limit', + 'number' => 'Max number of accounts', + ), 'title' => 'Manage users', 'user_list' => 'List of users', 'username' => 'Username', diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index 42a06906d..69162932f 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -72,6 +72,10 @@ return array( ), 'profile' => array( '_' => 'Profile management', + 'delete' => array( + '_' => 'Account deletion', + 'warn' => 'Your account and all the related data will be deleted.', + ), 'email_persona' => 'Login email address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'password_api' => 'Password API<br /><small>(e.g., for mobile apps)</small>', 'password_form' => 'Password<br /><small>(for the Web-form login method)</small>', diff --git a/app/i18n/en/feedback.php b/app/i18n/en/feedback.php index c9189c0d0..c9f73dc1d 100644 --- a/app/i18n/en/feedback.php +++ b/app/i18n/en/feedback.php @@ -102,6 +102,7 @@ return array( '_' => 'User %s has been deleted', 'error' => 'User %s cannot be deleted', ), + 'set_registration' => 'The maximum amount of accounts has been updated.', ), 'profile' => array( 'error' => 'Your profile cannot be modified', diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index 6e47e0921..1feb8d6ac 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -21,15 +21,27 @@ return array( 'truncate' => 'Delete all articles', ), 'auth' => array( + 'email' => 'Email address', 'keep_logged_in' => 'Keep me logged in <small>(1 month)</small>', 'login' => 'Login', 'login_persona' => 'Login with Persona', 'login_persona_problem' => 'Connection problem with Persona?', 'logout' => 'Logout', - 'password' => 'Password', + 'password' => array( + '_' => 'Password', + 'format' => '<small>At least 7 characters</small>', + ), + 'registration' => array( + '_' => 'New account', + 'ask' => 'Create an account?', + 'title' => 'Account creation', + ), 'reset' => 'Authentication reset', - 'username' => 'Username', - 'username_admin' => 'Administrator username', + 'username' => array( + '_' => 'Username', + 'admin' => 'Administrator username', + 'format' => '<small>maximum 16 alphanumeric characters</small>', + ), 'will_reset' => 'Authentication system will be reset: a form will be used instead of Persona.', ), 'date' => array( diff --git a/app/i18n/en/install.php b/app/i18n/en/install.php index d4137309a..b94fbc299 100644 --- a/app/i18n/en/install.php +++ b/app/i18n/en/install.php @@ -4,7 +4,9 @@ return array( 'action' => array( 'finish' => 'Complete installation', 'fix_errors_before' => 'Please fix errors before skipping to the next step.', + 'keep_install' => 'Keep previous installation', 'next_step' => 'Go to the next step', + 'reinstall' => 'Reinstall FreshRSS', ), 'auth' => array( 'email_persona' => 'Login email address<br /><small>(for <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', @@ -31,6 +33,7 @@ return array( ), 'check' => array( '_' => 'Checks', + 'already_installed' => 'We have detected that FreshRSS is already installed!', 'cache' => array( 'nok' => 'Check permissions on <em>./data/cache</em> directory. HTTP server must have rights to write into', 'ok' => 'Permissions on cache directory are good.', @@ -93,6 +96,9 @@ return array( 'delete_articles_after' => 'Remove articles after', 'fix_errors_before' => 'Please fix errors before skipping to the next step.', 'javascript_is_better' => 'FreshRSS is more pleasant with JavaScript enabled', + 'js' => array( + 'confirm_reinstall' => 'You will lose your previous configuration by reinstalling FreshRSS. Are you sure you want to continue?', + ), 'language' => array( '_' => 'Language', 'choose' => 'Choose a language for FreshRSS', diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php index b740bd0d2..01e0cb3c7 100644 --- a/app/i18n/fr/admin.php +++ b/app/i18n/fr/admin.php @@ -160,8 +160,15 @@ return array( 'create' => 'Créer un nouvel utilisateur', 'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'language' => 'Langue', + 'number' => '%d compte a déjà été créé', + 'numbers' => '%d comptes ont déjà été créés', 'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>', 'password_format' => '7 caractères minimum', + 'registration' => array( + 'allow' => 'Autoriser la création de comptes', + 'help' => 'Un chiffre de 0 signifie que l’on peut créer un nombre infini de comptes', + 'number' => 'Nombre max de comptes', + ), 'title' => 'Gestion des utilisateurs', 'user_list' => 'Liste des utilisateurs', 'username' => 'Nom d’utilisateur', diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index 87f9be290..6193b7a01 100644 --- a/app/i18n/fr/conf.php +++ b/app/i18n/fr/conf.php @@ -72,6 +72,10 @@ return array( ), 'profile' => array( '_' => 'Gestion du profil', + 'delete' => array( + '_' => 'Suppression du compte', + 'warn' => 'Le compte et toutes les données associées vont être supprimées.', + ), 'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', 'password_api' => 'Mot de passe API<br /><small>(ex. : pour applis mobiles)</small>', 'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>', diff --git a/app/i18n/fr/feedback.php b/app/i18n/fr/feedback.php index e2364a251..99c193d28 100644 --- a/app/i18n/fr/feedback.php +++ b/app/i18n/fr/feedback.php @@ -102,6 +102,7 @@ return array( '_' => 'L’utilisateur %s a été supprimé.', 'error' => 'L’utilisateur %s ne peut pas être supprimé.', ), + 'set_registration' => 'Le nombre maximal de comptes a été mis à jour.', ), 'profile' => array( 'error' => 'Votre profil n’a pas pu être mis à jour', diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index c81e57bf7..67d278be4 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -21,15 +21,27 @@ return array( 'truncate' => 'Supprimer tous les articles', ), 'auth' => array( + 'email' => 'Adresse courriel', 'keep_logged_in' => 'Rester connecté <small>(1 mois)</small>', 'login' => 'Connexion', 'login_persona' => 'Connexion avec Persona', 'login_persona_problem' => 'Problème de connexion à Persona ?', 'logout' => 'Déconnexion', - 'password' => 'Mot de passe', + 'password' => array( + '_' => 'Mot de passe', + 'format' => '<small>7 caractères minimum</small>', + ), + 'registration' => array( + '_' => 'Nouveau compte', + 'ask' => 'Créer un compte ?', + 'title' => 'Création de compte', + ), 'reset' => 'Réinitialisation de l’authentification', - 'username' => 'Nom d’utilisateur', - 'username_admin' => 'Nom d’utilisateur administrateur', + 'username' => array( + '_' => 'Nom d’utilisateur', + 'admin' => 'Nom d’utilisateur administrateur', + 'format' => '<small>16 caractères alphanumériques maximum</small>', + ), 'will_reset' => 'Le système d’authentification va être réinitialisé : un formulaire sera utilisé à la place de Persona.', ), 'date' => array( diff --git a/app/i18n/fr/install.php b/app/i18n/fr/install.php index 245a20c56..0401e1bbd 100644 --- a/app/i18n/fr/install.php +++ b/app/i18n/fr/install.php @@ -4,7 +4,9 @@ return array( 'action' => array( 'finish' => 'Terminer l’installation', 'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à l’étape suivante.', + 'keep_install' => 'Garder l’ancienne configuration', 'next_step' => 'Passer à l’étape suivante', + 'reinstall' => 'Réinstaller FreshRSS', ), 'auth' => array( 'email_persona' => 'Adresse courriel de connexion<br /><small>(pour <a href="https://persona.org/" rel="external">Mozilla Persona</a>)</small>', @@ -31,6 +33,7 @@ return array( ), 'check' => array( '_' => 'Vérifications', + 'already_installed' => 'FreshRSS semble avoir déjà été installé !', 'cache' => array( 'nok' => 'Veuillez vérifier les droits sur le répertoire <em>./data/cache</em>. Le serveur HTTP doit être capable d’écrire dedans', 'ok' => 'Les droits sur le répertoire de cache sont bons.', @@ -93,6 +96,9 @@ return array( 'delete_articles_after' => 'Supprimer les articles après', 'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à l’étape suivante.', 'javascript_is_better' => 'FreshRSS est plus agréable à utiliser avec JavaScript activé', + 'js' => array( + 'confirm_reinstall' => 'Réinstaller FreshRSS vous fera perdre la configuration précédente. Êtes-vous sûr de vouloir continuer ?', + ), 'language' => array( '_' => 'Langue', 'choose' => 'Choisissez la langue pour FreshRSS', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index 0a1a03e41..e3631eb8b 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -35,7 +35,7 @@ return array( 'title_add' => 'Ajouter un flux RSS', 'ttl' => 'Ne pas automatiquement rafraîchir plus souvent que', 'url' => 'URL du flux', - 'validator' => 'Vérifier la valididé du flux', + 'validator' => 'Vérifier la validité du flux', 'website' => 'URL du site', 'pubsubhubbub' => 'Notification instantanée par PubSubHubbub', ), diff --git a/app/install.php b/app/install.php index 313081c14..d5455f17d 100644 --- a/app/install.php +++ b/app/install.php @@ -9,6 +9,9 @@ session_name('FreshRSS'); session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start(); +Minz_Configuration::register('default_system', join_path(DATA_PATH, 'config.default.php')); +Minz_Configuration::register('default_user', join_path(USERS_PATH, '_', 'config.default.php')); + if (isset($_GET['step'])) { define('STEP',(int)$_GET['step']); } else { @@ -76,10 +79,51 @@ function saveLanguage() { } } +function saveStep1() { + if (isset($_POST['freshrss-keep-install']) && + $_POST['freshrss-keep-install'] === '1') { + // We want to keep our previous installation of FreshRSS + // so we need to make next steps valid by setting $_SESSION vars + // with values from the previous installation + + // First, we try to get previous configurations + Minz_Configuration::register('system', + join_path(DATA_PATH, 'config.php'), + join_path(DATA_PATH, 'config.default.php')); + $system_conf = Minz_Configuration::get('system'); + + $current_user = $system_conf->default_user; + Minz_Configuration::register('user', + join_path(USERS_PATH, $current_user, 'config.php'), + join_path(USERS_PATH, '_', 'config.default.php')); + $user_conf = Minz_Configuration::get('user'); + + // Then, we set $_SESSION vars + $_SESSION['title'] = $system_conf->title; + $_SESSION['auth_type'] = $system_conf->auth_type; + $_SESSION['old_entries'] = $user_conf->old_entries; + $_SESSION['mail_login'] = $user_conf->mail_login; + $_SESSION['default_user'] = $current_user; + $_SESSION['passwordHash'] = $user_conf->passwordHash; + + $db = $system_conf->db; + $_SESSION['bd_type'] = $db['type']; + $_SESSION['bd_host'] = $db['host']; + $_SESSION['bd_user'] = $db['user']; + $_SESSION['bd_password'] = $db['password']; + $_SESSION['bd_base'] = $db['base']; + $_SESSION['bd_prefix'] = $db['prefix']; + $_SESSION['bd_error'] = ''; + + header('Location: index.php?step=4'); + } +} + function saveStep2() { + $user_default_config = Minz_Configuration::get('default_user'); if (!empty($_POST)) { $_SESSION['title'] = substr(trim(param('title', _t('gen.freshrss'))), 0, 25); - $_SESSION['old_entries'] = param('old_entries', 3); + $_SESSION['old_entries'] = param('old_entries', $user_default_config->old_entries); $_SESSION['auth_type'] = param('auth_type', 'form'); $_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', param('default_user', '')), 0, 16); $_SESSION['mail_login'] = filter_var(param('mail_login', ''), FILTER_VALIDATE_EMAIL); @@ -108,7 +152,7 @@ function saveStep2() { $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { - $_SESSION['old_entries'] = 3; + $_SESSION['old_entries'] = $user_default_config->old_entries; } $token = ''; @@ -118,7 +162,7 @@ function saveStep2() { $config_array = array( 'language' => $_SESSION['language'], - 'theme' => 'Origine', + 'theme' => $user_default_config->theme, 'old_entries' => $_SESSION['old_entries'], 'mail_login' => $_SESSION['mail_login'], 'passwordHash' => $_SESSION['passwordHash'], @@ -168,10 +212,7 @@ function saveStep3() { $_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] . (empty($_SESSION['default_user']) ? '' : ($_SESSION['default_user'] . '_')); } - //TODO: load `config.default.php` as default $config_array = array( - 'environment' => 'production', - 'simplepie_syslog_enabled' => true, 'salt' => $_SESSION['salt'], 'title' => $_SESSION['title'], 'default_user' => $_SESSION['default_user'], @@ -301,6 +342,33 @@ function checkStep1() { ); } +function freshrss_already_installed() { + $conf_path = join_path(DATA_PATH, 'config.php'); + if (!file_exists($conf_path)) { + return false; + } + + // A configuration file already exists, we try to load it. + $system_conf = null; + try { + Minz_Configuration::register('system', $conf_path); + $system_conf = Minz_Configuration::get('system'); + } catch (Minz_FileNotExistException $e) { + return false; + } + + // ok, the global conf exists... but what about default user conf? + $current_user = $system_conf->default_user; + try { + Minz_Configuration::register('user', join_path(USERS_PATH, $current_user, 'config.php')); + } catch (Minz_FileNotExistException $e) { + return false; + } + + // ok, ok, default user exists too! + return true; +} + function checkStep2() { $conf = !empty($_SESSION['title']) && !empty($_SESSION['old_entries']) && @@ -428,7 +496,7 @@ function printStep0() { <div class="form-group"> <label class="group-name" for="language"><?php echo _t('install.language'); ?></label> <div class="group-controls"> - <select name="language" id="language"> + <select name="language" id="language" tabindex="1" > <?php foreach ($languages as $lang) { ?> <option value="<?php echo $lang; ?>"<?php echo $actual == $lang ? ' selected="selected"' : ''; ?>> <?php echo _t('gen.lang.' . $lang); ?> @@ -440,10 +508,10 @@ function printStep0() { <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important" tabindex="2" ><?php echo _t('gen.action.submit'); ?></button> + <button type="reset" class="btn" tabindex="3" ><?php echo _t('gen.action.cancel'); ?></button> <?php if ($s0['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=1"><?php echo _t('install.action.next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=1" tabindex="4" ><?php echo _t('install.action.next_step'); ?></a> <?php } ?> </div> </div> @@ -536,8 +604,38 @@ function printStep1() { <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.http_referer.nok'); ?></p> <?php } ?> - <?php if ($res['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=2"><?php echo _t('install.action.next_step'); ?></a> + <?php if (freshrss_already_installed() && $res['all'] == 'ok') { ?> + <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('install.check.already_installed'); ?></p> + + <form action="index.php?step=1" method="post"> + <input type="hidden" name="freshrss-keep-install" value="1" /> + <button type="submit" class="btn btn-important next-step" tabindex="1" ><?php echo _t('install.action.keep_install'); ?></button> + <a class="btn btn-attention next-step confirm" data-str-confirm="<?php echo _t('install.js.confirm_reinstall'); ?>" href="?step=2" tabindex="2" ><?php echo _t('install.action.reinstall'); ?></a> + </form> + + <script> + function ask_confirmation(e) { + var str_confirmation = this.getAttribute('data-str-confirm'); + if (!str_confirmation) { + str_confirmation = "<?php echo _t('gen.js.confirm_action'); ?>"; + } + + if (!confirm(str_confirmation)) { + e.preventDefault(); + } + } + + function init_confirm() { + confirms = document.getElementsByClassName('confirm'); + for (var i = 0 ; i < confirms.length ; i++) { + confirms[i].addEventListener('click', ask_confirmation); + } + } + + init_confirm(); + </script> + <?php } elseif ($res['all'] == 'ok') { ?> + <a class="btn btn-important next-step" href="?step=2" tabindex="1" ><?php echo _t('install.action.next_step'); ?></a> <?php } else { ?> <p class="alert alert-error"><?php echo _t('install.action.fix_errors_before'); ?></p> <?php } ?> @@ -545,6 +643,7 @@ function printStep1() { } function printStep2() { + $user_default_config = Minz_Configuration::get('default_user'); ?> <?php $s2 = checkStep2(); if ($s2['all'] == 'ok') { ?> <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.conf.ok'); ?></p> @@ -558,28 +657,28 @@ function printStep2() { <div class="form-group"> <label class="group-name" for="title"><?php echo _t('install.title'); ?></label> <div class="group-controls"> - <input type="text" id="title" name="title" value="<?php echo isset($_SESSION['title']) ? $_SESSION['title'] : _t('gen.freshrss'); ?>" /> + <input type="text" id="title" name="title" value="<?php echo isset($_SESSION['title']) ? $_SESSION['title'] : _t('gen.freshrss'); ?>" tabindex="1" /> </div> </div> <div class="form-group"> <label class="group-name" for="old_entries"><?php echo _t('install.delete_articles_after'); ?></label> <div class="group-controls"> - <input type="number" id="old_entries" name="old_entries" required="required" min="1" max="1200" value="<?php echo isset($_SESSION['old_entries']) ? $_SESSION['old_entries'] : '3'; ?>" /> <?php echo _t('gen.date.month'); ?> + <input type="number" id="old_entries" name="old_entries" required="required" min="1" max="1200" value="<?php echo isset($_SESSION['old_entries']) ? $_SESSION['old_entries'] : $user_default_config->old_entries; ?>" tabindex="2" /> <?php echo _t('gen.date.month'); ?> </div> </div> <div class="form-group"> <label class="group-name" for="default_user"><?php echo _t('install.default_user'); ?></label> <div class="group-controls"> - <input type="text" id="default_user" name="default_user" required="required" size="16" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" value="<?php echo isset($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'alice' : httpAuthUser(); ?>" /> + <input type="text" id="default_user" name="default_user" required="required" size="16" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" value="<?php echo isset($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'alice' : httpAuthUser(); ?>" tabindex="3" /> </div> </div> <div class="form-group"> <label class="group-name" for="auth_type"><?php echo _t('install.auth.type'); ?></label> <div class="group-controls"> - <select id="auth_type" name="auth_type" required="required" onchange="auth_type_change(true)"> + <select id="auth_type" name="auth_type" required="required" onchange="auth_type_change(true)" tabindex="4"> <?php function no_auth($auth_type) { return !in_array($auth_type, array('form', 'persona', 'http_auth', 'none')); @@ -598,7 +697,7 @@ function printStep2() { <label class="group-name" for="passwordPlain"><?php echo _t('install.auth.password_form'); ?></label> <div class="group-controls"> <div class="stick"> - <input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $auth_type === 'form' ? ' required="required"' : ''; ?> /> + <input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $auth_type === 'form' ? ' required="required"' : ''; ?> tabindex="5" /> <a class="btn toggle-password" data-toggle="passwordPlain"><?php echo FreshRSS_Themes::icon('key'); ?></a> </div> <?php echo _i('help'); ?> <?php echo _t('install.auth.password_format'); ?> @@ -609,7 +708,7 @@ function printStep2() { <div class="form-group"> <label class="group-name" for="mail_login"><?php echo _t('install.auth.email_persona'); ?></label> <div class="group-controls"> - <input type="email" id="mail_login" name="mail_login" value="<?php echo isset($_SESSION['mail_login']) ? $_SESSION['mail_login'] : ''; ?>" placeholder="alice@example.net" <?php echo $auth_type === 'persona' ? ' required="required"' : ''; ?> /> + <input type="email" id="mail_login" name="mail_login" value="<?php echo isset($_SESSION['mail_login']) ? $_SESSION['mail_login'] : ''; ?>" placeholder="alice@example.net" <?php echo $auth_type === 'persona' ? ' required="required"' : ''; ?> tabindex="6"/> <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> </div> </div> @@ -637,7 +736,7 @@ function printStep2() { toggles[i].addEventListener('mouseup', hide_password); } - function auth_type_change(focus) { + function auth_type_change() { var auth_value = document.getElementById('auth_type').value, password_input = document.getElementById('passwordPlain'), mail_input = document.getElementById('mail_login'); @@ -645,29 +744,23 @@ function printStep2() { if (auth_value === 'form') { password_input.required = true; mail_input.required = false; - if (focus) { - password_input.focus(); - } } else if (auth_value === 'persona') { password_input.required = false; mail_input.required = true; - if (focus) { - mail_input.focus(); - } } else { password_input.required = false; mail_input.required = false; } } - auth_type_change(false); + auth_type_change(); </script> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important" tabindex="7" ><?php echo _t('gen.action.submit'); ?></button> + <button type="reset" class="btn" tabindex="8" ><?php echo _t('gen.action.cancel'); ?></button> <?php if ($s2['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=3"><?php echo _t('install.action.next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=3" tabindex="9" ><?php echo _t('install.action.next_step'); ?></a> <?php } ?> </div> </div> @@ -676,6 +769,7 @@ function printStep2() { } function printStep3() { + $system_default_config = Minz_Configuration::get('default_system'); ?> <?php $s3 = checkStep3(); if ($s3['all'] == 'ok') { ?> <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.bdd.conf.ok'); ?></p> @@ -688,7 +782,7 @@ function printStep3() { <div class="form-group"> <label class="group-name" for="type"><?php echo _t('install.bdd.type'); ?></label> <div class="group-controls"> - <select name="type" id="type" onchange="mySqlShowHide()"> + <select name="type" id="type" onchange="mySqlShowHide()" tabindex="1" > <?php if (extension_loaded('pdo_mysql')) {?> <option value="mysql" <?php echo(isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'mysql') ? 'selected="selected"' : ''; ?>> @@ -709,35 +803,35 @@ function printStep3() { <div class="form-group"> <label class="group-name" for="host"><?php echo _t('install.bdd.host'); ?></label> <div class="group-controls"> - <input type="text" id="host" name="host" pattern="[0-9A-Za-z_.-]{1,64}" value="<?php echo isset($_SESSION['bd_host']) ? $_SESSION['bd_host'] : 'localhost'; ?>" /> + <input type="text" id="host" name="host" pattern="[0-9A-Za-z_.-]{1,64}" value="<?php echo isset($_SESSION['bd_host']) ? $_SESSION['bd_host'] : $system_default_config->db['host']; ?>" tabindex="2" /> </div> </div> <div class="form-group"> <label class="group-name" for="user"><?php echo _t('install.bdd.username'); ?></label> <div class="group-controls"> - <input type="text" id="user" name="user" maxlength="16" pattern="[0-9A-Za-z_.-]{1,16}" value="<?php echo isset($_SESSION['bd_user']) ? $_SESSION['bd_user'] : ''; ?>" /> + <input type="text" id="user" name="user" maxlength="16" pattern="[0-9A-Za-z_.-]{1,16}" value="<?php echo isset($_SESSION['bd_user']) ? $_SESSION['bd_user'] : ''; ?>" tabindex="3" /> </div> </div> <div class="form-group"> <label class="group-name" for="pass"><?php echo _t('install.bdd.password'); ?></label> <div class="group-controls"> - <input type="password" id="pass" name="pass" value="<?php echo isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" /> + <input type="password" id="pass" name="pass" value="<?php echo isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" tabindex="4" /> </div> </div> <div class="form-group"> <label class="group-name" for="base"><?php echo _t('install.bdd'); ?></label> <div class="group-controls"> - <input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_]{1,64}" value="<?php echo isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" /> + <input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_]{1,64}" value="<?php echo isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" tabindex="5" /> </div> </div> <div class="form-group"> <label class="group-name" for="prefix"><?php echo _t('install.bdd.prefix'); ?></label> <div class="group-controls"> - <input type="text" id="prefix" name="prefix" maxlength="16" pattern="[0-9A-Za-z_]{1,16}" value="<?php echo isset($_SESSION['bd_prefix']) ? $_SESSION['bd_prefix'] : 'freshrss_'; ?>" /> + <input type="text" id="prefix" name="prefix" maxlength="16" pattern="[0-9A-Za-z_]{1,16}" value="<?php echo isset($_SESSION['bd_prefix']) ? $_SESSION['bd_prefix'] : $system_default_config->db['prefix']; ?>" tabindex="6" /> </div> </div> </div> @@ -757,10 +851,10 @@ function printStep3() { <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important" tabindex="7" ><?php echo _t('gen.action.submit'); ?></button> + <button type="reset" class="btn" tabindex="8" ><?php echo _t('gen.action.cancel'); ?></button> <?php if ($s3['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=4"><?php echo _t('install.action.next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=4" tabindex="9" ><?php echo _t('install.action.next_step'); ?></a> <?php } ?> </div> </div> @@ -771,7 +865,7 @@ function printStep3() { function printStep4() { ?> <p class="alert alert-success"><span class="alert-head"><?php echo _t('install.congratulations'); ?></span> <?php echo _t('install.ok'); ?></p> - <a class="btn btn-important next-step" href="?step=5"><?php echo _t('install.action.finish'); ?></a> + <a class="btn btn-important next-step" href="?step=5" tabindex="1"><?php echo _t('install.action.finish'); ?></a> <?php } @@ -791,6 +885,7 @@ default: saveLanguage(); break; case 1: + saveStep1(); break; case 2: saveStep2(); @@ -830,7 +925,7 @@ case 5: <li class="item<?php echo STEP == 1 ? ' active' : ''; ?>"><a href="?step=1"><?php echo _t('install.check'); ?></a></li> <li class="item<?php echo STEP == 2 ? ' active' : ''; ?>"><a href="?step=2"><?php echo _t('install.conf'); ?></a></li> <li class="item<?php echo STEP == 3 ? ' active' : ''; ?>"><a href="?step=3"><?php echo _t('install.bdd.conf'); ?></a></li> - <li class="item<?php echo STEP == 4 ? ' active' : ''; ?>"><a href="?step=5"><?php echo _t('install.this_is_the_end'); ?></a></li> + <li class="item<?php echo STEP == 4 ? ' active' : ''; ?>"><a href="?step=4"><?php echo _t('install.this_is_the_end'); ?></a></li> </ul> <div class="post"> diff --git a/app/views/auth/formLogin.phtml b/app/views/auth/formLogin.phtml index 979e17349..b0083944f 100644 --- a/app/views/auth/formLogin.phtml +++ b/app/views/auth/formLogin.phtml @@ -1,6 +1,10 @@ <div class="prompt"> <h1><?php echo _t('gen.auth.login'); ?></h1> + <?php if (!max_registrations_reached()) { ?> + <a href="<?php echo _url('auth', 'register'); ?>"><?php echo _t('gen.auth.registration.ask'); ?></a> + <?php } ?> + <form id="crypto-form" method="post" action="<?php echo _url('auth', 'login'); ?>"> <div> <label for="username"><?php echo _t('gen.auth.username'); ?></label> diff --git a/app/views/auth/personaLogin.phtml b/app/views/auth/personaLogin.phtml index 545ed2eac..c6d738bf6 100644 --- a/app/views/auth/personaLogin.phtml +++ b/app/views/auth/personaLogin.phtml @@ -2,6 +2,10 @@ <div class="prompt"> <h1><?php echo _t('gen.auth.login'); ?></h1> + <?php if (!max_registrations_reached()) { ?> + <a href="<?php echo _url('auth', 'register'); ?>"><?php echo _t('gen.auth.registration.ask'); ?></a> + <?php } ?> + <p> <a class="signin btn btn-important" href="<?php echo _url('auth', 'login'); ?>"> <?php echo _i('login'); ?> <?php echo _t('gen.auth.login_persona'); ?> diff --git a/app/views/auth/register.phtml b/app/views/auth/register.phtml new file mode 100644 index 000000000..306679601 --- /dev/null +++ b/app/views/auth/register.phtml @@ -0,0 +1,38 @@ +<div class="prompt"> + <h1><?php echo _t('gen.auth.registration'); ?></h1> + + <form method="post" action="<?php echo _url('user', 'create'); ?>"> + <div> + <label class="group-name" for="new_user_name"><?php echo _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format'); ?></label> + <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" maxlength="16" autocomplete="off" pattern="[0-9a-zA-Z]{1,16}" /> + </div> + + <div> + <label class="group-name" for="new_user_passwordPlain"><?php echo _t('gen.auth.password'), '<br />', _i('help'), ' ', _t('gen.auth.password.format'); ?></label> + <div class="stick"> + <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" required="required" autocomplete="off" pattern=".{7,}" /> + <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a> + </div> + <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> + </div> + + <div> + <label class="group-name" for="new_user_email"><?php echo _t('gen.auth.email'); ?></label> + <input type="email" id="new_user_email" name="new_user_email" class="extend" required="required" autocomplete="off" /> + </div> + + <div> + <?php + $redirect_url = urlencode(Minz_Url::display( + array('c' => 'index', 'a' => 'index'), + 'php', true + )); + ?> + <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" /> + <button type="submit" class="btn btn-important"><?php echo _t('gen.action.create'); ?></button> + <a class="btn" href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.cancel'); ?></a> + </div> + </form> + + <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.freshrss.about'); ?></a></p> +</div> diff --git a/app/views/auth/reset.phtml b/app/views/auth/reset.phtml index 6e9816ad3..9c820c7c8 100644 --- a/app/views/auth/reset.phtml +++ b/app/views/auth/reset.phtml @@ -16,7 +16,7 @@ </p> <div> - <label for="username"><?php echo _t('gen.auth.username_admin'); ?></label> + <label for="username"><?php echo _t('gen.auth.username.admin'); ?></label> <input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" /> </div> <div> diff --git a/app/views/feed/add.phtml b/app/views/feed/add.phtml index 4cdd3f390..35f6fbb12 100644 --- a/app/views/feed/add.phtml +++ b/app/views/feed/add.phtml @@ -67,7 +67,7 @@ <div class="form-group"> <label class="group-name" for="http_user"><?php echo _t('sub.feed.auth.username'); ?></label> <div class="group-controls"> - <input type="text" name="http_user" id="http_user" class="extend" value="<?php echo $auth['username']; ?>" autocomplete="off" /> + <input type="text" name="http_user" id="http_user" class="extend" value="<?php echo empty($auth['username']) ? ' ' : $auth['username']; ?>" autocomplete="off" /> </div> <label class="group-name" for="http_pass"><?php echo _t('sub.feed.auth.password'); ?></label> diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index b2cf9f93c..12f485ec3 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -144,15 +144,15 @@ <legend><?php echo _t('sub.feed.auth.configuration'); ?></legend> <?php $auth = $this->feed->httpAuth(false); ?> <div class="form-group"> - <label class="group-name" for="http_user"><?php echo _t('sub.feed.auth.username'); ?></label> + <label class="group-name" for="http_user_feed<?php echo $this->feed->id(); ?>"><?php echo _t('sub.feed.auth.username'); ?></label> <div class="group-controls"> - <input type="text" name="http_user" id="http_user" class="extend" value="<?php echo $auth['username']; ?>" autocomplete="off" /> + <input type="text" name="http_user_feed<?php echo $this->feed->id(); ?>" id="http_user_feed<?php echo $this->feed->id(); ?>" class="extend" value="<?php echo empty($auth['username']) ? ' ' : $auth['username']; ?>" autocomplete="off" /> <?php echo _i('help'); ?> <?php echo _t('sub.feed.auth.help'); ?> </div> - <label class="group-name" for="http_pass"><?php echo _t('sub.feed.auth.password'); ?></label> + <label class="group-name" for="http_pass_feed<?php echo $this->feed->id(); ?>"><?php echo _t('sub.feed.auth.password'); ?></label> <div class="group-controls"> - <input type="password" name="http_pass" id="http_pass" class="extend" value="<?php echo $auth['password']; ?>" autocomplete="off" /> + <input type="password" name="http_pass_feed<?php echo $this->feed->id(); ?>" id="http_pass_feed<?php echo $this->feed->id(); ?>" class="extend" value="<?php echo $auth['password']; ?>" autocomplete="off" /> </div> </div> diff --git a/app/views/subscription/index.phtml b/app/views/subscription/index.phtml index 331e8244e..2cfe3f33c 100644 --- a/app/views/subscription/index.phtml +++ b/app/views/subscription/index.phtml @@ -36,10 +36,10 @@ <li class="dropdown-header"><?php echo _t('sub.feed.auth.http'); ?></li> <li class="input"> - <input type="text" name="http_user" id="http_user_add" autocomplete="off" placeholder="<?php echo _t('sub.feed.auth.username'); ?>" /> + <input type="text" name="http_user" id="http_user_feed" value=" " autocomplete="off" placeholder="<?php echo _t('sub.feed.auth.username'); ?>" /> </li> <li class="input"> - <input type="password" name="http_pass" id="http_pass_add" autocomplete="off" placeholder="<?php echo _t('sub.feed.auth.password'); ?>" /> + <input type="password" name="http_pass" id="http_pass_feed" autocomplete="off" placeholder="<?php echo _t('sub.feed.auth.password'); ?>" /> </li> </ul> </div> diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml index fe1b6618b..3d3bc3ddf 100644 --- a/app/views/user/manage.phtml +++ b/app/views/user/manage.phtml @@ -3,6 +3,34 @@ <div class="post"> <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <form method="post" action="<?php echo _url('user', 'setRegistration'); ?>"> + <legend><?php echo _t('admin.user.registration.allow'); ?></legend> + + <div class="form-group"> + <label class="group-name" for="max-registrations"><?php echo _t('admin.user.registration.number'); ?></label> + <div class="group-controls"> + <input type="number" id="max-registrations" name="max-registrations" value="<?php echo FreshRSS_Context::$system_conf->limits['max_registrations']; ?>" min="0" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->limits['max_registrations']; ?>"/> + <?php echo _i('help'); ?> <?php echo _t('admin.user.registration.help'); ?> + </div> + </div> + + <div class="form-group"> + <div class="group-controls"> + <?php + $number = count(listUsers()); + echo _t($number > 1 ? 'admin.user.numbers' : 'admin.user.number', $number); + ?> + </div> + </div> + + <div class="form-group form-actions"> + <div class="group-controls"> + <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> + <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + </div> + </div> + </form> + <form method="post" action="<?php echo _url('user', 'create'); ?>"> <legend><?php echo _t('admin.user.create'); ?></legend> diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml index c44202edd..7ae2c7ede 100644 --- a/app/views/user/profile.phtml +++ b/app/views/user/profile.phtml @@ -18,11 +18,11 @@ </div> <div class="form-group"> - <label class="group-name" for="passwordPlain"><?php echo _t('conf.profile.password_form'); ?></label> + <label class="group-name" for="newPasswordPlain"><?php echo _t('conf.profile.password_form'); ?></label> <div class="group-controls"> <div class="stick"> - <input type="password" id="passwordPlain" name="passwordPlain" autocomplete="off" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/> - <a class="btn toggle-password" data-toggle="passwordPlain"><?php echo _i('key'); ?></a> + <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="off" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/> + <a class="btn toggle-password" data-toggle="newPasswordPlain"><?php echo _i('key'); ?></a> </div> <?php echo _i('help'); ?> <?php echo _t('conf.profile.password_format'); ?> <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> @@ -57,4 +57,35 @@ </div> </div> </form> + + <?php if (!FreshRSS_Auth::hasAccess('admin')) { ?> + <form id="crypto-form" method="post" action="<?php echo _url('user', 'delete'); ?>"> + <legend><?php echo _t('conf.profile.delete'); ?></legend> + + <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('conf.profile.delete.warn'); ?></p> + + <div class="form-group"> + <label class="group-name" for="passwordPlain"><?php echo _t('gen.auth.password'); ?></label> + <div class="group-controls"> + <input type="password" id="passwordPlain" required="required" /> + <input type="hidden" id="challenge" name="challenge" /><br /> + <noscript><strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + </div> + </div> + + <div class="form-group form-actions"> + <div class="group-controls"> + <?php + $redirect_url = urlencode(Minz_Url::display( + array('c' => 'user', 'a' => 'profile'), + 'php', true + )); + ?> + <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" /> + <input type="hidden" name="username" id="username" value="<?php echo Minz_Session::param('currentUser', '_'); ?>" /> + <button type="submit" class="btn btn-attention confirm"><?php echo _t('gen.action.remove'); ?></button> + </div> + </div> + </form> + <?php } ?> </div> diff --git a/data/config.default.php b/data/config.default.php index 97085df29..5db933ff8 100644 --- a/data/config.default.php +++ b/data/config.default.php @@ -1,7 +1,7 @@ <?php -# Do not modify this file, which is only a template. -# See `config.php` after the install process is completed. +# Do not modify this file, which defines default values, +# but edit `config.php` instead, after the install process is completed. return array( # Set to `development` to get additional error messages, @@ -77,6 +77,25 @@ return array( # Max number of categories for a user. 'max_categories' => 16384, + # Max number of accounts that anonymous users can create + # 0 for an unlimited number of accounts + # 1 is to not allow user registrations (1 is corresponding to the admin account) + 'max_registrations' => 1, + ), + + # Options used by cURL when making HTTP requests, e.g. when the SimplePie library retrieves feeds. + # http://php.net/manual/function.curl-setopt + 'curl_options' => array( + # Options to disable SSL/TLS certificate check (e.g. for self-signed HTTPS) + //CURLOPT_SSL_VERIFYHOST => 0, + //CURLOPT_SSL_VERIFYPEER => false, + + # Options to use a proxy for retrieving feeds. + //CURLOPT_PROXYTYPE => CURLPROXY_HTTP, + //CURLOPT_PROXY => '127.0.0.1', + //CURLOPT_PROXYPORT => 8080, + //CURLOPT_PROXYAUTH => CURLAUTH_BASIC, + //CURLOPT_PROXYUSERPWD => 'user:password', ), 'db' => array( @@ -85,7 +104,7 @@ return array( 'type' => 'sqlite', # MySQL host. - 'host' => '', + 'host' => 'localhost', # MySQL user. 'user' => '', @@ -97,7 +116,7 @@ return array( 'base' => '', # MySQL table prefix. - 'prefix' => '', + 'prefix' => 'freshrss_', 'pdo_options' => array( //PDO::MYSQL_ATTR_SSL_KEY => '/path/to/client-key.pem', diff --git a/lib/Minz/Extension.php b/lib/Minz/Extension.php index d7ee8fe81..78b8a2725 100644 --- a/lib/Minz/Extension.php +++ b/lib/Minz/Extension.php @@ -168,7 +168,7 @@ class Minz_Extension { $url = '/ext.php?f=' . $file_name_url . '&t=' . $type . '&' . $mtime; - return Minz_Url::display($url); + return Minz_Url::display($url, 'php'); } /** diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index b9eda82a5..bf01bc26f 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -85,26 +85,37 @@ class Minz_Request { } /** - * Détermine la base de l'url - * @return la base de l'url + * Try to guess the base URL from $_SERVER information + * + * @return the base url (e.g. http://example.com/) */ - public static function getBaseUrl($baseUrlSuffix = '') { - $conf = Minz_Configuration::get('system'); - $url = $conf->base_url; - if ($url == '' || !preg_match('%^https?://%i', $url)) { - $url = 'http'; - $host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']; - $port = empty($_SERVER['SERVER_PORT']) ? 80 : $_SERVER['SERVER_PORT']; - if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') { - $url .= 's://' . $host . ($port == 443 ? '' : ':' . $port); - } else { - $url .= '://' . $host . ($port == 80 ? '' : ':' . $port); - } - $url .= isset($_SERVER['REQUEST_URI']) ? dirname($_SERVER['REQUEST_URI']) : ''; + public static function guessBaseUrl() { + $url = 'http'; + $host = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']; + $port = empty($_SERVER['SERVER_PORT']) ? 80 : $_SERVER['SERVER_PORT']; + if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') { + $url .= 's://' . $host . ($port == 443 ? '' : ':' . $port); } else { - $url = rtrim($url, '/\\') . $baseUrlSuffix; + $url .= '://' . $host . ($port == 80 ? '' : ':' . $port); } - return filter_var($url . '/', FILTER_SANITIZE_URL); + if (isset($_SERVER['REQUEST_URI'])) { + $path = $_SERVER['REQUEST_URI']; + $url .= substr($path, -1) === '/' ? substr($path, 0, -1) : dirname($path); + } + + return filter_var($url, FILTER_SANITIZE_URL); + } + + /** + * Return the base_url from configuration and add a suffix if given. + * + * @param $base_url_suffix a string to add at base_url (default: empty string) + * @return the base_url with a suffix. + */ + public static function getBaseUrl($base_url_suffix = '') { + $conf = Minz_Configuration::get('system'); + $url = rtrim($conf->base_url, '/\\') . $base_url_suffix; + return filter_var($url, FILTER_SANITIZE_URL); } /** diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php index 879077d0f..4279b045b 100644 --- a/lib/Minz/Url.php +++ b/lib/Minz/Url.php @@ -25,14 +25,19 @@ class Minz_Url { if ($absolute) { $url_string = Minz_Request::getBaseUrl(PUBLIC_TO_INDEX_PATH); + if ($url_string === PUBLIC_TO_INDEX_PATH) { + $url_string = Minz_Request::guessBaseUrl(); + } } else { $url_string = $isArray ? '.' : PUBLIC_RELATIVE; } if ($isArray) { $url_string .= self::printUri($url, $encodage); - } else { + } elseif ($encodage === 'html') { $url_string = Minz_Helper::htmlspecialchars_utf8($url_string . $url); + } else { + $url_string .= $url; } return $url_string; diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index 54f4c5770..6c0962a9f 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -456,7 +456,7 @@ class SimplePie * @see SimplePie::subscribe_url() * @access private */ - public $permanent_url = null; //FreshRSS + public $permanent_url = null; /** * @var object Instance of SimplePie_File to use as a feed @@ -480,6 +480,13 @@ class SimplePie public $timeout = 10; /** + * @var array Custom curl options + * @see SimplePie::set_curl_options() + * @access private + */ + public $curl_options = array(); + + /** * @var bool Forces fsockopen() to be used for remote files instead * of cURL, even if a new enough version is installed * @see SimplePie::force_fsockopen() @@ -754,7 +761,7 @@ class SimplePie else { $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); - $this->permanent_url = $this->feed_url; //FreshRSS + $this->permanent_url = $this->feed_url; } } @@ -769,7 +776,7 @@ class SimplePie if ($file instanceof SimplePie_File) { $this->feed_url = $file->url; - $this->permanent_url = $this->feed_url; //FreshRSS + $this->permanent_url = $this->feed_url; $this->file =& $file; return true; } @@ -807,6 +814,19 @@ class SimplePie { $this->timeout = (int) $timeout; } + + /** + * Set custom curl options + * + * This allows you to change default curl options + * + * @since 1.0 Beta 3 + * @param array $curl_options Curl options to add to default settings + */ + public function set_curl_options(array $curl_options = array()) + { + $this->curl_options = $curl_options; + } /** * Force SimplePie to use fsockopen() instead of cURL @@ -1251,7 +1271,7 @@ class SimplePie $this->enable_exceptions = $enable; } - function cleanMd5($rss) //FreshRSS + function cleanMd5($rss) { return md5(preg_replace(array('#<(lastBuildDate|pubDate|updated|feedDate|dc:date|slash:comments)>[^<]+</\\1>#', '#<!--.+?-->#s'), '', $rss)); } @@ -1297,7 +1317,7 @@ class SimplePie // Pass whatever was set with config options over to the sanitizer. // Pass the classes in for legacy support; new classes should use the registry instead $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); - $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen); + $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen, $this->curl_options); if (!empty($this->multifeed_url)) { @@ -1342,7 +1362,7 @@ class SimplePie // Fetch the data via SimplePie_File into $this->raw_data if (($fetched = $this->fetch_data($cache)) === true) { - return $this->data['mtime']; //FreshRSS + return $this->data['mtime']; } elseif ($fetched === false) { return false; @@ -1350,7 +1370,7 @@ class SimplePie list($headers, $sniffed) = $fetched; - if (isset($this->data['md5'])) //FreshRSS + if (isset($this->data['md5'])) { $md5 = $this->data['md5']; } @@ -1435,8 +1455,8 @@ class SimplePie $this->data['headers'] = $headers; } $this->data['build'] = SIMPLEPIE_BUILD; - $this->data['mtime'] = time(); //FreshRSS - $this->data['md5'] = empty($md5) ? $this->cleanMd5($this->raw_data) : $md5; //FreshRSS + $this->data['mtime'] = time(); + $this->data['md5'] = empty($md5) ? $this->cleanMd5($this->raw_data) : $md5; // Cache the file if caching is enabled if ($cache && !$cache->save($this)) @@ -1451,7 +1471,7 @@ class SimplePie if (isset($parser)) { // We have an error, just set SimplePie_Misc::error to it and quit - $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); + $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d, encoding %s, URL: %s', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column(), $encoding, $this->feed_url); } else { @@ -1477,7 +1497,7 @@ class SimplePie { // Load the Cache $this->data = $cache->load(); - if ($cache->mtime() + $this->cache_duration > time()) //FreshRSS + if ($cache->mtime() + $this->cache_duration > time()) { $this->raw_data = false; return true; // If the cache is still valid, just return true @@ -1514,71 +1534,58 @@ class SimplePie } } // Check if the cache has been updated - else //if ($cache->mtime() + $this->cache_duration < time()) //FreshRSS removed + else { - // If we have last-modified and/or etag set - //if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) //FreshRSS removed + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + if (isset($this->data['headers']['last-modified'])) { - $headers = array( - 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', - ); - if (isset($this->data['headers']['last-modified'])) - { - $headers['if-modified-since'] = $this->data['headers']['last-modified']; - } - if (isset($this->data['headers']['etag'])) - { - $headers['if-none-match'] = $this->data['headers']['etag']; - } + $headers['if-modified-since'] = $this->data['headers']['last-modified']; + } + if (isset($this->data['headers']['etag'])) + { + $headers['if-none-match'] = $this->data['headers']['etag']; + } - $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); //FreshRSS + $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options)); - if ($file->success) + if ($file->success) + { + if ($file->status_code === 304) { - if ($file->status_code === 304) - { - $cache->touch(); - return true; - } + $cache->touch(); + return true; } - else + } + else + { + $cache->touch(); + $this->error = $file->error; + return !empty($this->data); + } + + $md5 = $this->cleanMd5($file->body); + if ($this->data['md5'] === $md5) { + if ($this->syslog_enabled) { - $cache->touch(); //FreshRSS - $this->error = $file->error; //FreshRSS - return !empty($this->data); //FreshRSS - //unset($file); //FreshRSS removed + syslog(LOG_DEBUG, 'SimplePie MD5 cache match for ' . SimplePie_Misc::url_remove_credentials($this->feed_url)); } - } - { //FreshRSS - $md5 = $this->cleanMd5($file->body); - if ($this->data['md5'] === $md5) { - if ($this->syslog_enabled) - { - syslog(LOG_DEBUG, 'SimplePie MD5 cache match for ' . SimplePie_Misc::url_remove_credentials($this->feed_url)); - } - $cache->touch(); - return true; //Content unchanged even though server did not send a 304 - } else { - if ($this->syslog_enabled) - { - syslog(LOG_DEBUG, 'SimplePie MD5 cache no match for ' . SimplePie_Misc::url_remove_credentials($this->feed_url)); - } - $this->data['md5'] = $md5; + $cache->touch(); + return true; //Content unchanged even though server did not send a 304 + } else { + if ($this->syslog_enabled) + { + syslog(LOG_DEBUG, 'SimplePie MD5 cache no match for ' . SimplePie_Misc::url_remove_credentials($this->feed_url)); } + $this->data['md5'] = $md5; } } - //// If the cache is still valid, just return true - //else //FreshRSS removed - //{ - // $this->raw_data = false; - // return true; - //} - } - // If the cache is empty, delete it + } + // If the cache is empty else { - //$cache->unlink(); //FreshRSS removed - $cache->touch(); //FreshRSS + $cache->touch(); //To keep the date/time of the last tentative update $this->data = array(); } } @@ -1594,7 +1601,7 @@ class SimplePie $headers = array( 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', ); - $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); + $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen, $this->curl_options)); } } // If the file connection has an error, set SimplePie::error to that and quit @@ -1611,15 +1618,15 @@ class SimplePie if (!$locate->is_feed($file)) { - $copyStatusCode = $file->status_code; //FreshRSS - $copyContentType = $file->headers['content-type']; //FreshRSS + $copyStatusCode = $file->status_code; + $copyContentType = $file->headers['content-type']; // We need to unset this so that if SimplePie::set_file() has been called that object is untouched unset($file); try { if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))) { - $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; //FreshRSS + $this->error = "A feed could not be found at `$this->feed_url`; the status code is `$copyStatusCode` and content-type is `$copyContentType`"; $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); return false; } @@ -1634,8 +1641,8 @@ class SimplePie if ($cache) { $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); - $this->data['mtime'] = time(); //FreshRSS - $this->data['md5'] = empty($md5) ? $this->cleanMd5($file->body) : $md5; //FreshRSS + $this->data['mtime'] = time(); + $this->data['md5'] = empty($md5) ? $this->cleanMd5($file->body) : $md5; if (!$cache->save($this)) { trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); @@ -1648,7 +1655,7 @@ class SimplePie } $this->raw_data = $file->body; - $this->permanent_url = $file->permanent_url; //FreshRSS + $this->permanent_url = $file->permanent_url; $headers = $file->headers; $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); $sniffed = $sniffer->get_type(); @@ -1852,7 +1859,7 @@ class SimplePie */ public function subscribe_url($permanent = false) { - if ($permanent) //FreshRSS + if ($permanent) { if ($this->permanent_url !== null) { diff --git a/lib/SimplePie/SimplePie/Cache/File.php b/lib/SimplePie/SimplePie/Cache/File.php index cb4b528c4..72e75a4b6 100644 --- a/lib/SimplePie/SimplePie/Cache/File.php +++ b/lib/SimplePie/SimplePie/Cache/File.php @@ -136,11 +136,7 @@ class SimplePie_Cache_File implements SimplePie_Cache_Base */ public function mtime() { - //if (file_exists($this->name)) //FreshRSS removed - { - return @filemtime($this->name); //FreshRSS - } - //return false; //FreshRSS removed + return @filemtime($this->name); } /** @@ -150,11 +146,7 @@ class SimplePie_Cache_File implements SimplePie_Cache_Base */ public function touch() { - //if (file_exists($this->name)) //FreshRSS removed - { - return @touch($this->name); //FreshRSS - } - //return false; //FreshRSS removed + return @touch($this->name); } /** diff --git a/lib/SimplePie/SimplePie/Decode/HTML/Entities.php b/lib/SimplePie/SimplePie/Decode/HTML/Entities.php index cde06c884..46b3a1dff 100644 --- a/lib/SimplePie/SimplePie/Decode/HTML/Entities.php +++ b/lib/SimplePie/SimplePie/Decode/HTML/Entities.php @@ -169,7 +169,6 @@ class SimplePie_Decode_HTML_Entities case "\x09": case "\x0A": case "\x0B": - case "\x0B": case "\x0C": case "\x20": case "\x3C": diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index 1f9e3d502..45994d102 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -66,7 +66,7 @@ class SimplePie_File var $method = SIMPLEPIE_FILE_SOURCE_NONE; var $permanent_url; //FreshRSS - public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $syslog_enabled = SIMPLEPIE_SYSLOG) + public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = array(), $syslog_enabled = SIMPLEPIE_SYSLOG) { if (class_exists('idna_convert')) { @@ -75,7 +75,7 @@ class SimplePie_File $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } $this->url = $url; - $this->permanent_url = $url; //FreshRSS + $this->permanent_url = $url; $this->useragent = $useragent; if (preg_match('/^http(s)?:\/\//i', $url)) { @@ -113,12 +113,15 @@ class SimplePie_File curl_setopt($fp, CURLOPT_REFERER, $url); curl_setopt($fp, CURLOPT_USERAGENT, $useragent); curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); - curl_setopt($fp, CURLOPT_SSL_VERIFYPEER, false); //FreshRSS if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) { curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); } + foreach ($curl_options as $curl_param => $curl_value) + { + curl_setopt($fp, $curl_param, $curl_value); + } $this->headers = curl_exec($fp); if (curl_errno($fp) === 23 || curl_errno($fp) === 61) @@ -149,7 +152,7 @@ class SimplePie_File $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); $previousStatusCode = $this->status_code; $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS + $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; return; } } diff --git a/lib/SimplePie/SimplePie/Item.php b/lib/SimplePie/SimplePie/Item.php index 7bd96c15f..27e93456e 100644 --- a/lib/SimplePie/SimplePie/Item.php +++ b/lib/SimplePie/SimplePie/Item.php @@ -406,6 +406,30 @@ class SimplePie_Item return null; } } + + /** + * Get the media:thumbnail of the item + * + * Uses `<media:thumbnail>` + * + * + * @return array|null + */ + public function get_thumbnail() + { + if (!isset($this->data['thumbnail'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + $this->data['thumbnail'] = $return[0]['attribs']['']; + } + else + { + $this->data['thumbnail'] = null; + } + } + return $this->data['thumbnail']; + } /** * Get a category for the item @@ -738,31 +762,31 @@ class SimplePie_Item { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) { $this->data['date']['raw'] = $return[0]['data']; } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) { $this->data['date']['raw'] = $return[0]['data']; } @@ -2733,7 +2757,9 @@ class SimplePie_Item { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + if (isset($thumbnail['attribs']['']['url'])) { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } } if (is_array($thumbnails)) { diff --git a/lib/SimplePie/SimplePie/Locator.php b/lib/SimplePie/SimplePie/Locator.php index 4e5f7c1ca..ba4a843b0 100644 --- a/lib/SimplePie/SimplePie/Locator.php +++ b/lib/SimplePie/SimplePie/Locator.php @@ -148,7 +148,7 @@ class SimplePie_Locator { $sniffer = $this->registry->create('Content_Type_Sniffer', array($file)); $sniffed = $sniffer->get_type(); - if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml', 'application/x-rss+xml'))) //FreshRSS + if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml', 'application/x-rss+xml'))) { return true; } diff --git a/lib/SimplePie/SimplePie/Misc.php b/lib/SimplePie/SimplePie/Misc.php index 956a284cb..9e7ac4fa8 100644 --- a/lib/SimplePie/SimplePie/Misc.php +++ b/lib/SimplePie/SimplePie/Misc.php @@ -79,8 +79,8 @@ class SimplePie_Misc public static function absolutize_url($relative, $base) { - if (substr($relative, 0, 2) === '//') //FreshRSS: disable absolutize_url for "//www.example.net" which will pick HTTP or HTTPS automatically - { + if (substr($relative, 0, 2) === '//') + {//Allow protocol-relative URLs "//www.example.net" which will pick HTTP or HTTPS automatically return $relative; } $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); @@ -128,7 +128,7 @@ class SimplePie_Misc { $attribs[$j][2] = $attribs[$j][1]; } - $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); //FreshRSS + $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); } } } @@ -142,7 +142,7 @@ class SimplePie_Misc foreach ($element['attribs'] as $key => $value) { $key = strtolower($key); - $full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"'; //FreshRSS + $full .= " $key=\"" . htmlspecialchars($value['data'], ENT_COMPAT, 'UTF-8') . '"'; } if ($element['self_closing']) { diff --git a/lib/SimplePie/SimplePie/Parse/Date.php b/lib/SimplePie/SimplePie/Parse/Date.php index ba7c0703e..50bb5cffa 100644 --- a/lib/SimplePie/SimplePie/Parse/Date.php +++ b/lib/SimplePie/SimplePie/Parse/Date.php @@ -173,7 +173,7 @@ class SimplePie_Parse_Date 'aug' => 8, 'august' => 8, 'sep' => 9, - 'september' => 8, + 'september' => 9, 'oct' => 10, 'october' => 10, 'nov' => 11, @@ -331,8 +331,8 @@ class SimplePie_Parse_Date 'CCT' => 23400, 'CDT' => -18000, 'CEDT' => 7200, - 'CEST' => 7200, //FreshRSS 'CET' => 3600, + 'CEST' => 7200, 'CGST' => -7200, 'CGT' => -10800, 'CHADT' => 49500, @@ -721,7 +721,7 @@ class SimplePie_Parse_Date { $output .= substr($string, $position, $pos - $position); $position = $pos + 1; - if ($string[$pos - 1] !== '\\') + if ($pos === 0 || $string[$pos - 1] !== '\\') { $depth++; while ($depth && $position < $length) diff --git a/lib/SimplePie/SimplePie/Registry.php b/lib/SimplePie/SimplePie/Registry.php index bd9c1f535..dac55e34e 100755 --- a/lib/SimplePie/SimplePie/Registry.php +++ b/lib/SimplePie/SimplePie/Registry.php @@ -113,7 +113,7 @@ class SimplePie_Registry */ public function register($type, $class, $legacy = false) { - if (!is_subclass_of($class, $this->default[$type])) + if (!@is_subclass_of($class, $this->default[$type])) { return false; } @@ -222,4 +222,4 @@ class SimplePie_Registry $result = call_user_func_array(array($class, $method), $parameters); return $result; } -}
\ No newline at end of file +} diff --git a/lib/SimplePie/SimplePie/Sanitize.php b/lib/SimplePie/SimplePie/Sanitize.php index e7c9f925f..a6863ec03 100644 --- a/lib/SimplePie/SimplePie/Sanitize.php +++ b/lib/SimplePie/SimplePie/Sanitize.php @@ -249,7 +249,7 @@ class SimplePie_Sanitize { if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) { - $data = htmlspecialchars_decode($data, ENT_QUOTES); //FreshRSS + $data = htmlspecialchars_decode($data, ENT_QUOTES); if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) { $type |= SIMPLEPIE_CONSTRUCT_HTML; @@ -280,7 +280,7 @@ class SimplePie_Sanitize $document->loadHTML($data); restore_error_handler(); - $xpath = new DOMXPath($document); //FreshRSS + $xpath = new DOMXPath($document); // Strip comments if ($this->strip_comments) diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 191a58f35..c99e2c7e8 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -143,6 +143,7 @@ function customSimplePie() { $simplePie->set_cache_location(CACHE_PATH); $simplePie->set_cache_duration($limits['cache_duration']); $simplePie->set_timeout($limits['timeout']); + $simplePie->set_curl_options($system_conf->curl_options); $simplePie->strip_htmltags(array( 'base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', @@ -195,17 +196,27 @@ function sanitizeHTML($data, $base = '') { /* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */ function get_content_by_parsing ($url, $path) { - require_once (LIB_PATH . '/lib_phpQuery.php'); + require_once(LIB_PATH . '/lib_phpQuery.php'); Minz_Log::notice('FreshRSS GET ' . SimplePie_Misc::url_remove_credentials($url)); - $html = file_get_contents ($url); + $html = file_get_contents($url); if ($html) { - $doc = phpQuery::newDocument ($html); - $content = $doc->find ($path); + $doc = phpQuery::newDocument($html); + $content = $doc->find($path); + + foreach (pq('img[data-src]') as $img) { + $imgP = pq($img); + $dataSrc = $imgP->attr('data-src'); + if (strlen($dataSrc) > 4) { + $imgP->attr('src', $dataSrc); + $imgP->removeAttr('data-src'); + } + } + return sanitizeHTML($content->__toString(), $url); } else { - throw new Exception (); + throw new Exception(); } } @@ -256,6 +267,22 @@ function listUsers() { /** + * Return if the maximum number of registrations has been reached. + * + * Note a max_regstrations of 0 means there is no limit. + * + * @return true if number of users >= max registrations, false else. + */ +function max_registrations_reached() { + $system_conf = Minz_Configuration::get('system'); + $limit_registrations = $system_conf->limits['max_registrations']; + $number_accounts = count(listUsers()); + + return $limit_registrations > 0 && $number_accounts >= $limit_registrations; +} + + +/** * Register and return the configuration for a given user. * * Note this function has been created to generate temporary configuration |
