aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controllers/authController.php13
-rwxr-xr-xapp/Controllers/feedController.php98
-rw-r--r--app/Controllers/importExportController.php6
-rw-r--r--app/Controllers/subscriptionController.php6
-rw-r--r--app/Controllers/updateController.php2
-rw-r--r--app/Controllers/userController.php101
-rw-r--r--app/Models/CategoryDAO.php2
-rw-r--r--app/Models/ConfigurationSetter.php8
-rw-r--r--app/Models/EntryDAO.php17
-rw-r--r--app/Models/Feed.php150
-rw-r--r--app/SQL/install.sql.mysql.php2
-rw-r--r--app/SQL/install.sql.sqlite.php2
-rw-r--r--app/i18n/cz/admin.php7
-rw-r--r--app/i18n/cz/conf.php5
-rw-r--r--app/i18n/cz/feedback.php1
-rw-r--r--app/i18n/cz/gen.php18
-rw-r--r--app/i18n/cz/install.php6
-rw-r--r--app/i18n/cz/sub.php1
-rw-r--r--app/i18n/de/admin.php7
-rw-r--r--app/i18n/de/conf.php4
-rw-r--r--app/i18n/de/feedback.php1
-rw-r--r--app/i18n/de/gen.php18
-rw-r--r--app/i18n/de/install.php6
-rw-r--r--app/i18n/de/sub.php1
-rw-r--r--app/i18n/en/admin.php7
-rw-r--r--app/i18n/en/conf.php4
-rw-r--r--app/i18n/en/feedback.php1
-rw-r--r--app/i18n/en/gen.php18
-rw-r--r--app/i18n/en/install.php6
-rw-r--r--app/i18n/en/sub.php1
-rw-r--r--app/i18n/fr/admin.php7
-rw-r--r--app/i18n/fr/conf.php4
-rw-r--r--app/i18n/fr/feedback.php1
-rw-r--r--app/i18n/fr/gen.php18
-rw-r--r--app/i18n/fr/install.php6
-rw-r--r--app/i18n/fr/sub.php3
-rw-r--r--app/install.php182
-rw-r--r--app/views/auth/formLogin.phtml4
-rw-r--r--app/views/auth/personaLogin.phtml4
-rw-r--r--app/views/auth/register.phtml38
-rw-r--r--app/views/auth/reset.phtml2
-rw-r--r--app/views/configure/display.phtml2
-rw-r--r--app/views/configure/reading.phtml2
-rw-r--r--app/views/feed/add.phtml2
-rw-r--r--app/views/helpers/feed/update.phtml16
-rw-r--r--app/views/subscription/index.phtml4
-rw-r--r--app/views/user/manage.phtml28
-rw-r--r--app/views/user/profile.phtml37
48 files changed, 761 insertions, 118 deletions
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 8db273aca..ec3dce777 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;
}
@@ -168,6 +168,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
// Ok, feed has been added in database. Now we have to refresh entries.
$feed->_id($id);
$feed->faviconPrepare();
+ //$feed->pubSubHubbubPrepare(); //TODO: prepare PubSubHubbub already when adding the feed
$is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0;
@@ -261,12 +262,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
* This action actualizes entries from one or several feeds.
*
* Parameters are:
- * - id (default: false)
+ * - id (default: false): Feed ID
+ * - url (default: false): Feed URL
* - force (default: false)
- * If id is not specified, all the feeds are actualized. But if force is
+ * If id and url are not specified, all the feeds are actualized. But if force is
* false, process stops at 10 feeds to avoid time execution problem.
*/
- public function actualizeAction() {
+ public function actualizeAction($simplePiePush = null) {
@set_time_limit(300);
$feedDAO = FreshRSS_Factory::createFeedDao();
@@ -274,14 +276,15 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
Minz_Session::_param('actualize_feeds', false);
$id = Minz_Request::param('id');
+ $url = Minz_Request::param('url');
$force = Minz_Request::param('force');
// Create a list of feeds to actualize.
// If id is set and valid, corresponding feed is added to the list but
// alone in order to automatize further process.
$feeds = array();
- if ($id) {
- $feed = $feedDAO->searchById($id);
+ if ($id || $url) {
+ $feed = $id ? $feedDAO->searchById($id) : $feedDAO->searchByUrl($url);
if ($feed) {
$feeds[] = $feed;
}
@@ -293,20 +296,36 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1);
$date_min = time() - (3600 * 24 * 30 * $nb_month_old);
+ // PubSubHubbub support
+ $pubsubhubbubEnabledGeneral = FreshRSS_Context::$system_conf->pubsubhubbub_enabled;
+ $pshbMinAge = time() - (3600 * 24); //TODO: Make a configuration.
+
$updated_feeds = 0;
$is_read = FreshRSS_Context::$user_conf->mark_when['reception'] ? 1 : 0;
foreach ($feeds as $feed) {
+ $url = $feed->url(); //For detection of HTTP 301
+
+ $pubSubHubbubEnabled = $pubsubhubbubEnabledGeneral && $feed->pubSubHubbubEnabled();
+ if ((!$simplePiePush) && (!$id) && $pubSubHubbubEnabled && ($feed->lastUpdate() > $pshbMinAge)) {
+ $text = 'Skip pull of feed using PubSubHubbub: ' . $url;
+ //Minz_Log::debug($text);
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ continue; //When PubSubHubbub is used, do not pull refresh so often
+ }
+
if (!$feed->lock()) {
Minz_Log::notice('Feed already being actualized: ' . $feed->url());
continue;
}
- $url = $feed->url(); //For detection of HTTP 301
try {
- // Load entries
- $feed->load(false);
+ if ($simplePiePush) {
+ $feed->loadEntries($simplePiePush); //Used by PubSubHubbub
+ } else {
+ $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;
@@ -368,6 +387,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
continue;
}
+ if ($pubSubHubbubEnabled && !$simplePiePush) { //We use push, but have discovered an article by pull!
+ $text = 'An article was discovered by pull although we use PubSubHubbub!: Feed ' . $url . ' GUID ' . $entry->guid();
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ Minz_Log::warning($text);
+ $pubSubHubbubEnabled = false;
+ $feed->pubSubHubbubError(true);
+ }
+
if (!$entryDAO->hasTransaction()) {
$entryDAO->beginTransaction();
}
@@ -398,13 +425,32 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
$entryDAO->commit();
}
- if ($feed->url() !== $url) {
- // HTTP 301 Moved Permanently
+ if ($feed->hubUrl() && $feed->selfUrl()) { //selfUrl has priority for PubSubHubbub
+ if ($feed->selfUrl() !== $url) { //https://code.google.com/p/pubsubhubbub/wiki/MovingFeedsOrChangingHubs
+ $selfUrl = checkUrl($feed->selfUrl());
+ if ($selfUrl) {
+ Minz_Log::debug('PubSubHubbub unsubscribe ' . $feed->url());
+ if (!$feed->pubSubHubbubSubscribe(false)) { //Unsubscribe
+ Minz_Log::warning('Error while PubSubHubbub unsubscribing from ' . $feed->url());
+ }
+ $feed->_url($selfUrl, false);
+ Minz_Log::notice('Feed ' . $url . ' canonical address moved to ' . $feed->url());
+ $feedDAO->updateFeed($feed->id(), array('url' => $feed->url()));
+ }
+ }
+ }
+ elseif ($feed->url() !== $url) { // HTTP 301 Moved Permanently
Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url());
$feedDAO->updateFeed($feed->id(), array('url' => $feed->url()));
}
$feed->faviconPrepare();
+ if ($pubsubhubbubEnabledGeneral && $feed->pubSubHubbubPrepare()) {
+ Minz_Log::notice('PubSubHubbub subscribe ' . $feed->url());
+ if (!$feed->pubSubHubbubSubscribe(true)) { //Subscribe
+ Minz_Log::warning('Error while PubSubHubbub subscribing to ' . $feed->url());
+ }
+ }
$feed->unlock();
$updated_feeds++;
unset($feed);
@@ -427,20 +473,20 @@ class FreshRSS_feed_Controller extends Minz_ActionController {
Minz_Session::_param('notification', $notif);
// No layout in ajax request.
$this->view->_useLayout(false);
- return;
- }
-
- // Redirect to the main page with correct notification.
- if ($updated_feeds === 1) {
- $feed = reset($feeds);
- Minz_Request::good(_t('feedback.sub.feed.actualized', $feed->name()), array(
- 'params' => array('get' => 'f_' . $feed->id())
- ));
- } elseif ($updated_feeds > 1) {
- Minz_Request::good(_t('feedback.sub.feed.n_actualized', $updated_feeds), array());
} else {
- Minz_Request::good(_t('feedback.sub.feed.no_refresh'), array());
+ // Redirect to the main page with correct notification.
+ if ($updated_feeds === 1) {
+ $feed = reset($feeds);
+ Minz_Request::good(_t('feedback.sub.feed.actualized', $feed->name()), array(
+ 'params' => array('get' => 'f_' . $feed->id())
+ ));
+ } elseif ($updated_feeds > 1) {
+ Minz_Request::good(_t('feedback.sub.feed.n_actualized', $updated_feeds), array());
+ } else {
+ Minz_Request::good(_t('feedback.sub.feed.no_refresh'), array());
+ }
}
+ return $updated_feeds;
}
/**
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..992a3a387 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) {
@@ -359,10 +362,11 @@ class FreshRSS_ConfigurationSetter {
continue;
}
+ $value = intval($value);
$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/Models/Feed.php b/app/Models/Feed.php
index 85fb173ec..bf7ed3967 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -19,6 +19,8 @@ class FreshRSS_Feed extends Minz_Model {
private $ttl = -2;
private $hash = null;
private $lockPath = '';
+ private $hubUrl = '';
+ private $selfUrl = '';
public function __construct($url, $validate=true) {
if ($validate) {
@@ -49,6 +51,12 @@ class FreshRSS_Feed extends Minz_Model {
public function url() {
return $this->url;
}
+ public function selfUrl() {
+ return $this->selfUrl;
+ }
+ public function hubUrl() {
+ return $this->hubUrl;
+ }
public function category() {
return $this->category;
}
@@ -96,6 +104,16 @@ class FreshRSS_Feed extends Minz_Model {
public function ttl() {
return $this->ttl;
}
+ // public function ttlExpire() {
+ // $ttl = $this->ttl;
+ // if ($ttl == -2) { //Default
+ // $ttl = FreshRSS_Context::$user_conf->ttl_default;
+ // }
+ // if ($ttl == -1) { //Never
+ // $ttl = 64000000; //~2 years. Good enough for PubSubHubbub logic
+ // }
+ // return $this->lastUpdate + $ttl;
+ // }
public function nbEntries() {
if ($this->nbEntries < 0) {
$feedDAO = FreshRSS_Factory::createFeedDao();
@@ -226,6 +244,11 @@ class FreshRSS_Feed extends Minz_Model {
throw new FreshRSS_Feed_Exception(($errorMessage == '' ? 'Feed error' : $errorMessage) . ' [' . $url . ']');
}
+ $links = $feed->get_links('self');
+ $this->selfUrl = isset($links[0]) ? $links[0] : null;
+ $links = $feed->get_links('hub');
+ $this->hubUrl = isset($links[0]) ? $links[0] : null;
+
if ($loadDetails) {
// si on a utilisé l'auto-discover, notre url va avoir changé
$subscribe_url = $feed->subscribe_url(false);
@@ -259,7 +282,7 @@ class FreshRSS_Feed extends Minz_Model {
}
}
- private function loadEntries($feed) {
+ public function loadEntries($feed) {
$entries = array();
foreach ($feed->get_items() as $item) {
@@ -333,4 +356,129 @@ class FreshRSS_Feed extends Minz_Model {
function unlock() {
@unlink($this->lockPath);
}
+
+ //<PubSubHubbub>
+
+ function pubSubHubbubEnabled() {
+ $url = $this->selfUrl ? $this->selfUrl : $this->url;
+ $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json';
+ if ($hubFile = @file_get_contents($hubFilename)) {
+ $hubJson = json_decode($hubFile, true);
+ if ($hubJson && empty($hubJson['error']) &&
+ (empty($hubJson['lease_end']) || $hubJson['lease_end'] > time())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function pubSubHubbubError($error = true) {
+ $url = $this->selfUrl ? $this->selfUrl : $this->url;
+ $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json';
+ $hubFile = @file_get_contents($hubFilename);
+ $hubJson = $hubFile ? json_decode($hubFile, true) : array();
+ if (!isset($hubJson['error']) || $hubJson['error'] !== (bool)$error) {
+ $hubJson['error'] = (bool)$error;
+ file_put_contents($hubFilename, json_encode($hubJson));
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t"
+ . 'Set error to ' . ($error ? 1 : 0) . ' for ' . $url . "\n", FILE_APPEND);
+ }
+ return false;
+ }
+
+ function pubSubHubbubPrepare() {
+ $key = '';
+ if (FreshRSS_Context::$system_conf->base_url && $this->hubUrl && $this->selfUrl && @is_dir(PSHB_PATH)) {
+ $path = PSHB_PATH . '/feeds/' . base64url_encode($this->selfUrl);
+ $hubFilename = $path . '/!hub.json';
+ if ($hubFile = @file_get_contents($hubFilename)) {
+ $hubJson = json_decode($hubFile, true);
+ if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key'])) {
+ $text = 'Invalid JSON for PubSubHubbub: ' . $this->url;
+ Minz_Log::warning($text);
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ return false;
+ }
+ if ((!empty($hubJson['lease_end'])) && ($hubJson['lease_end'] < (time() + (3600 * 23)))) { //TODO: Make a better policy
+ $text = 'PubSubHubbub lease ends at '
+ . date('c', empty($hubJson['lease_end']) ? time() : $hubJson['lease_end'])
+ . ' and needs renewal: ' . $this->url;
+ Minz_Log::warning($text);
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ $key = $hubJson['key']; //To renew our lease
+ } elseif (((!empty($hubJson['error'])) || empty($hubJson['lease_end'])) &&
+ (empty($hubJson['lease_start']) || $hubJson['lease_start'] < time() - (3600 * 23))) { //Do not renew too often
+ $key = $hubJson['key']; //To renew our lease
+ }
+ } else {
+ @mkdir($path, 0777, true);
+ $key = sha1($path . FreshRSS_Context::$system_conf->salt . uniqid(mt_rand(), true));
+ $hubJson = array(
+ 'hub' => $this->hubUrl,
+ 'key' => $key,
+ );
+ file_put_contents($hubFilename, json_encode($hubJson));
+ @mkdir(PSHB_PATH . '/keys/');
+ file_put_contents(PSHB_PATH . '/keys/' . $key . '.txt', base64url_encode($this->selfUrl));
+ $text = 'PubSubHubbub prepared for ' . $this->url;
+ Minz_Log::debug($text);
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . $text . "\n", FILE_APPEND);
+ }
+ $currentUser = Minz_Session::param('currentUser');
+ if (ctype_alnum($currentUser) && !file_exists($path . '/' . $currentUser . '.txt')) {
+ touch($path . '/' . $currentUser . '.txt');
+ }
+ }
+ return $key;
+ }
+
+ //Parameter true to subscribe, false to unsubscribe.
+ function pubSubHubbubSubscribe($state) {
+ if (FreshRSS_Context::$system_conf->base_url && $this->hubUrl && $this->selfUrl) {
+ $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($this->selfUrl) . '/!hub.json';
+ $hubFile = @file_get_contents($hubFilename);
+ if ($hubFile === false) {
+ Minz_Log::warning('JSON not found for PubSubHubbub: ' . $this->url);
+ return false;
+ }
+ $hubJson = json_decode($hubFile, true);
+ if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key'])) {
+ Minz_Log::warning('Invalid JSON for PubSubHubbub: ' . $this->url);
+ return false;
+ }
+ $callbackUrl = checkUrl(FreshRSS_Context::$system_conf->base_url . 'api/pshb.php?k=' . $hubJson['key']);
+ if ($callbackUrl == '') {
+ Minz_Log::warning('Invalid callback for PubSubHubbub: ' . $this->url);
+ return false;
+ }
+ $ch = curl_init();
+ curl_setopt_array($ch, array(
+ CURLOPT_URL => $this->hubUrl,
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_USERAGENT => _t('gen.freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ')',
+ CURLOPT_POSTFIELDS => 'hub.verify=sync'
+ . '&hub.mode=' . ($state ? 'subscribe' : 'unsubscribe')
+ . '&hub.topic=' . urlencode($this->selfUrl)
+ . '&hub.callback=' . urlencode($callbackUrl)
+ )
+ );
+ $response = curl_exec($ch);
+ $info = curl_getinfo($ch);
+
+ file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" .
+ 'PubSubHubbub ' . ($state ? 'subscribe' : 'unsubscribe') . ' to ' . $this->selfUrl .
+ ' with callback ' . $callbackUrl . ': ' . $info['http_code'] . ' ' . $response . "\n", FILE_APPEND);
+
+ if (!$state) { //unsubscribe
+ $hubJson['lease_end'] = time() - 60;
+ file_put_contents($hubFilename, json_encode($hubJson));
+ }
+
+ return substr($info['http_code'], 0, 1) == '2';
+ }
+ return false;
+ }
+
+ //</PubSubHubbub>
}
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..4ca56cc37 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' => 'Zatím je vytvořen %d účet',
+ 'numbers' => 'Zatím je vytvořeno %d účtů',
'password_form' => 'Heslo<br /><small>(pro přihlášení webovým formulářem)</small>',
'password_format' => 'Alespoň 7 znaků',
+ 'registration' => array(
+ 'allow' => 'Povolit vytváření účtů',
+ 'help' => '0 znamená žádná omezení účtu',
+ 'number' => 'Maximální počet účtů',
+ ),
'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 29fb1e4d4..823ab1ea3 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(
+ '_' => 'Smazání účtu',
+ 'warn' => 'Váš účet bude smazán spolu se všemi souvisejícími daty',
+ ),
'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>',
@@ -84,6 +88,7 @@ return array(
'articles_per_page' => 'Počet článků na stranu',
'auto_load_more' => 'Načítat další články dole na stránce',
'auto_remove_article' => 'Po přečtení články schovat',
+ 'mark_updated_article_unread' => 'Označte aktualizované položky jako nepřečtené',
'confirm_enabled' => 'Vyžadovat potvrzení pro akci “označit vše jako přečtené”',
'display_articles_unfolded' => 'Ve výchozím stavu zobrazovat články otevřené',
'display_categories_unfolded' => 'Ve výchozím stavu zobrazovat kategorie zavřené',
diff --git a/app/i18n/cz/feedback.php b/app/i18n/cz/feedback.php
index b75a4a15a..5ba64b938 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' => 'Maximální počet účtů byl změněn',
),
'profile' => array(
'error' => 'Váš profil nelze změnit',
diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php
index 7c333a9c8..138def772 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(
+ '_' => 'Nový účet',
+ 'ask' => 'Vytvořit účet?',
+ 'title' => 'Vytvoření účtu',
+ ),
'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..a8bc62909 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' => 'Zachovat předchozí instalaci',
'next_step' => 'Přejít na další krok',
+ 'reinstall' => 'Reinstalovat FreshRSS',
),
'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' => 'Zjistili jsme, že FreshRSS je již nainstalován!',
'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' => 'Reinstalací FreshRSS ztratíte předchozí konfiguraci. Opravdu chcete pokračovat?',
+ ),
'language' => array(
'_' => 'Jazyk',
'choose' => 'Vyberte jazyk FreshRSS',
diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php
index 78712506c..cea0541e3 100644
--- a/app/i18n/cz/sub.php
+++ b/app/i18n/cz/sub.php
@@ -37,6 +37,7 @@ return array(
'url' => 'URL kanálu',
'validator' => 'Zkontrolovat platnost kanálu',
'website' => 'URL webové stránky',
+ 'pubsubhubbub' => 'Okamžité oznámení s PubSubHubbub',
),
'import_export' => array(
'export' => 'Export',
diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php
index c0cbf6787..68dcc2ebf 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' => 'Es wurde bis jetzt %d Account erstellt',
+ 'numbers' => 'Es wurden bis jetzt %d Accounts erstellt',
'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>',
'password_format' => 'mindestens 7 Zeichen',
+ 'registration' => array(
+ 'allow' => 'Erlaube die Accounterstellung',
+ 'help' => '0 meint, dass es kein Account Limit gibt',
+ 'number' => 'Maximale Anzahl von Accounts',
+ ),
'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..c1a762f12 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(
+ '_' => 'Accountlöschung',
+ 'warn' => 'Dein Account und alle damit bezogenen Daten werden gelöscht.',
+ ),
'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..e92dacfe9 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' => 'Die maximale Anzahl von Accounts wurde aktualisiert.',
),
'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..de2d846c5 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(
+ '_' => 'Neuer Account',
+ 'ask' => 'Erstelle einen Account?',
+ 'title' => 'Accounterstellung',
+ ),
'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..286272e71 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' => 'Vorherige Installation beibehalten (Daten)',
'next_step' => 'Zum nächsten Schritt springen',
+ 'reinstall' => 'Neuinstallation von FreshRSS',
),
'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' => 'Wir haben festgestellt, dass FreshRSS bereits installiert wurde!',
'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' => 'Du wirst deine vorherige Konfiguration (Daten) verlieren FreshRSS. Bist du sicher, dass du fortfahren willst?',
+ ),
'language' => array(
'_' => 'Sprache',
'choose' => 'Wählen Sie eine Sprache für FreshRSS',
diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php
index 0479b8f46..0f05a5635 100644
--- a/app/i18n/de/sub.php
+++ b/app/i18n/de/sub.php
@@ -37,6 +37,7 @@ return array(
'url' => 'Feed-URL',
'validator' => 'Überprüfen Sie die Gültigkeit des Feeds',
'website' => 'Webseiten-URL',
+ 'pubsubhubbub' => 'Sofortbenachrichtigung mit PubSubHubbub',
),
'import_export' => array(
'export' => 'Exportieren',
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/en/sub.php b/app/i18n/en/sub.php
index 8fe150bb2..aaaa02827 100644
--- a/app/i18n/en/sub.php
+++ b/app/i18n/en/sub.php
@@ -37,6 +37,7 @@ return array(
'url' => 'Feed URL',
'validator' => 'Check the validity of the feed',
'website' => 'Website URL',
+ 'pubsubhubbub' => 'Instant notification with PubSubHubbub',
),
'import_export' => array(
'export' => 'Export',
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 a3f7c4d6d..e3631eb8b 100644
--- a/app/i18n/fr/sub.php
+++ b/app/i18n/fr/sub.php
@@ -35,8 +35,9 @@ 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',
),
'import_export' => array(
'export' => 'Exporter',
diff --git a/app/install.php b/app/install.php
index 3cdd59d11..5a774d7c1 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'],
@@ -165,14 +209,14 @@ function saveStep3() {
$_SESSION['bd_user'] = $_POST['user'];
$_SESSION['bd_password'] = $_POST['pass'];
$_SESSION['bd_prefix'] = substr($_POST['prefix'], 0, 16);
- $_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] .(empty($_SESSION['default_user']) ? '' :($_SESSION['default_user'] . '_'));
+ $_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] . (empty($_SESSION['default_user']) ? '' : ($_SESSION['default_user'] . '_'));
}
- //TODO: load `config.default.php` as default
+ // We use dirname to remove the /i part
+ $base_url = dirname(Minz_Request::guessBaseUrl());
$config_array = array(
- 'environment' => 'production',
- 'simplepie_syslog_enabled' => true,
'salt' => $_SESSION['salt'],
+ 'base_url' => $base_url,
'title' => $_SESSION['title'],
'default_user' => $_SESSION['default_user'],
'auth_type' => $_SESSION['auth_type'],
@@ -183,7 +227,9 @@ function saveStep3() {
'password' => $_SESSION['bd_password'],
'base' => $_SESSION['bd_base'],
'prefix' => $_SESSION['bd_prefix'],
+ 'pdo_options' => array(),
),
+ 'pubsubhubbub_enabled' => server_is_public($base_url),
);
@unlink(join_path(DATA_PATH, 'config.php')); //To avoid access-rights problems
@@ -300,6 +346,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']) &&
@@ -427,7 +500,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); ?>
@@ -439,10 +512,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>
@@ -535,8 +608,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 } ?>
@@ -544,6 +647,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>
@@ -557,28 +661,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'));
@@ -597,7 +701,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'); ?>
@@ -608,7 +712,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>
@@ -636,7 +740,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');
@@ -644,29 +748,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>
@@ -675,6 +773,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>
@@ -687,7 +786,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"' : ''; ?>>
@@ -708,35 +807,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>
@@ -756,10 +855,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>
@@ -770,7 +869,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
}
@@ -790,6 +889,7 @@ default:
saveLanguage();
break;
case 1:
+ saveStep1();
break;
case 2:
saveStep2();
@@ -829,7 +929,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/configure/display.phtml b/app/views/configure/display.phtml
index 91b0b8189..db6d7951c 100644
--- a/app/views/configure/display.phtml
+++ b/app/views/configure/display.phtml
@@ -107,7 +107,7 @@
</div>
<div class="form-group">
- <label class="group-name" for="posts_per_page"><?php echo _t('conf.display.notif_html5.timeout'); ?></label>
+ <label class="group-name" for="html5_notif_timeout"><?php echo _t('conf.display.notif_html5.timeout'); ?></label>
<div class="group-controls">
<input type="number" id="html5_notif_timeout" name="html5_notif_timeout" value="<?php echo FreshRSS_Context::$user_conf->html5_notif_timeout; ?>" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->html5_notif_timeout; ?>"/> <?php echo _t('conf.display.notif_html5.seconds'); ?>
</div>
diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml
index 1b7a101df..9c54b6bd5 100644
--- a/app/views/configure/reading.phtml
+++ b/app/views/configure/reading.phtml
@@ -9,7 +9,7 @@
<div class="form-group">
<label class="group-name" for="posts_per_page"><?php echo _t('conf.reading.articles_per_page'); ?></label>
<div class="group-controls">
- <input type="number" id="posts_per_page" name="posts_per_page" value="<?php echo FreshRSS_Context::$user_conf->posts_per_page; ?>" min="5" max="50" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->posts_per_page; ?>"/>
+ <input type="number" id="posts_per_page" name="posts_per_page" value="<?php echo FreshRSS_Context::$user_conf->posts_per_page; ?>" min="5" max="500" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->posts_per_page; ?>"/>
<?php echo _i('help'); ?> <?php echo _t('conf.reading.number_divided_when_reader'); ?>
</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 0b08d036c..12f485ec3 100644
--- a/app/views/helpers/feed/update.phtml
+++ b/app/views/helpers/feed/update.phtml
@@ -126,6 +126,14 @@
?></select>
</div>
</div>
+ <div class="form-group">
+ <label class="group-name" for="pubsubhubbub"><?php echo _t('sub.feed.pubsubhubbub'); ?></label>
+ <div class="group-controls">
+ <label class="checkbox" for="pubsubhubbub">
+ <input type="checkbox" name="pubsubhubbub" id="pubsubhubbub" disabled="disabled" value="1"<?php echo $this->feed->pubSubHubbubEnabled() ? ' checked="checked"' : ''; ?> />
+ </label>
+ </div>
+ </div>
<div class="form-group form-actions">
<div class="group-controls">
<button class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button>
@@ -136,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>