Merge branch 'FreshRSS/dev' into dev

This commit is contained in:
Alexandre Alapetite
2015-07-23 11:59:32 +02:00
60 changed files with 773 additions and 235 deletions

View File

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

View File

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

View File

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

View File

@@ -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') . ' · ');
}
}

View File

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

View File

@@ -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') {

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

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

View File

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

View File

@@ -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');

View File

@@ -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');

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 lon peut créer un nombre infini de comptes',
'number' => 'Nombre max de comptes',
),
'title' => 'Gestion des utilisateurs',
'user_list' => 'Liste des utilisateurs',
'username' => 'Nom dutilisateur',

View File

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

View File

@@ -102,6 +102,7 @@ return array(
'_' => 'Lutilisateur %s a été supprimé.',
'error' => 'Lutilisateur %s ne peut pas être supprimé.',
),
'set_registration' => 'Le nombre maximal de comptes a été mis à jour.',
),
'profile' => array(
'error' => 'Votre profil na pas pu être mis à jour',

View File

@@ -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 lauthentification',
'username' => 'Nom dutilisateur',
'username_admin' => 'Nom dutilisateur administrateur',
'username' => array(
'_' => 'Nom dutilisateur',
'admin' => 'Nom dutilisateur administrateur',
'format' => '<small>16 caractères alphanumériques maximum</small>',
),
'will_reset' => 'Le système dauthentification va être réinitialisé : un formulaire sera utilisé à la place de Persona.',
),
'date' => array(

View File

@@ -4,7 +4,9 @@ return array(
'action' => array(
'finish' => 'Terminer linstallation',
'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à létape suivante.',
'keep_install' => 'Garder lancienne 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',

View File

@@ -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',
),

View File

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

View File

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

View File

@@ -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'); ?>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -168,7 +168,7 @@ class Minz_Extension {
$url = '/ext.php?f=' . $file_name_url .
'&amp;t=' . $type .
'&amp;' . $mtime;
return Minz_Url::display($url);
return Minz_Url::display($url, 'php');
}
/**

View File

@@ -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);
}
/**

View File

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

View File

@@ -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
@@ -479,6 +479,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
@@ -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;
}
}
else
{
$cache->touch(); //FreshRSS
$this->error = $file->error; //FreshRSS
return !empty($this->data); //FreshRSS
//unset($file); //FreshRSS removed
$cache->touch();
return true;
}
}
{ //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;
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)
{
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;
}
}
//// 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)
{

View File

@@ -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);
}
/**

View File

@@ -169,7 +169,6 @@ class SimplePie_Decode_HTML_Entities
case "\x09":
case "\x0A":
case "\x0B":
case "\x0B":
case "\x0C":
case "\x20":
case "\x3C":

View File

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

View File

@@ -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,6 +762,18 @@ class SimplePie_Item
{
$this->data['date']['raw'] = $return[0]['data'];
}
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_DC_11, 'date'))
{
$this->data['date']['raw'] = $return[0]['data'];
}
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_10, 'updated'))
{
$this->data['date']['raw'] = $return[0]['data'];
@@ -754,18 +790,6 @@ class SimplePie_Item
{
$this->data['date']['raw'] = $return[0]['data'];
}
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_DC_11, 'date'))
{
$this->data['date']['raw'] = $return[0]['data'];
}
elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date'))
{
$this->data['date']['raw'] = $return[0]['data'];
}
if (!empty($this->data['date']['raw']))
{
@@ -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))
{

View File

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

View File

@@ -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'])
{

View File

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

View File

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

View File

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

View File

@@ -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();
}
}
@@ -255,6 +266,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.
*