diff options
| author | 2014-09-19 09:07:11 +0200 | |
|---|---|---|
| committer | 2014-09-19 09:07:11 +0200 | |
| commit | ffbfbb92cc89c5ae07e0a28ee3477fcd0c44505d (patch) | |
| tree | 827469859e1300f3525196658b7191fa1dbb40f9 | |
| parent | 4fd1478e82dabaa042f4e80d4b9b2830f29a7da8 (diff) | |
| parent | 2f5304a1f7052bce1315f2ed85141568f0995e7c (diff) | |
Merge branch 'dev' of https://github.com/marienfressinaud/FreshRSS into dev
31 files changed, 412 insertions, 114 deletions
diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index b0b051119..b69c09127 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -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')); + } + } + } } diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 72244e9c7..da5bddc65 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -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') ); } } diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 6cca27f78..cdf8962cb 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -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( diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 830d39f0d..f94d82402 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -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'; diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 0bf1f2616..5f1c8abc4 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -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; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 75a8aeba4..dee49212d 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -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 '; diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 756b1f008..b89ae2045 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -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()) { diff --git a/app/i18n/en.php b/app/i18n/en.php index ca31c4bfc..8598e61cb 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -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 hasn’t been set. This feature isn’t 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.', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index d6c0118e7..4af819cac 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -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 l’affichage', '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 d’une 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 qu’il est toujours accessible puis actualisez-le.', 'feed_empty' => 'Ce flux est vide. Veuillez vérifier qu’il 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 d’utilisateur', + 'username_admin' => 'Nom d’utilisateur administrateur', 'password' => 'Mot de passe', 'create' => 'Créer', 'user_created' => 'L’utilisateur %s a été créé.', @@ -269,7 +274,9 @@ return array ( 'reading_configuration' => 'Lecture', 'display_configuration' => 'Affichage', 'articles_per_page' => 'Nombre d’articles par page', + 'number_divided_when_unfolded' => 'Divisé par 2 lors du chargement d’articles 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 y’a 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 n’a 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 l’authentification', + 'auth_will_reset' => 'Le système d’authentification va être remis à zéro : le formulaire sera utilisé à la place de Persona.', + 'auth_not_persona' => 'Seul le système d’authentification Persona peut être remis à zéro.', + 'auth_no_password_set' => 'Aucun mot de passe administrateur n’a été précisé. Cette fonctionnalité n’est pas disponible.', + 'auth_form_set' => 'Le formulaire est désormais votre système d’authentification.', + 'auth_form_not_set' => 'Un problème est survenu lors de la configuration de votre système d’authentification. Veuillez réessayer plus tard.', ); diff --git a/app/i18n/install.en.php b/app/i18n/install.en.php index 50208fcef..c422de90f 100644 --- a/app/i18n/install.en.php +++ b/app/i18n/install.en.php @@ -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.', diff --git a/app/i18n/install.fr.php b/app/i18n/install.fr.php index 9c039f904..785c02459 100644 --- a/app/i18n/install.fr.php +++ b/app/i18n/install.fr.php @@ -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.', diff --git a/app/install.php b/app/install.php index 8986e9965..4449cd063 100644 --- a/app/install.php +++ b/app/install.php @@ -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"> diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index 03bea4836..d5c9bf4c9 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -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 } ?> diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 96a88d245..f95f45b5e 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -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 } ?> diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index 5a26501a4..3dd457a2b 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -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> diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index f6fcbc701..1b15cc632 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -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> diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index b79c1b614..b05cdced4 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -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; } ?> diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml new file mode 100644 index 000000000..6d4282c14 --- /dev/null +++ b/app/views/index/resetAuth.phtml @@ -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> diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 2ba5237f7..608e2d33c 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -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 } diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml index a48181fe4..46cdc2a8b 100644 --- a/app/views/stats/index.phtml +++ b/app/views/stats/index.phtml @@ -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> diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index d9dc4c89d..ead275696 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -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> diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 5be8b1e8b..401f6acd6 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -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> diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index ec4e25a6b..52f53012f 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -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 diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 823f53716..31c9cdbc1 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -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); +} diff --git a/p/scripts/main.js b/p/scripts/main.js index 9d2d83a32..b2ca90c5b 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -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(); diff --git a/p/themes/Dark/dark.css b/p/themes/Dark/dark.css index 669f4ce42..e47415366 100644 --- a/p/themes/Dark/dark.css +++ b/p/themes/Dark/dark.css @@ -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 */ diff --git a/p/themes/Flat/flat.css b/p/themes/Flat/flat.css index a942df0e8..41d03c57d 100644 --- a/p/themes/Flat/flat.css +++ b/p/themes/Flat/flat.css @@ -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 */ diff --git a/p/themes/Origine/origine.css b/p/themes/Origine/origine.css index 55ff3fc73..6a4ef9699 100644 --- a/p/themes/Origine/origine.css +++ b/p/themes/Origine/origine.css @@ -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 */ diff --git a/p/themes/Screwdriver/screwdriver.css b/p/themes/Screwdriver/screwdriver.css index 1d84753c7..c96d4bfe3 100644 --- a/p/themes/Screwdriver/screwdriver.css +++ b/p/themes/Screwdriver/screwdriver.css @@ -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 */ diff --git a/p/themes/base-theme/base.css b/p/themes/base-theme/base.css index b49cd79ea..1688a6f79 100644 --- a/p/themes/base-theme/base.css +++ b/p/themes/base-theme/base.css @@ -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 { diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css index f05ddff4e..0f00632ee 100644 --- a/p/themes/base-theme/template.css +++ b/p/themes/base-theme/template.css @@ -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%; } |
