This commit is contained in:
plopoyop
2014-09-19 09:07:11 +02:00
31 changed files with 412 additions and 114 deletions

View File

@@ -83,6 +83,11 @@ class FreshRSS_index_Controller extends Minz_ActionController {
$nb = Minz_Request::param ('nb', $this->view->conf->posts_per_page);
$first = Minz_Request::param ('next', '');
$ajax_request = Minz_Request::param('ajax', false);
if ($ajax_request == 1 && $this->view->conf->display_posts) {
$nb = max(1, round($nb / 2));
}
if ($this->view->state === FreshRSS_Entry::STATE_NOT_READ) { //Any unread article in this category at all?
switch ($getType) {
case 'a':
@@ -415,4 +420,75 @@ class FreshRSS_index_Controller extends Minz_ActionController {
self::deleteLongTermCookie();
Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true);
}
public function resetAuthAction() {
Minz_View::prependTitle(_t('auth_reset') . ' · ');
Minz_View::appendScript(Minz_Url::display(
'/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js')
));
$this->view->no_form = false;
// Enable changement of auth only if Persona!
if (Minz_Configuration::authType() != 'persona') {
$this->view->message = array(
'status' => 'bad',
'title' => _t('damn'),
'body' => _t('auth_not_persona')
);
$this->view->no_form = true;
return;
}
$conf = new FreshRSS_Configuration(Minz_Configuration::defaultUser());
// Admin user must have set its master password.
if (!$conf->passwordHash) {
$this->view->message = array(
'status' => 'bad',
'title' => _t('damn'),
'body' => _t('auth_no_password_set')
);
$this->view->no_form = true;
return;
}
invalidateHttpCache();
if (Minz_Request::isPost()) {
$nonce = Minz_Session::param('nonce');
$username = Minz_Request::param('username', '');
$c = Minz_Request::param('challenge', '');
if (!(ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce))) {
Minz_Log::debug('Invalid credential parameters:' .
' user=' . $username .
' challenge=' . $c .
' nonce=' . $nonce);
Minz_Request::bad(_t('invalid_login'),
array('c' => 'index', 'a' => 'resetAuth'));
}
if (!function_exists('password_verify')) {
include_once(LIB_PATH . '/password_compat.php');
}
$s = $conf->passwordHash;
$ok = password_verify($nonce . $s, $c);
if ($ok) {
Minz_Configuration::_authType('form');
$ok = Minz_Configuration::writeFile();
if ($ok) {
Minz_Request::good(_t('auth_form_set'));
} else {
Minz_Request::bad(_t('auth_form_not_set'),
array('c' => 'index', 'a' => 'resetAuth'));
}
} else {
Minz_Log::debug('Password mismatch for user ' . $username .
', nonce=' . $nonce . ', c=' . $c);
Minz_Request::bad(_t('invalid_login'),
array('c' => 'index', 'a' => 'resetAuth'));
}
}
}
}

View File

@@ -10,7 +10,10 @@ class FreshRSS_update_Controller extends Minz_ActionController {
);
}
invalidateHttpCache();
Minz_View::prependTitle(_t('update_system') . ' · ');
$this->view->update_to_apply = false;
$this->view->last_update_time = 'unknown';
$this->view->check_last_hour = false;
$timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt');
@@ -29,10 +32,11 @@ class FreshRSS_update_Controller extends Minz_ActionController {
);
} elseif (file_exists(UPDATE_FILENAME)) {
// There is an update file to apply!
$this->view->update_to_apply = true;
$this->view->message = array(
'status' => 'good',
'title' => _t('ok'),
'body' => _t('update_can_apply', _url('update', 'apply'))
'body' => _t('update_can_apply')
);
}
}

View File

@@ -6,7 +6,7 @@ class FreshRSS extends Minz_FrontController {
}
$loginOk = $this->accessControl(Minz_Session::param('currentUser', ''));
$this->loadParamsView();
if (Minz_Request::isPost() && !Minz_Request::isRefererFromSameDomain()) {
if (Minz_Request::isPost() && !is_referer_from_same_domain()) {
$loginOk = false; //Basic protection against XSRF attacks
Minz_Error::error(
403,
@@ -143,11 +143,12 @@ class FreshRSS extends Minz_FrontController {
$theme = FreshRSS_Themes::load($this->conf->theme);
if ($theme) {
foreach($theme['files'] as $file) {
$theme_id = $theme['id'];
$filename = $file;
if ($file[0] == '_') {
if ($file[0] === '_') {
$theme_id = 'base-theme';
$filename = substr($file, 1);
} else {
$theme_id = $theme['id'];
$filename = $file;
}
$filetime = @filemtime(PUBLIC_PATH . '/themes/' . $theme_id . '/' . $filename);
Minz_View::appendStyle(Minz_Url::display(

View File

@@ -142,7 +142,18 @@ class FreshRSS_Configuration {
}
}
public function _default_view ($value) {
$this->data['default_view'] = $value === FreshRSS_Entry::STATE_ALL ? FreshRSS_Entry::STATE_ALL : FreshRSS_Entry::STATE_NOT_READ;
switch ($value) {
case FreshRSS_Entry::STATE_ALL:
// left blank on purpose
case FreshRSS_Entry::STATE_NOT_READ:
// left blank on purpose
case FreshRSS_Entry::STATE_NOT_READ_STRICT:
$this->data['default_view'] = $value;
break;
default:
$this->data['default_view'] = FreshRSS_Entry::STATE_ALL;
break;
}
}
public function _display_posts ($value) {
$this->data['display_posts'] = ((bool)$value) && $value !== 'no';

View File

@@ -6,6 +6,8 @@ class FreshRSS_Entry extends Minz_Model {
const STATE_NOT_READ = 2;
const STATE_FAVORITE = 4;
const STATE_NOT_FAVORITE = 8;
const STATE_READ_STRICT = 16;
const STATE_NOT_READ_STRICT = 32;
private $id = 0;
private $guid;

View File

@@ -338,6 +338,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo {
elseif ($state & FreshRSS_Entry::STATE_READ) {
$where .= 'AND e1.is_read=1 ';
}
elseif ($state & FreshRSS_Entry::STATE_NOT_READ_STRICT) {
$where .= 'AND e1.is_read=0 ';
}
if ($state & FreshRSS_Entry::STATE_FAVORITE) {
if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
$where .= 'AND e1.is_favorite=1 ';

View File

@@ -331,7 +331,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo {
$id_max = intval($date_min) . '000000';
$stm->bindParam(':id_feed', $id, PDO::PARAM_INT);
$stm->bindParam(':id_max', $id_max, PDO::PARAM_INT);
$stm->bindParam(':id_max', $id_max, PDO::PARAM_STR);
$stm->bindParam(':keep', $keep, PDO::PARAM_INT);
if ($stm && $stm->execute()) {

View File

@@ -5,6 +5,7 @@ return array (
'login' => 'Login',
'keep_logged_in' => 'Keep me logged in <small>(1 month)</small>',
'login_with_persona' => 'Login with Persona',
'login_persona_problem' => 'Problem of connection with Persona?',
'logout' => 'Logout',
'search' => 'Search words or #tags',
'search_short' => 'Search',
@@ -92,6 +93,7 @@ return array (
'rss_view' => 'RSS feed',
'show_all_articles' => 'Show all articles',
'show_not_reads' => 'Show only unread',
'show_adaptive' => 'Adjust showing',
'show_read' => 'Show only read',
'show_favorite' => 'Show only favorites',
'show_not_favorite' => 'Show all but favorites',
@@ -158,6 +160,7 @@ return array (
'save' => 'Save',
'delete' => 'Delete',
'cancel' => 'Cancel',
'submit' => 'Submit',
'back_to_rss_feeds' => '← Go back to your RSS feeds',
'feeds_moved_category_deleted' => 'When you delete a category, their feeds are automatically classified under <em>%s</em>.',
@@ -203,6 +206,7 @@ return array (
'informations' => 'Information',
'damn' => 'Damn!',
'ok' => 'Ok!',
'attention' => 'Be careful!',
'feed_in_error' => 'This feed has encountered a problem. Please verify that it is always reachable then actualize it.',
'feed_empty' => 'This feed is empty. Please verify that it is still maintained.',
'feed_description' => 'Description',
@@ -254,6 +258,7 @@ return array (
'users_list' => 'List of users',
'create_user' => 'Create new user',
'username' => 'Username',
'username_admin' => 'Administrator username',
'password' => 'Password',
'create' => 'Create',
'user_created' => 'User %s has been created',
@@ -269,7 +274,9 @@ return array (
'reading_configuration' => 'Reading',
'display_configuration' => 'Display',
'articles_per_page' => 'Number of articles per page',
'number_divided_when_unfolded' => 'Divided by 2 during loading of unfolded articles.',
'default_view' => 'Default view',
'articles_to_display' => 'Articles to display',
'sort_order' => 'Sort order',
'auto_load_more' => 'Load next articles at the page bottom',
'display_articles_unfolded' => 'Show articles unfolded by default',
@@ -427,9 +434,17 @@ return array (
'update_system' => 'Update system',
'update_check' => 'Check for new updates',
'update_last' => 'Last verification: %s',
'update_can_apply' => 'There is an available update. <a class="btn" href="%s">Apply</a>',
'update_can_apply' => 'An update is available.',
'update_apply' => 'Apply',
'update_server_not_found' => 'Update server cannot be found. [%s]',
'no_update' => 'No update to apply',
'update_problem' => 'Update has encountered an error: %s',
'update_finished' => 'Update is now finished!',
'update_problem' => 'The update process has encountered an error: %s',
'update_finished' => 'Update completed!',
'auth_reset' => 'Authentication reset',
'auth_will_reset' => 'Authentication system will be reseted: form will be used instead of Persona.',
'auth_not_persona' => 'Only Persona system can be reseted.',
'auth_no_password_set' => 'Administrator password hasnt been set. This feature isnt available.',
'auth_form_set' => 'Form is now your default authentication system.',
'auth_form_not_set' => 'A problem occured during authentication system configuration. Please retry later.',
);

View File

@@ -5,6 +5,7 @@ return array (
'login' => 'Connexion',
'keep_logged_in' => 'Rester connecté <small>(1 mois)</small>',
'login_with_persona' => 'Connexion avec Persona',
'login_persona_problem' => 'Problème de connexion à Persona ?',
'logout' => 'Déconnexion',
'search' => 'Rechercher des mots ou des #tags',
'search_short' => 'Rechercher',
@@ -92,6 +93,7 @@ return array (
'rss_view' => 'Flux RSS',
'show_all_articles' => 'Afficher tous les articles',
'show_not_reads' => 'Afficher les non lus',
'show_adaptive' => 'Adapter laffichage',
'show_read' => 'Afficher les lus',
'show_favorite' => 'Afficher les favoris',
'show_not_favorite' => 'Afficher tout sauf les favoris',
@@ -158,6 +160,7 @@ return array (
'save' => 'Enregistrer',
'delete' => 'Supprimer',
'cancel' => 'Annuler',
'submit' => 'Valider',
'back_to_rss_feeds' => '← Retour à vos flux RSS',
'feeds_moved_category_deleted' => 'Lors de la suppression dune catégorie, ses flux seront automatiquement classés dans <em>%s</em>.',
@@ -203,6 +206,7 @@ return array (
'informations' => 'Informations',
'damn' => 'Arf !',
'ok' => 'Ok !',
'attention' => 'Attention !',
'feed_in_error' => 'Ce flux a rencontré un problème. Veuillez vérifier quil est toujours accessible puis actualisez-le.',
'feed_empty' => 'Ce flux est vide. Veuillez vérifier quil est toujours maintenu.',
'feed_description' => 'Description',
@@ -254,6 +258,7 @@ return array (
'users_list' => 'Liste des utilisateurs',
'create_user' => 'Créer un nouvel utilisateur',
'username' => 'Nom dutilisateur',
'username_admin' => 'Nom dutilisateur administrateur',
'password' => 'Mot de passe',
'create' => 'Créer',
'user_created' => 'Lutilisateur %s a été créé.',
@@ -269,7 +274,9 @@ return array (
'reading_configuration' => 'Lecture',
'display_configuration' => 'Affichage',
'articles_per_page' => 'Nombre darticles par page',
'number_divided_when_unfolded' => 'Divisé par 2 lors du chargement darticles dépliés.',
'default_view' => 'Vue par défaut',
'articles_to_display' => 'Articles à afficher',
'sort_order' => 'Ordre de tri',
'auto_load_more' => 'Charger les articles suivants en bas de page',
'display_articles_unfolded' => 'Afficher les articles dépliés par défaut',
@@ -427,9 +434,17 @@ return array (
'update_system' => 'Système de mise à jour',
'update_check' => 'Vérifier les mises à jour',
'update_last' => 'Dernière vérification : %s',
'update_can_apply' => 'Il ya une mise à jour à appliquer. <a class="btn" href="%s">Appliquer la mise à jour</a>',
'update_can_apply' => 'Une mise à jour est disponible.',
'update_apply' => 'Appliquer la mise à jour',
'update_server_not_found' => 'Le serveur de mise à jour na pas été trouvé. [%s]',
'no_update' => 'Aucune mise à jour à appliquer',
'update_problem' => 'La mise à jour a rencontré un problème : %s',
'update_finished' => 'La mise à jour est terminée !',
'auth_reset' => 'Reset de lauthentification',
'auth_will_reset' => 'Le système dauthentification va être remis à zéro : le formulaire sera utilisé à la place de Persona.',
'auth_not_persona' => 'Seul le système dauthentification Persona peut être remis à zéro.',
'auth_no_password_set' => 'Aucun mot de passe administrateur na été précisé. Cette fonctionnalité nest pas disponible.',
'auth_form_set' => 'Le formulaire est désormais votre système dauthentification.',
'auth_form_not_set' => 'Un problème est survenu lors de la configuration de votre système dauthentification. Veuillez réessayer plus tard.',
);

View File

@@ -42,6 +42,8 @@ return array (
'data_is_ok' => 'Permissions on data directory are good',
'persona_is_ok' => 'Permissions on Mozilla Persona directory are good',
'file_is_nok' => 'Check permissions on <em>%s</em> directory. HTTP server must have rights to write into',
'http_referer_is_ok' => 'Your HTTP REFERER is known and corresponds to your server.',
'http_referer_is_nok' => 'Please check that you are not altering your HTTP REFERER.',
'fix_errors_before' => 'Fix errors before skip to the next step.',
'general_conf_is_ok' => 'General configuration has been saved.',

View File

@@ -42,6 +42,8 @@ return array (
'data_is_ok' => 'Les droits sur le répertoire de data sont bons',
'persona_is_ok' => 'Les droits sur le répertoire de Mozilla Persona sont bons',
'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire <em>%s</em>. Le serveur HTTP doit être capable décrire dedans',
'http_referer_is_ok' => 'Le HTTP REFERER est connu et semble correspondre à votre serveur.',
'http_referer_is_nok' => 'Veuillez vérifier que vous ne modifiez pas votre HTTP REFERER.',
'fix_errors_before' => 'Veuillez corriger les erreurs avant de passer à létape suivante.',
'general_conf_is_ok' => 'La configuration générale a été enregistrée.',

View File

@@ -149,7 +149,7 @@ function saveStep2() {
$config_array = array(
'language' => $_SESSION['language'],
'theme' => $_SESSION['theme'],
'theme' => 'Origine',
'old_entries' => $_SESSION['old_entries'],
'mail_login' => $_SESSION['mail_login'],
'passwordHash' => $_SESSION['passwordHash'],
@@ -307,6 +307,7 @@ function checkStep1() {
$log = LOG_PATH && is_writable(LOG_PATH);
$favicons = is_writable(DATA_PATH . '/favicons');
$persona = is_writable(DATA_PATH . '/persona');
$http_referer = is_referer_from_same_domain();
return array(
'php' => $php ? 'ok' : 'ko',
@@ -323,8 +324,10 @@ function checkStep1() {
'log' => $log ? 'ok' : 'ko',
'favicons' => $favicons ? 'ok' : 'ko',
'persona' => $persona ? 'ok' : 'ko',
'http_referer' => $http_referer ? 'ok' : 'ko',
'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom &&
$data && $cache && $log && $favicons && $persona ? 'ok' : 'ko'
$data && $cache && $log && $favicons && $persona && $http_referer ?
'ok' : 'ko'
);
}
@@ -334,9 +337,15 @@ function checkStep2() {
isset($_SESSION['mail_login']) &&
!empty($_SESSION['default_user']);
$form = $_SESSION['auth_type'] != 'form' || !empty($_SESSION['passwordHash']);
$form = (
isset($_SESSION['auth_type']) &&
($_SESSION['auth_type'] != 'form' || !empty($_SESSION['passwordHash']))
);
$persona = $_SESSION['auth_type'] != 'persona' || !empty($_SESSION['mail_login']);
$persona = (
isset($_SESSION['auth_type']) &&
($_SESSION['auth_type'] != 'persona' || !empty($_SESSION['mail_login']))
);
$defaultUser = empty($_POST['default_user']) ? null : $_POST['default_user'];
if ($defaultUser === null) {
@@ -548,6 +557,12 @@ function printStep1() {
<p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('file_is_nok', DATA_PATH . '/persona'); ?></p>
<?php } ?>
<?php if ($res['http_referer'] == 'ok') { ?>
<p class="alert alert-success"><span class="alert-head"><?php echo _t('ok'); ?></span> <?php echo _t('http_referer_is_ok'); ?></p>
<?php } else { ?>
<p class="alert alert-error"><span class="alert-head"><?php echo _t('damn'); ?></span> <?php echo _t('http_referer_is_nok'); ?></p>
<?php } ?>
<?php if ($res['all'] == 'ok') { ?>
<a class="btn btn-important next-step" href="?step=2"><?php echo _t('next_step'); ?></a>
<?php } else { ?>
@@ -591,16 +606,17 @@ function printStep2() {
<div class="form-group">
<label class="group-name" for="auth_type"><?php echo _t('auth_type'); ?></label>
<div class="group-controls">
<select id="auth_type" name="auth_type" required="required" onchange="auth_type_change()">
<select id="auth_type" name="auth_type" required="required" onchange="auth_type_change(true)">
<?php
function no_auth() {
return !in_array($_SESSION['auth_type'], array('form', 'persona', 'http_auth', 'none'));
function no_auth($auth_type) {
return !in_array($auth_type, array('form', 'persona', 'http_auth', 'none'));
}
$auth_type = isset($_SESSION['auth_type']) ? $_SESSION['auth_type'] : '';
?>
<option value="form"<?php echo $_SESSION['auth_type'] === 'form' || no_auth() ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"'; ?>><?php echo _t('auth_form'); ?></option>
<option value="persona"<?php echo $_SESSION['auth_type'] === 'persona' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_persona'); ?></option>
<option value="http_auth"<?php echo $_SESSION['auth_type'] === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('http_auth'); ?>(REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option>
<option value="none"<?php echo $_SESSION['auth_type'] === 'none' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_none'); ?></option>
<option value="form"<?php echo $auth_type === 'form' || no_auth($auth_type) ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"'; ?>><?php echo _t('auth_form'); ?></option>
<option value="persona"<?php echo $auth_type === 'persona' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_persona'); ?></option>
<option value="http_auth"<?php echo $auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('http_auth'); ?>(REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option>
<option value="none"<?php echo $auth_type === 'none' ? ' selected="selected"' : ''; ?>><?php echo _t('auth_none'); ?></option>
</select>
</div>
</div>
@@ -609,7 +625,7 @@ function printStep2() {
<label class="group-name" for="passwordPlain"><?php echo _t('password_form'); ?></label>
<div class="group-controls">
<div class="stick">
<input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $_SESSION['auth_type'] === 'form' ? ' required="required"' : ''; ?> />
<input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $auth_type === 'form' ? ' required="required"' : ''; ?> />
<a class="btn toggle-password" data-toggle="passwordPlain"><?php echo FreshRSS_Themes::icon('key'); ?></a>
</div>
<noscript><b><?php echo _t('javascript_should_be_activated'); ?></b></noscript>
@@ -619,7 +635,7 @@ function printStep2() {
<div class="form-group">
<label class="group-name" for="mail_login"><?php echo _t('persona_connection_email'); ?></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 $_SESSION['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"' : ''; ?> />
<noscript><b><?php echo _t('javascript_should_be_activated'); ?></b></noscript>
</div>
</div>
@@ -644,7 +660,7 @@ function printStep2() {
toggles[i].addEventListener('click', toggle_password);
}
function auth_type_change() {
function auth_type_change(focus) {
var auth_value = document.getElementById('auth_type').value,
password_input = document.getElementById('passwordPlain'),
mail_input = document.getElementById('mail_login');
@@ -652,15 +668,21 @@ 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();
auth_type_change(false);
</script>
<div class="form-group form-actions">

View File

@@ -1,32 +1,32 @@
<ul class="nav nav-list aside">
<li class="nav-header"><?php echo _t('configuration'); ?></li>
<li class="item<?php echo Minz_Request::actionName() == 'display' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'display' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'display'); ?>"><?php echo _t('display_configuration'); ?></a>
</li>
<li class="item<?php echo Minz_Request::actionName() == 'reading' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'reading' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'reading'); ?>"><?php echo _t('reading_configuration'); ?></a>
</li>
<li class="item<?php echo Minz_Request::actionName() == 'archiving' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'archiving' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'archiving'); ?>"><?php echo _t('archiving_configuration'); ?></a>
</li>
<li class="item<?php echo Minz_Request::actionName() == 'sharing' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'sharing' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'sharing'); ?>"><?php echo _t('sharing'); ?></a>
</li>
<li class="item<?php echo Minz_Request::actionName() == 'shortcut' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'shortcut' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'shortcut'); ?>"><?php echo _t('shortcuts'); ?></a>
</li>
<li class="item<?php echo Minz_Request::actionName() == 'queries' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'queries' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('queries'); ?></a>
</li>
<li class="separator"></li>
<li class="item<?php echo Minz_Request::actionName() == 'users' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::actionName() === 'users' ? ' active' : ''; ?>">
<a href="<?php echo _url('configure', 'users'); ?>"><?php echo _t('users'); ?></a>
</li>
<?php
$current_user = Minz_Session::param('currentUser', '');
if (Minz_Configuration::isAdmin($current_user)) {
?>
<li class="item<?php echo Minz_Request::controllerName() == 'update' ? ' active' : ''; ?>">
<li class="item<?php echo Minz_Request::controllerName() === 'update' ? ' active' : ''; ?>">
<a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('update'); ?></a>
</li>
<?php } ?>

View File

@@ -13,6 +13,7 @@
if (!empty($this->nextId)) {
$params = Minz_Request::params();
$params['next'] = $this->nextId;
$params['ajax'] = 1;
?>
<link id="prefetch" rel="next prefetch" href="<?php echo Minz_Url::display(array('c' => Minz_Request::controllerName(), 'a' => Minz_Request::actionName(), 'params' => $params)); ?>" />
<?php } ?>

View File

@@ -10,6 +10,9 @@
<label class="group-name" for="posts_per_page"><?php echo Minz_Translate::t ('articles_per_page'); ?></label>
<div class="group-controls">
<input type="number" id="posts_per_page" name="posts_per_page" value="<?php echo $this->conf->posts_per_page; ?>" min="5" max="50" />
<?php if ($this->conf->display_posts) { ?>
<?php echo _i('help'); ?> <?php echo _t('number_divided_when_unfolded'); ?>
<?php } ?>
</div>
</div>
@@ -31,14 +34,17 @@
<option value="reader"<?php echo $this->conf->view_mode === 'reader' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t ('reader_view'); ?></option>
<option value="global"<?php echo $this->conf->view_mode === 'global' ? ' selected="selected"' : ''; ?>><?php echo Minz_Translate::t ('global_view'); ?></option>
</select>
<label class="radio" for="radio_all">
<input type="radio" name="default_view" id="radio_all" value="<?php echo FreshRSS_Entry::STATE_ALL; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_ALL ? ' checked="checked"' : ''; ?> />
<?php echo Minz_Translate::t ('show_all_articles'); ?>
</label>
<label class="radio" for="radio_not_read">
<input type="radio" name="default_view" id="radio_not_read" value="<?php echo FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ ? ' checked="checked"' : ''; ?> />
<?php echo Minz_Translate::t ('show_not_reads'); ?>
</label>
</div>
</div>
<div class="form-group">
<label class="group-name" for="view_mode"><?php echo _t('articles_to_display'); ?></label>
<div class="group-controls">
<select name="default_view" id="default_view">
<option value="<?php echo FreshRSS_Entry::STATE_NOT_READ; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ ? ' selected="selected"' : ''; ?>><?php echo _t('show_adaptive'); ?></option>
<option value="<?php echo FreshRSS_Entry::STATE_ALL; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_ALL ? ' selected="selected"' : ''; ?>><?php echo _t('show_all_articles'); ?></option>
<option value="<?php echo FreshRSS_Entry::STATE_NOT_READ_STRICT; ?>"<?php echo $this->conf->default_view === FreshRSS_Entry::STATE_NOT_READ_STRICT ? ' selected="selected"' : ''; ?>><?php echo _t('show_not_reads'); ?></option>
</select>
</div>
</div>

View File

@@ -9,7 +9,10 @@
<ul class="pagination">
<li class="item pager-next">
<?php if (!empty($this->nextId)) { ?>
<?php $params['next'] = $this->nextId; ?>
<?php
$params['next'] = $this->nextId;
$params['ajax'] = 1;
?>
<a id="load_more" href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>">
<?php echo _t('load_more'); ?>
</a>

View File

@@ -3,7 +3,7 @@
switch (Minz_Configuration::authType()) {
case 'form':
?><form id="loginForm" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
?><form id="crypto-form" method="post" action="<?php echo _url('index', 'formLogin'); ?>">
<div>
<label for="username"><?php echo _t('username'); ?></label>
<input type="text" id="username" name="username" size="16" required="required" maxlength="16" pattern="[0-9a-zA-Z]{1,16}" autofocus="autofocus" />
@@ -29,8 +29,15 @@
case 'persona':
?><p>
<?php echo _i('login'); ?>
<a class="signin" href="#"><?php echo _t('login_with_persona'); ?></a>
<a class="signin btn btn-important" href="#">
<?php echo _i('login'); ?>
<?php echo _t('login_with_persona'); ?>
</a><br /><br />
<?php echo _i('help'); ?>
<small>
<a href="<?php echo _url('index', 'resetAuth'); ?>"><?php echo _t('login_persona_problem'); ?></a>
</small>
</p><?php
break;
} ?>

View File

@@ -0,0 +1,33 @@
<div class="prompt">
<h1><?php echo _t('auth_reset'); ?></h1>
<?php if (!empty($this->message)) { ?>
<p class="alert <?php echo $this->message['status'] === 'bad' ? 'alert-error' : 'alert-warn'; ?>">
<span class="alert-head"><?php echo $this->message['title']; ?></span><br />
<?php echo $this->message['body']; ?>
</p>
<?php } ?>
<?php if (!$this->no_form) { ?>
<form id="crypto-form" method="post" action="<?php echo _url('index', 'resetAuth'); ?>">
<p class="alert alert-warn">
<span class="alert-head"><?php echo _t('attention'); ?></span><br />
<?php echo _t('auth_will_reset'); ?>
</p>
<div>
<label for="username"><?php echo _t('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>
<label for="passwordPlain"><?php echo _t('password'); ?></label>
<input type="password" id="passwordPlain" required="required" />
<input type="hidden" id="challenge" name="challenge" /><br />
<noscript><strong><?php echo _t('javascript_should_be_activated'); ?></strong></noscript>
</div>
<div>
<button id="loginButton" type="submit" class="btn btn-important"><?php echo _t('submit'); ?></button>
</div>
</form>
<?php } ?>
</div>

View File

@@ -1,6 +1,6 @@
<?php $this->partial('aside_stats'); ?>
<div class="post content">
<div class="post">
<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
<h1><?php echo _t('stats_idle'); ?></h1>
@@ -12,11 +12,20 @@
<div class="stat">
<h2><?php echo _t($period); ?></h2>
<ul>
<?php foreach ($feeds as $feed) { ?>
<li><a href="<?php echo _url('configure', 'feed', 'id', $feed['id']); ?>" title="<?php echo date('Y-m-d', $feed['last_date']); ?>"><?php echo $feed['name']; ?></a></li>
<?php } ?>
<form id="form-delete" method="post" style="display: none"></form>
<?php foreach ($feeds as $feed) { ?>
<ul class="horizontal-list">
<li class="item">
<div class="stick">
<a class="btn" href="<?php echo _url('index', 'index', 'get', 'f_' . $feed['id']); ?>"><?php echo _i('link'); ?> <?php echo _t('filter'); ?></a>
<a class="btn" href="<?php echo _url('configure', 'feed', 'id', $feed['id']); ?>"><?php echo _i('configure'); ?> <?php echo _t('administration'); ?></a>
<button class="btn btn-attention confirm" form="form-delete" formaction="<?php echo _url('feed', 'delete', 'id', $feed['id']); ?>"><?php echo _t('delete'); ?></button>
</div>
</li>
<li class="item"><span title="<?php echo timestamptodate($feed['last_date'], false); ?>"><?php echo $feed['name']; ?></span></li>
</ul>
<?php } ?>
</div>
<?php
}

View File

@@ -1,11 +1,11 @@
<?php $this->partial('aside_stats'); ?>
<div class="post content">
<div class="post">
<a href="<?php echo _url ('index', 'index'); ?>"><?php echo _t ('back_to_rss_feeds'); ?></a>
<h1><?php echo _t ('stats_main'); ?></h1>
<div class="stat">
<div class="stat half">
<h2><?php echo _t ('stats_entry_repartition'); ?></h2>
<table>
<thead>
@@ -38,26 +38,9 @@
</tr>
</tbody>
</table>
</div>
<div class="stat">
<h2><?php echo _t ('stats_entry_per_day'); ?></h2>
<div id="statsEntryPerDay" style="height: 300px"></div>
</div>
<div class="stat">
<h2><?php echo _t ('stats_feed_per_category'); ?></h2>
<div id="statsFeedPerCategory" style="height: 300px"></div>
<div id="statsFeedPerCategoryLegend"></div>
</div>
<div class="stat">
<h2><?php echo _t ('stats_entry_per_category'); ?></h2>
<div id="statsEntryPerCategory" style="height: 300px"></div>
<div id="statsEntryPerCategoryLegend"></div>
</div>
<div class="stat">
</div><!--
--><div class="stat half">
<h2><?php echo _t ('stats_top_feed'); ?></h2>
<table>
<thead>
@@ -78,6 +61,23 @@
</tbody>
</table>
</div>
<div class="stat">
<h2><?php echo _t ('stats_entry_per_day'); ?></h2>
<div id="statsEntryPerDay" style="height: 300px"></div>
</div>
<div class="stat half">
<h2><?php echo _t ('stats_feed_per_category'); ?></h2>
<div id="statsFeedPerCategory" style="height: 300px"></div>
<div id="statsFeedPerCategoryLegend"></div>
</div><!--
--><div class="stat half">
<h2><?php echo _t ('stats_entry_per_category'); ?></h2>
<div id="statsEntryPerCategory" style="height: 300px"></div>
<div id="statsEntryPerCategoryLegend"></div>
</div>
</div>
<script>

View File

@@ -1,6 +1,6 @@
<?php $this->partial('aside_stats'); ?>
<div class="post content">
<div class="post ">
<a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('back_to_rss_feeds'); ?></a>
<h1><?php echo _t('stats_repartition'); ?></h1>
@@ -34,12 +34,12 @@
<div id="statsEntryPerHour" style="height: 300px"></div>
</div>
<div class="stat">
<div class="stat half">
<h2><?php echo _t('stats_entry_per_day_of_week'); ?></h2>
<div id="statsEntryPerDayOfWeek" style="height: 300px"></div>
</div>
</div><!--
<div class="stat">
--><div class="stat half">
<h2><?php echo _t('stats_entry_per_month'); ?></h2>
<div id="statsEntryPerMonth" style="height: 300px"></div>
</div>

View File

@@ -29,4 +29,8 @@
<a href="<?php echo _url('update', 'check'); ?>" class="btn"><?php echo _t('update_check'); ?></a>
</p>
<?php } ?>
<?php if ($this->update_to_apply) { ?>
<a class="btn btn-important" href="<?php echo _url('update', 'apply'); ?>"><?php echo _t('update_apply'); ?></a>
<?php } ?>
</div>

View File

@@ -84,20 +84,6 @@ class Minz_Request {
return $_SERVER['HTTP_HOST'];
}
public static function isRefererFromSameDomain() {
if (empty($_SERVER['HTTP_REFERER'])) {
return false;
}
$host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') .
(empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']));
$referer = parse_url($_SERVER['HTTP_REFERER']);
if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] ||
empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) {
return false;
}
return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0);
}
/**
* Détermine la base de l'url
* @return la base de l'url

View File

@@ -230,3 +230,17 @@ function cryptAvailable() {
}
return false;
}
function is_referer_from_same_domain() {
if (empty($_SERVER['HTTP_REFERER'])) {
return false;
}
$host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') .
(empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']));
$referer = parse_url($_SERVER['HTTP_REFERER']);
if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] ||
empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) {
return false;
}
return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0);
}

View File

@@ -984,7 +984,7 @@ function init_load_more(box) {
}
//</endless_mode>
//<Web login form>
//<crypto form (Web login)>
function poormanSalt() { //If crypto.getRandomValues is not available
var text = '$2a$04$',
base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz';
@@ -994,20 +994,24 @@ function poormanSalt() { //If crypto.getRandomValues is not available
return text;
}
function init_loginForm() {
var $loginForm = $('#loginForm');
if ($loginForm.length === 0) {
function init_crypto_form() {
var $crypto_form = $('#crypto-form');
if ($crypto_form.length === 0) {
return;
}
if (!(window.dcodeIO)) {
if (window.console) {
console.log('FreshRSS waiting for bcrypt.js…');
}
window.setTimeout(init_loginForm, 100);
window.setTimeout(init_crypto_form, 100);
return;
}
$loginForm.on('submit', function() {
$('#loginButton').attr('disabled', '');
$crypto_form.on('submit', function() {
var $submit_button = $(this).find('button[type="submit"]');
$submit_button.attr('disabled', '');
var success = false;
$.ajax({
url: './?c=javascript&a=nonce&user=' + $('#username').val(),
@@ -1015,7 +1019,7 @@ function init_loginForm() {
async: false
}).done(function (data) {
if (data.salt1 == '' || data.nonce == '') {
alert('Invalid user!');
openNotification('Invalid user!', 'bad');
} else {
try {
var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'),
@@ -1023,22 +1027,23 @@ function init_loginForm() {
c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt());
$('#challenge').val(c);
if (s == '' || c == '') {
alert('Crypto error!');
openNotification('Crypto error!', 'bad');
} else {
success = true;
}
} catch (e) {
alert('Crypto exception! ' + e);
openNotification('Crypto exception! ' + e, 'bad');
}
}
}).fail(function() {
alert('Communication error!');
openNotification('Communication error!', 'bad');
});
$('#loginButton').removeAttr('disabled');
$submit_button.removeAttr('disabled');
return success;
});
}
//</Web login form>
//</crypto form (Web login)>
//<persona>
function init_persona() {
@@ -1240,14 +1245,12 @@ function init_all() {
}
init_notifications();
switch (authType) {
case 'form':
init_loginForm();
break;
case 'persona':
init_persona();
break;
}
init_confirm_action();
init_crypto_form();
$stream = $('#stream');
if ($stream.length > 0) {
init_actualize();

View File

@@ -872,7 +872,18 @@ a.btn {
.stat > table td,
.stat > table th {
border-bottom: 1px solid #333;
text-align: center;
}
.stat > .horizontal-list {
margin: 0 0 5px;
}
.stat > .horizontal-list .item {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.stat > .horizontal-list .item:first-child {
width: 270px;
}
/*=== LOGS */

View File

@@ -859,7 +859,18 @@ a.btn {
.stat > table td,
.stat > table th {
border-bottom: 1px solid #ddd;
text-align: center;
}
.stat > .horizontal-list {
margin: 0 0 5px;
}
.stat > .horizontal-list .item {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.stat > .horizontal-list .item:first-child {
width: 270px;
}
/*=== LOGS */

View File

@@ -808,12 +808,12 @@ a.btn {
background: #fafafa;
}
#bigMarkAsRead:hover {
color: #27ae60;
color: #0062be;
background: #fff;
box-shadow: 0 -5px 10px #eee inset;
}
#bigMarkAsRead:hover .bigTick {
text-shadow: 0 0 5px #27ae60;
text-shadow: 0 0 5px #0062be;
}
/*=== Navigation menu (for articles) */
@@ -913,7 +913,18 @@ a.btn {
.stat > table td,
.stat > table th {
border-bottom: 1px solid #ddd;
text-align: center;
}
.stat > .horizontal-list {
margin: 0 0 5px;
}
.stat > .horizontal-list .item {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.stat > .horizontal-list .item:first-child {
width: 270px;
}
/*=== LOGS */

View File

@@ -1025,7 +1025,18 @@ opacity: 1;
border-bottom: 1px solid #ccc;
background: rgba(255,255,255,0.38);
box-shadow: 0 1px #fff;
text-align: center;
}
.stat > .horizontal-list {
margin: 0 0 5px;
}
.stat > .horizontal-list .item {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.stat > .horizontal-list .item:first-child {
width: 250px;
}
/*=== LOGS */

View File

@@ -678,6 +678,18 @@ a.btn {
text-align: center;
}
.stat > .horizontal-list {
margin: 0 0 5px;
}
.stat > .horizontal-list .item {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.stat > .horizontal-list .item:first-child {
width: 250px;
}
/*=== LOGS */
/*=========*/
.logs {

View File

@@ -98,6 +98,15 @@ button.as-link:active {
font-size: 1.1em;
}
/*=== Tables */
table {
max-width: 100%;
}
th.numeric,
td.numeric {
text-align: center;
}
/*=== COMPONENTS */
/*===============*/
/*=== Forms */
@@ -458,6 +467,12 @@ a.btn {
.content pre {
overflow: auto;
}
br {
line-height: 1em;
}
br + br + br {
display: none;
}
/*=== Notification and actualize notification */
.notification {
@@ -526,6 +541,14 @@ a.btn {
}
/*=== Statistiques */
.stat {
margin: 15px 0;
}
.stat.half {
display: inline-block;
width: 46%;
padding: 0 2%;
}
.stat > table {
width: 100%;
}