From b6e89d4e06814541802c428c4df0e28633f551cd Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 9 Aug 2014 14:23:15 +0200 Subject: [PATCH 001/126] Refactor install.php - Remove all references to update functions - Improve step2 by checking if password exists (form) or mail (persona) - Add a param($key, $default=falsse) function to get $_POST vars - Fix coding style (mainly spaces) --- app/install.php | 897 ++++++++++++++++-------------------------------- 1 file changed, 300 insertions(+), 597 deletions(-) diff --git a/app/install.php b/app/install.php index eaa1100c1..f563d58d3 100644 --- a/app/install.php +++ b/app/install.php @@ -9,10 +9,10 @@ session_name('FreshRSS'); session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start(); -if (isset ($_GET['step'])) { - define ('STEP', (int)$_GET['step']); +if (isset($_GET['step'])) { + define('STEP',(int)$_GET['step']); } else { - define ('STEP', 1); + define('STEP', 0); } define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); @@ -23,88 +23,28 @@ if (STEP === 3 && isset($_POST['type'])) { if (isset($_SESSION['bd_type'])) { switch ($_SESSION['bd_type']) { - case 'mysql': - include(APP_PATH . '/SQL/install.sql.mysql.php'); - break; - case 'sqlite': - include(APP_PATH . '/SQL/install.sql.sqlite.php'); - break; + case 'mysql': + include(APP_PATH . '/SQL/install.sql.mysql.php'); + break; + case 'sqlite': + include(APP_PATH . '/SQL/install.sql.sqlite.php'); + break; } } -// -define('SQL_BACKUP006', 'RENAME TABLE `%1$scategory` TO `%1$scategory006`, `%1$sfeed` TO `%1$sfeed006`, `%1$sentry` TO `%1$sentry006`;'); - -define('SQL_SHOW_COLUMNS_UPDATEv006', 'SHOW columns FROM `%1$sentry006` LIKE "id2";'); - -define('SQL_UPDATEv006', ' -ALTER TABLE `%1$scategory006` ADD id2 SMALLINT; - -SET @i = 0; -UPDATE `%1$scategory006` SET id2=(@i:=@i+1) ORDER BY id; - -ALTER TABLE `%1$sfeed006` ADD id2 SMALLINT, ADD category2 SMALLINT; - -SET @i = 0; -UPDATE `%1$sfeed006` SET id2=(@i:=@i+1) ORDER BY name; - -UPDATE `%1$sfeed006` f -INNER JOIN `%1$scategory006` c ON f.category = c.id -SET f.category2 = c.id2; - -INSERT IGNORE INTO `%2$scategory` (name) -SELECT name -FROM `%1$scategory006` -ORDER BY id2; - -INSERT IGNORE INTO `%2$sfeed` (url, category, name, website, description, priority, pathEntries, httpAuth, keep_history) -SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, IF(keep_history = 1, -1, -2) -FROM `%1$sfeed006` -ORDER BY id2; - -ALTER TABLE `%1$sentry006` ADD id2 bigint; - -UPDATE `%1$sentry006` SET id2 = ((date * 1000000) + (rand() * 100000000)); - -INSERT IGNORE INTO `%2$sentry` (id, guid, title, author, link, date, is_read, is_favorite, id_feed, tags) -SELECT e0.id2, e0.guid, e0.title, e0.author, e0.link, e0.date, e0.is_read, e0.is_favorite, f0.id2, e0.tags -FROM `%1$sentry006` e0 -INNER JOIN `%1$sfeed006` f0 ON e0.id_feed = f0.id; -'); - -define('SQL_CONVERT_SELECTv006', ' -SELECT e0.id2, e0.content -FROM `%1$sentry006` e0 -INNER JOIN `%2$sentry` e1 ON e0.id2 = e1.id -WHERE e1.content_bin IS NULL'); - -define('SQL_CONVERT_UPDATEv006', 'UPDATE `%1$sentry` SET ' - . (isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'mysql' ? 'content_bin=COMPRESS(?)' : 'content=?') - . ' WHERE id=?;'); - -define('SQL_DROP_BACKUPv006', 'DROP TABLE IF EXISTS `%1$sentry006`, `%1$sfeed006`, `%1$scategory006`;'); - -define('SQL_UPDATE_CACHED_VALUES', ' -UPDATE `%1$sfeed` f -INNER JOIN ( - SELECT e.id_feed, - COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbUnreads, - COUNT(e.id) AS nbEntries - FROM `%1$sentry` e - GROUP BY e.id_feed -) x ON x.id_feed=f.id -SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads -'); - -define('SQL_UPDATE_HISTORYv007b', 'UPDATE `%1$sfeed` SET keep_history = CASE WHEN keep_history = 0 THEN -2 WHEN keep_history = 1 THEN -1 ELSE keep_history END;'); +function param($key, $default = false) { + if (isset($_POST[$key])) { + return $_POST[$key]; + } else { + return $default; + } +} -define('SQL_GET_FEEDS', 'SELECT id, url, website FROM `%1$sfeed`;'); -// // gestion internationalisation -$translates = array (); +$translates = array(); $actual = 'en'; -function initTranslate () { +function initTranslate() { global $translates; global $actual; @@ -121,81 +61,93 @@ function initTranslate () { } } -function getBetterLanguage ($fallback) { - $available = availableLanguages (); +function getBetterLanguage($fallback) { + $available = availableLanguages(); $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE']; - $language = strtolower (substr ($accept, 0, 2)); + $language = strtolower(substr($accept, 0, 2)); - if (isset ($available[$language])) { + if (isset($available[$language])) { return $language; } else { return $fallback; } } -function availableLanguages () { - return array ( + +function availableLanguages() { + return array( 'en' => 'English', 'fr' => 'Français' ); } -function _t ($key) { + +function _t($key) { global $translates; $translate = $key; - if (isset ($translates[$key])) { + if (isset($translates[$key])) { $translate = $translates[$key]; } - $args = func_get_args (); + $args = func_get_args(); unset($args[0]); - return vsprintf ($translate, $args); + return vsprintf($translate, $args); } + /*** SAUVEGARDES ***/ -function saveLanguage () { - if (!empty ($_POST)) { - if (!isset ($_POST['language'])) { +function saveLanguage() { + if (!empty($_POST)) { + if (!isset($_POST['language'])) { return false; } $_SESSION['language'] = $_POST['language']; - header ('Location: index.php?step=1'); + header('Location: index.php?step=1'); } } -function saveStep2 () { - if (!empty ($_POST)) { - if (empty ($_POST['title']) || - empty ($_POST['old_entries']) || - empty ($_POST['auth_type']) || - empty ($_POST['default_user'])) { - return false; - } - $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); - $_SESSION['title'] = substr(trim($_POST['title']), 0, 25); - $_SESSION['old_entries'] = $_POST['old_entries']; - if ((!ctype_digit($_SESSION['old_entries'])) || ($_SESSION['old_entries'] < 1)) { - $_SESSION['old_entries'] = 3; - } - $_SESSION['mail_login'] = filter_var($_POST['mail_login'], FILTER_VALIDATE_EMAIL); - $_SESSION['default_user'] = substr(preg_replace('/[^a-zA-Z0-9]/', '', $_POST['default_user']), 0, 16); - $_SESSION['auth_type'] = $_POST['auth_type']; - if (!empty($_POST['passwordPlain'])) { +function saveStep2() { + if (!empty($_POST)) { + $_SESSION['title'] = substr(trim(param('title', _t('freshrss'))), 0, 25); + $_SESSION['old_entries'] = param('old_entries', 3); + $_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); + + $password_plain = param('passwordPlain', false); + if ($password_plain !== false) { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } - $passwordHash = password_hash($_POST['passwordPlain'], PASSWORD_BCRYPT, array('cost' => BCRYPT_COST)); + $passwordHash = password_hash($password_plain, PASSWORD_BCRYPT, array('cost' => BCRYPT_COST)); $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js $_SESSION['passwordHash'] = $passwordHash; } + if (empty($_SESSION['title']) || + empty($_SESSION['old_entries']) || + empty($_SESSION['auth_type']) || + empty($_SESSION['default_user'])) { + return false; + } + + if (($_SESSION['auth_type'] === 'form' && empty($_SESSION['passwordHash'])) || + ($_SESSION['auth_type'] === 'persona' && empty($_SESSION['mail_login']))) { + return false; + } + + $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); + if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { + $_SESSION['old_entries'] = 3; + } + $token = ''; if ($_SESSION['mail_login']) { $token = sha1($_SESSION['salt'] . $_SESSION['mail_login']); } - $config_array = array ( + $config_array = array( 'language' => $_SESSION['language'], 'theme' => $_SESSION['theme'], 'old_entries' => $_SESSION['old_entries'], @@ -214,12 +166,12 @@ function saveStep2 () { file_put_contents($personaFile, $_SESSION['default_user']); } - header ('Location: index.php?step=3'); + header('Location: index.php?step=3'); } } -function saveStep3 () { - if (!empty ($_POST)) { +function saveStep3() { + if (!empty($_POST)) { if ($_SESSION['bd_type'] === 'sqlite') { $_SESSION['bd_base'] = $_SESSION['default_user']; $_SESSION['bd_host'] = ''; @@ -228,10 +180,10 @@ function saveStep3 () { $_SESSION['bd_prefix'] = ''; $_SESSION['bd_prefix_user'] = ''; //No prefix for SQLite } else { - if (empty ($_POST['type']) || - empty ($_POST['host']) || - empty ($_POST['user']) || - empty ($_POST['base'])) { + if (empty($_POST['type']) || + empty($_POST['host']) || + empty($_POST['user']) || + empty($_POST['base'])) { $_SESSION['bd_error'] = 'Missing parameters!'; } $_SESSION['bd_base'] = substr($_POST['base'], 0, 64); @@ -239,7 +191,7 @@ 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'] . '_')); } $ini_array = array( @@ -268,15 +220,11 @@ function saveStep3 () { @unlink(DATA_PATH . '/config.php'); //To avoid access-rights problems file_put_contents(DATA_PATH . '/config.php', " 'SET NAMES utf8', - ); - break; - case 'sqlite': - return false; //No update for SQLite needed so far - default: - return false; - } - - $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - - $stm = $c->prepare(SQL_SHOW_TABLES); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - if (!in_array($_SESSION['bd_prefix'] . 'entry006', $res)) { - return false; - } - - $sql = sprintf(SQL_SHOW_COLUMNS_UPDATEv006, $_SESSION['bd_prefix']); - $stm = $c->prepare($sql); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - if (!in_array('id2', $res)) { - if (!$perform) { - return true; - } - $sql = sprintf(SQL_UPDATEv006, $_SESSION['bd_prefix'], $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql, array(PDO::ATTR_EMULATE_PREPARES => true)); - $stm->execute(); - } - - $sql = sprintf(SQL_CONVERT_SELECTv006, $_SESSION['bd_prefix'], $_SESSION['bd_prefix_user']); - if (!$perform) { - $sql .= ' LIMIT 1'; - } - $stm = $c->prepare($sql); - $stm->execute(); - if (!$perform) { - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - return count($res) > 0; - } else { - @set_time_limit(300); - } - - $c2 = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf(SQL_CONVERT_UPDATEv006, $_SESSION['bd_prefix_user']); - $stm2 = $c2->prepare($sql); - while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { - $id = $row['id2']; - $content = unserialize(gzinflate(base64_decode($row['content']))); - $stm2->execute(array($content, $id)); - } - - return true; - } catch (PDOException $e) { - return false; - } - return false; -} - function newPdo() { switch ($_SESSION['bd_type']) { - case 'mysql': - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', - ); - break; - case 'sqlite': - $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - break; - default: - return false; + case 'mysql': + $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; + $driver_options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', + ); + break; + case 'sqlite': + $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + break; + default: + return false; } return new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); } -function postUpdate() { - $c = newPdo(); - - if ($_SESSION['bd_type'] !== 'sqlite') { //No update for SQLite needed yet - $sql = sprintf(SQL_UPDATE_HISTORYv007b, $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql); - $stm->execute(); - - $sql = sprintf(SQL_UPDATE_CACHED_VALUES, $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql); - $stm->execute(); - } - - // - $sql = sprintf(SQL_GET_FEEDS, $_SESSION['bd_prefix_user']); - $stm = $c->prepare($sql); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_ASSOC); - foreach ($res as $feed) { - if (empty($feed['url'])) { - continue; - } - $hash = hash('crc32b', $_SESSION['salt'] . $feed['url']); - @file_put_contents(DATA_PATH . '/favicons/' . $hash . '.txt', - empty($feed['website']) ? $feed['url'] : $feed['website']); - } - // -} - -function deleteInstall () { - $res = unlink (DATA_PATH . '/do-install.txt'); - if ($res) { - header ('Location: index.php'); - } - - $needs = array('bd_type', 'bd_host', 'bd_base', 'bd_user', 'bd_password', 'bd_prefix'); - foreach ($needs as $need) { - if (!isset($_SESSION[$need])) { - return false; - } - } - - try { - $c = newPdo(); - $sql = sprintf(SQL_DROP_BACKUPv006, $_SESSION['bd_prefix']); - $stm = $c->prepare($sql); - $stm->execute(); +function deleteInstall() { + $res = unlink(DATA_PATH . '/do-install.txt'); - return true; - } catch (PDOException $e) { + if (!$res) { return false; } - return false; -} -function moveOldFiles() { - $mvs = array( - '/app/configuration/application.ini' => '/data/application.ini', //v0.6 - '/public/data/Configuration.array.php' => '/data/Configuration.array.php', //v0.6 - ); - $ok = true; - foreach ($mvs as $fFrom => $fTo) { - if (file_exists(FRESHRSS_PATH . $fFrom)) { - if (copy(FRESHRSS_PATH . $fFrom, FRESHRSS_PATH . $fTo)) { - @unlink(FRESHRSS_PATH . $fFrom); - } else { - $ok = false; - } - } - } - return $ok; + header('Location: index.php'); } -function delTree($dir) { //http://php.net/rmdir#110489 - if (!is_dir($dir)) { - return true; - } - $files = array_diff(scandir($dir), array('.', '..')); - foreach ($files as $file) { - $f = $dir . '/' . $file; - if (is_dir($f)) { - @chmod($f, 0777); - delTree($f); - } - else unlink($f); - } - return rmdir($dir); -} /*** VÉRIFICATIONS ***/ -function checkStep () { - $s0 = checkStep0 (); - $s1 = checkStep1 (); - $s2 = checkStep2 (); - $s3 = checkStep3 (); +function checkStep() { + $s0 = checkStep0(); + $s1 = checkStep1(); + $s2 = checkStep2(); + $s3 = checkStep3(); if (STEP > 0 && $s0['all'] != 'ok') { - header ('Location: index.php?step=0'); + header('Location: index.php?step=0'); } elseif (STEP > 1 && $s1['all'] != 'ok') { - header ('Location: index.php?step=1'); + header('Location: index.php?step=1'); } elseif (STEP > 2 && $s2['all'] != 'ok') { - header ('Location: index.php?step=2'); + header('Location: index.php?step=2'); } elseif (STEP > 3 && $s3['all'] != 'ok') { - header ('Location: index.php?step=3'); + header('Location: index.php?step=3'); } $_SESSION['actualize_feeds'] = true; } -function checkStep0 () { - moveOldFiles(); - - if (file_exists(DATA_PATH . '/config.php')) { - $ini_array = include(DATA_PATH . '/config.php'); - } elseif (file_exists(DATA_PATH . '/application.ini')) { //v0.6 - $ini_array = parse_ini_file(DATA_PATH . '/application.ini', true); - $ini_array['general']['title'] = empty($ini_array['general']['title']) ? '' : stripslashes($ini_array['general']['title']); - } else { - $ini_array = null; - } - - if ($ini_array) { - $ini_general = isset($ini_array['general']) ? $ini_array['general'] : null; - if ($ini_general) { - $keys = array('environment', 'salt', 'title', 'default_user', 'allow_anonymous', 'allow_anonymous_refresh', 'auth_type', 'api_enabled', 'unsafe_autologin_enabled'); - foreach ($keys as $key) { - if ((empty($_SESSION[$key])) && isset($ini_general[$key])) { - $_SESSION[$key] = $ini_general[$key]; - } - } - } - $ini_db = isset($ini_array['db']) ? $ini_array['db'] : null; - if ($ini_db) { - $keys = array('type', 'host', 'user', 'password', 'base', 'prefix'); - foreach ($keys as $key) { - if ((!isset($_SESSION['bd_' . $key])) && isset($ini_db[$key])) { - $_SESSION['bd_' . $key] = $ini_db[$key]; - } - } - } - } - - if (isset($_SESSION['default_user']) && file_exists(DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php')) { - $userConfig = include(DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php'); - } elseif (file_exists(DATA_PATH . '/Configuration.array.php')) { - $userConfig = include(DATA_PATH . '/Configuration.array.php'); //v0.6 - if (empty($_SESSION['auth_type'])) { - $_SESSION['auth_type'] = empty($userConfig['mail_login']) ? 'none' : 'persona'; - } - if (!isset($_SESSION['allow_anonymous'])) { - $_SESSION['allow_anonymous'] = empty($userConfig['anon_access']) ? false : ($userConfig['anon_access'] === 'yes'); - } - } else { - $userConfig = array(); - } - if (empty($_SESSION['auth_type'])) { //v0.7b - $_SESSION['auth_type'] = ''; - } - - $keys = array('language', 'theme', 'old_entries', 'mail_login', 'passwordHash'); - foreach ($keys as $key) { - if ((!isset($_SESSION[$key])) && isset($userConfig[$key])) { - $_SESSION[$key] = $userConfig[$key]; - } - } - - $languages = availableLanguages (); - $language = isset ($_SESSION['language']) && - isset ($languages[$_SESSION['language']]); - if (empty($_SESSION['passwordHash'])) { //v0.7b - $_SESSION['passwordHash'] = ''; - } - if (empty($_SESSION['theme'])) { - $_SESSION['theme'] = 'Origine'; - } else { - switch (strtolower($_SESSION['theme'])) { - case 'default': //v0.7b - $_SESSION['theme'] = 'Origine'; - break; - case 'flat-design': //v0.7b - $_SESSION['theme'] = 'Flat'; - break; - case 'default_dark': //v0.7b - $_SESSION['theme'] = 'Dark'; - break; - } - } +function checkStep0() { + $languages = availableLanguages(); + $language = isset($_SESSION['language']) && + isset($languages[$_SESSION['language']]); - return array ( + return array( 'language' => $language ? 'ok' : 'ko', 'all' => $language ? 'ok' : 'ko' ); } -function checkStep1 () { - $php = version_compare (PHP_VERSION, '5.2.1') >= 0; - $minz = file_exists (LIB_PATH . '/Minz'); - $curl = extension_loaded ('curl'); - $pdo_mysql = extension_loaded ('pdo_mysql'); - $pdo_sqlite = extension_loaded ('pdo_sqlite'); +function checkStep1() { + $php = version_compare(PHP_VERSION, '5.2.1') >= 0; + $minz = file_exists(LIB_PATH . '/Minz'); + $curl = extension_loaded('curl'); + $pdo_mysql = extension_loaded('pdo_mysql'); + $pdo_sqlite = extension_loaded('pdo_sqlite'); $pdo = $pdo_mysql || $pdo_sqlite; - $pcre = extension_loaded ('pcre'); - $ctype = extension_loaded ('ctype'); + $pcre = extension_loaded('pcre'); + $ctype = extension_loaded('ctype'); $dom = class_exists('DOMDocument'); - $data = DATA_PATH && is_writable (DATA_PATH); - $cache = CACHE_PATH && is_writable (CACHE_PATH); - $log = LOG_PATH && is_writable (LOG_PATH); - $favicons = is_writable (DATA_PATH . '/favicons'); - $persona = is_writable (DATA_PATH . '/persona'); + $data = DATA_PATH && is_writable(DATA_PATH); + $cache = CACHE_PATH && is_writable(CACHE_PATH); + $log = LOG_PATH && is_writable(LOG_PATH); + $favicons = is_writable(DATA_PATH . '/favicons'); + $persona = is_writable(DATA_PATH . '/persona'); - return array ( + return array( 'php' => $php ? 'ok' : 'ko', 'minz' => $minz ? 'ok' : 'ko', 'curl' => $curl ? 'ok' : 'ko', @@ -601,44 +323,49 @@ function checkStep1 () { 'log' => $log ? 'ok' : 'ko', 'favicons' => $favicons ? 'ok' : 'ko', 'persona' => $persona ? 'ok' : 'ko', - 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $data && $cache && $log && $favicons && $persona ? 'ok' : 'ko' + 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && + $data && $cache && $log && $favicons && $persona ? 'ok' : 'ko' ); } -function checkStep2 () { - $conf = !empty($_SESSION['salt']) && - !empty($_SESSION['title']) && +function checkStep2() { + $conf = !empty($_SESSION['title']) && !empty($_SESSION['old_entries']) && isset($_SESSION['mail_login']) && !empty($_SESSION['default_user']); + + $form = $_SESSION['auth_type'] != 'form' || !empty($_SESSION['passwordHash']); + + $persona = $_SESSION['auth_type'] != 'persona' || !empty($_SESSION['mail_login']); + $defaultUser = empty($_POST['default_user']) ? null : $_POST['default_user']; if ($defaultUser === null) { $defaultUser = empty($_SESSION['default_user']) ? '' : $_SESSION['default_user']; } $data = is_writable(DATA_PATH . '/' . $defaultUser . '_user.php'); - if ($data) { - @unlink(DATA_PATH . '/Configuration.array.php'); //v0.6 - } - return array ( + return array( 'conf' => $conf ? 'ok' : 'ko', + 'form' => $form ? 'ok' : 'ko', + 'persona' => $persona ? 'ok' : 'ko', 'data' => $data ? 'ok' : 'ko', - 'all' => $conf && $data ? 'ok' : 'ko' + 'all' => $conf && $form && $persona && $data ? 'ok' : 'ko' ); } -function checkStep3 () { + +function checkStep3() { $conf = is_writable(DATA_PATH . '/config.php'); - $bd = isset ($_SESSION['bd_type']) && - isset ($_SESSION['bd_host']) && - isset ($_SESSION['bd_user']) && - isset ($_SESSION['bd_password']) && - isset ($_SESSION['bd_base']) && - isset ($_SESSION['bd_prefix']) && - isset ($_SESSION['bd_error']); + $bd = isset($_SESSION['bd_type']) && + isset($_SESSION['bd_host']) && + isset($_SESSION['bd_user']) && + isset($_SESSION['bd_password']) && + isset($_SESSION['bd_base']) && + isset($_SESSION['bd_prefix']) && + isset($_SESSION['bd_error']); $conn = empty($_SESSION['bd_error']); - return array ( + return array( 'bd' => $bd ? 'ok' : 'ko', 'conn' => $conn ? 'ok' : 'ko', 'conf' => $conf ? 'ok' : 'ko', @@ -646,51 +373,41 @@ function checkStep3 () { ); } -function checkBD () { +function checkBD() { $ok = false; try { $str = ''; $driver_options = null; switch ($_SESSION['bd_type']) { - case 'mysql': - $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' - ); - - try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';'; - $c = new PDO ($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); - $sql = sprintf (SQL_CREATE_DB, $_SESSION['bd_base']); - $res = $c->query ($sql); - } catch (PDOException $e) { - } - - // on écrase la précédente connexion en sélectionnant la nouvelle BDD - $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; - break; - case 'sqlite': - $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; - $driver_options = array( - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - ); - break; - default: - return false; - } - - $c = new PDO ($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + case 'mysql': + $driver_options = array( + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + ); - if ($_SESSION['bd_type'] !== 'sqlite') { //No SQL backup for SQLite - $stm = $c->prepare(SQL_SHOW_TABLES); - $stm->execute(); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - if (in_array($_SESSION['bd_prefix'] . 'entry', $res) && !in_array($_SESSION['bd_prefix'] . 'entry006', $res)) { - $sql = sprintf(SQL_BACKUP006, $_SESSION['bd_prefix']); //v0.6 - $res = $c->query($sql); //Backup tables + try { // on ouvre une connexion juste pour créer la base si elle n'existe pas + $str = 'mysql:host=' . $_SESSION['bd_host'] . ';'; + $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + $sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']); + $res = $c->query($sql); + } catch (PDOException $e) { } + + // on écrase la précédente connexion en sélectionnant la nouvelle BDD + $str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base']; + break; + case 'sqlite': + $str = 'sqlite:' . DATA_PATH . '/' . $_SESSION['default_user'] . '.sqlite'; + $driver_options = array( + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + break; + default: + return false; } + $c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options); + if (defined('SQL_CREATE_TABLES')) { $sql = sprintf(SQL_CREATE_TABLES, $_SESSION['bd_prefix_user'], _t('default_category')); $stm = $c->prepare($sql); @@ -719,20 +436,20 @@ function checkBD () { } /*** AFFICHAGE ***/ -function printStep0 () { +function printStep0() { global $actual; ?> - -

+ +

- +
- +
+
- +
- +
- +
- +
- + + - +
@@ -887,25 +608,45 @@ function printStep2 () {
- + />
- +
- - + /> +
+ +
- - + + - +
@@ -913,29 +654,29 @@ function printStep2 () { - -

+ +

-

+

- +
- +
+
- +
- +
- +
- +
- +
- +
- +
- +
@@ -988,10 +729,10 @@ function printStep3 () {
- - + + - +
@@ -999,74 +740,40 @@ function printStep3 () { - - - - -

- -
-
- - -
-
- - -

- -
-
- -
-
- -
+

+ -

- +

-

- @@ -1075,7 +782,7 @@ case 6: - <?php echo _t ('freshrss_installation'); ?> + <?php echo _t('freshrss_installation'); ?> @@ -1083,20 +790,19 @@ case 6:
-

-

+

+

@@ -1104,25 +810,22 @@ case 6: switch (STEP) { case 0: default: - printStep0 (); + printStep0(); break; case 1: - printStep1 (); + printStep1(); break; case 2: - printStep2 (); + printStep2(); break; case 3: - printStep3 (); + printStep3(); break; case 4: - printStep4 (); + printStep4(); break; case 5: - printStep5 (); - break; - case 6: - printStep6 (); + printStep5(); break; } ?> From e7dba0ce7cfaf5e84687593a8b0d58d89fbff302 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sat, 9 Aug 2014 23:29:13 +0200 Subject: [PATCH 002/126] Add basic system of update - Check on update.freshrss.org for new updates - Download script - Apply script - Need translations and verifications NOTE: current script on server indicates version 0.7.3 is an update of 0.8-dev ==> IT'S ONLY FOR MY TESTS! Script just does a backup of ./data actually... See https://github.com/marienfressinaud/FreshRSS/issues/411 --- app/Controllers/updateController.php | 106 +++++++++++++++++++++++++++ app/i18n/en.php | 2 + app/i18n/fr.php | 2 + app/layout/aside_configure.phtml | 38 ++++++---- app/layout/header.phtml | 6 ++ app/views/update/index.phtml | 19 +++++ constants.php | 2 + lib/Minz/View.php | 15 +++- 8 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 app/Controllers/updateController.php create mode 100644 app/views/update/index.phtml diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php new file mode 100644 index 000000000..a15cb9fd5 --- /dev/null +++ b/app/Controllers/updateController.php @@ -0,0 +1,106 @@ +view->loginOk && Minz_Configuration::isAdmin($current_user)) { + Minz_Error::error( + 403, + array('error' => array(_t('access_denied'))) + ); + } + + Minz_View::prependTitle(_t('update_system') . ' · '); + } + + public function indexAction() { + if (file_exists(UPDATE_FILENAME)) { + // There is an update file to apply! + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + + return; + } + } + + public function checkAction() { + $this->view->change_view('update', 'index'); + + if (file_exists(UPDATE_FILENAME)) { + // There is already an update file to apply: we don't need to check + // the webserver! + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + + return; + } + + $c = curl_init(FRESHRSS_UPDATE_WEBSITE); + curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + $result = curl_exec($c); + + if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) { + $res_array = explode("\n", $result, 2); + $status = $res_array[0]; + + if (strpos($status, 'UPDATE') === 0) { + $script = $res_array[1]; + if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_problem') + ); + } + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('no_update') + ); + } + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) + ); + } + curl_close($c); + } + + public function applyAction() { + require(UPDATE_FILENAME); + $res = apply_update(); + + if ($res === true) { + @unlink(UPDATE_FILENAME); + + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => Minz_Translate::t('update_finished') + )); + + Minz_Request::forward(array(), true); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('update_failed', $res) + )); + + Minz_Request::forward(array('c' => 'update'), true); + } + } +} \ No newline at end of file diff --git a/app/i18n/en.php b/app/i18n/en.php index d80299b10..6110ccb11 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -408,4 +408,6 @@ return array ( 'stats_entry_per_category' => 'Entries per category', 'stats_top_feed' => 'Top ten feeds', 'stats_entry_count' => 'Entry count', + + 'update_can_apply' => 'There is an available update. Apply', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 4be028ac3..5f88aa069 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -408,4 +408,6 @@ return array ( 'stats_entry_per_category' => 'Articles par catégorie', 'stats_top_feed' => 'Les dix plus gros flux', 'stats_entry_count' => 'Nombre d’articles', + + 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer', ); diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index e66f2f64c..03bea4836 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -1,25 +1,33 @@ diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 2e42bfdea..028e63d8a 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -75,6 +75,12 @@ if (Minz_Configuration::canLogIn()) {
  • + +
  • +
  • diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml new file mode 100644 index 000000000..a1a872845 --- /dev/null +++ b/app/views/update/index.phtml @@ -0,0 +1,19 @@ +partial('aside_configure'); ?> + +
    +

    + + message)) { ?> +

    + message['title']; ?> + message['body']; ?> +

    + + + message) || $this->message['status'] !== 'good') { ?> +

    + + last_update_time); ?> +

    + +
    diff --git a/constants.php b/constants.php index e32b8cbc3..a968b82f4 100644 --- a/constants.php +++ b/constants.php @@ -1,6 +1,7 @@ change_view(Minz_Request::controllerName(), + Minz_Request::actionName()); + self::$title = Minz_Configuration::title (); + } + + /** + * Change le fichier de vue en fonction d'un controller / action + */ + public function change_view($controller_name, $action_name) { $this->view_filename = APP_PATH . self::VIEWS_PATH_NAME . '/' - . Minz_Request::controllerName () . '/' - . Minz_Request::actionName () . '.phtml'; - - self::$title = Minz_Configuration::title (); + . $controller_name . '/' + . $action_name . '.phtml'; } /** From 7ed111b1bf152613d17254808a4fcf89f5774297 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 10:35:17 +0200 Subject: [PATCH 003/126] Add translations for update system --- app/Controllers/updateController.php | 7 +++++-- app/i18n/en.php | 9 +++++++++ app/i18n/fr.php | 11 ++++++++++- app/views/update/index.phtml | 4 +++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index a15cb9fd5..a94af4417 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -11,6 +11,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } Minz_View::prependTitle(_t('update_system') . ' · '); + $this->view->last_update_time = 'unknown'; // TODO } public function indexAction() { @@ -61,7 +62,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { $this->view->message = array( 'status' => 'bad', 'title' => _t('damn'), - 'body' => _t('update_problem') + 'body' => _t('update_problem', 'Cannot save the update script') ); } } else { @@ -88,6 +89,8 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); + // TODO: record last update + Minz_Session::_param('notification', array( 'type' => 'good', 'content' => Minz_Translate::t('update_finished') @@ -97,7 +100,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } else { Minz_Session::_param('notification', array( 'type' => 'bad', - 'content' => Minz_Translate::t('update_failed', $res) + 'content' => Minz_Translate::t('update_problem', $res) )); Minz_Request::forward(array('c' => 'update'), true); diff --git a/app/i18n/en.php b/app/i18n/en.php index 6110ccb11..6a0b4a139 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -192,6 +192,7 @@ return array ( 'informations' => 'Information', 'damn' => 'Damn!', + 'ok' => 'Ok!', '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', @@ -409,5 +410,13 @@ return array ( 'stats_top_feed' => 'Top ten feeds', 'stats_entry_count' => 'Entry count', + 'update' => 'Update', + 'update_system' => 'Update system', + 'update_check' => 'Check for new updates', + 'update_last' => 'Last update: %s', 'update_can_apply' => 'There is an available update. 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!', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 5f88aa069..d0637b9f7 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -192,6 +192,7 @@ return array ( 'informations' => 'Informations', 'damn' => 'Arf !', + 'ok' => 'Ok !', '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', @@ -409,5 +410,13 @@ return array ( 'stats_top_feed' => 'Les dix plus gros flux', 'stats_entry_count' => 'Nombre d’articles', - 'update_can_apply' => 'Il y’a une mise à jour à appliquer. Appliquer', + 'update' => 'Mise à jour', + 'update_system' => 'Système de mise à jour', + 'update_check' => 'Vérifier les mises à jour', + 'update_last' => 'Dernière mise à jour : %s', + 'update_can_apply' => 'Il y’a une mise à jour à appliquer. 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 !', ); diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index a1a872845..8f6ee6269 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -12,8 +12,10 @@ message) || $this->message['status'] !== 'good') { ?>

    - last_update_time); ?>

    +

    + +

    From 9a5d6245fbeb413766362fd6b2c4f5f5b6a22a22 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 10:55:51 +0200 Subject: [PATCH 004/126] Improve update API Update script must implement 4 functions: - apply_update() to perform the update (most important). Return true if all is ok, else false. - need_info_update() returns true if we need more info for update, else false. If this function always returns false, you don't need to implement following functions (but it's better to not forget) - ask_info_update() should be a HTML form to ask infos. Method must be post and action must point to _url('update', 'apply') (or leave it blank) - save_info_update() is called for POST requests (to save form from ask_info_update()) --- app/Controllers/updateController.php | 37 +++++++++++++++++----------- app/views/update/apply.phtml | 9 +++++++ app/views/update/index.phtml | 2 ++ 3 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 app/views/update/apply.phtml diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index a94af4417..1095f9da7 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -84,26 +84,33 @@ class FreshRSS_update_Controller extends Minz_ActionController { public function applyAction() { require(UPDATE_FILENAME); - $res = apply_update(); - if ($res === true) { - @unlink(UPDATE_FILENAME); + if (Minz_Request::isPost()) { + save_info_update(); + } - // TODO: record last update + if (!need_info_update()) { + $res = apply_update(); - Minz_Session::_param('notification', array( - 'type' => 'good', - 'content' => Minz_Translate::t('update_finished') - )); + if ($res === true) { + @unlink(UPDATE_FILENAME); - Minz_Request::forward(array(), true); - } else { - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('update_problem', $res) - )); + // TODO: record last update + + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => Minz_Translate::t('update_finished') + )); - Minz_Request::forward(array('c' => 'update'), true); + Minz_Request::forward(array(), true); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('update_problem', $res) + )); + + Minz_Request::forward(array('c' => 'update'), true); + } } } } \ No newline at end of file diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml new file mode 100644 index 000000000..d7ea466c5 --- /dev/null +++ b/app/views/update/apply.phtml @@ -0,0 +1,9 @@ +partial('aside_configure'); ?> + +
    + + +

    + + +
    \ No newline at end of file diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 8f6ee6269..1824c02b8 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -1,6 +1,8 @@ partial('aside_configure'); ?>
    + +

    message)) { ?> From 3ca8c7ec4c55b4fa751fbcdc8e28f28351c4a967 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Sun, 10 Aug 2014 11:52:18 +0200 Subject: [PATCH 005/126] Litlle improvements (update system) - Check UPDATE_FILENAME exists before applying update - Add empty line at the end of files --- app/Controllers/updateController.php | 7 ++++++- app/views/update/apply.phtml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 1095f9da7..fa62f4a70 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -79,10 +79,15 @@ class FreshRSS_update_Controller extends Minz_ActionController { 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) ); } + curl_close($c); } public function applyAction() { + if (!file_exists(UPDATE_FILENAME)) { + Minz_Request::forward(array('c' => 'update'), true); + } + require(UPDATE_FILENAME); if (Minz_Request::isPost()) { @@ -113,4 +118,4 @@ class FreshRSS_update_Controller extends Minz_ActionController { } } } -} \ No newline at end of file +} diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml index d7ea466c5..30566c7ab 100644 --- a/app/views/update/apply.phtml +++ b/app/views/update/apply.phtml @@ -6,4 +6,4 @@

    -
    \ No newline at end of file +
    From 909d8747ba09f9c9a6ac895f1f4f0763bdb27a55 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 12 Aug 2014 20:15:46 +0200 Subject: [PATCH 006/126] Update system now uses HTTPS connection - Add some curl checks - Refactor code --- app/Controllers/updateController.php | 60 +++++++++++++++------------- constants.php | 2 +- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index fa62f4a70..857d975b2 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -44,43 +44,47 @@ class FreshRSS_update_Controller extends Minz_ActionController { $c = curl_init(FRESHRSS_UPDATE_WEBSITE); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); + curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true); + curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); $result = curl_exec($c); + $c_status = curl_getinfo($c, CURLINFO_HTTP_CODE); + curl_close($c); - if (curl_getinfo($c, CURLINFO_HTTP_CODE) == 200) { - $res_array = explode("\n", $result, 2); - $status = $res_array[0]; - - if (strpos($status, 'UPDATE') === 0) { - $script = $res_array[1]; - if (file_put_contents(UPDATE_FILENAME, $script) !== false) { - $this->view->message = array( - 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) - ); - } else { - $this->view->message = array( - 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('update_problem', 'Cannot save the update script') - ); - } - } else { - $this->view->message = array( - 'status' => 'bad', - 'title' => _t('damn'), - 'body' => _t('no_update') - ); - } - } else { + if ($c_status !== 200) { $this->view->message = array( 'status' => 'bad', 'title' => _t('damn'), 'body' => _t('update_server_not_found', FRESHRSS_UPDATE_WEBSITE) ); + return; } - curl_close($c); + $res_array = explode("\n", $result, 2); + $status = $res_array[0]; + if (strpos($status, 'UPDATE') !== 0) { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('no_update') + ); + + return; + } + + $script = $res_array[1]; + if (file_put_contents(UPDATE_FILENAME, $script) !== false) { + $this->view->message = array( + 'status' => 'good', + 'title' => _t('ok'), + 'body' => _t('update_can_apply', _url('update', 'apply')) + ); + } else { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('update_problem', 'Cannot save the update script') + ); + } } public function applyAction() { diff --git a/constants.php b/constants.php index a968b82f4..ba9c508dc 100644 --- a/constants.php +++ b/constants.php @@ -1,7 +1,7 @@ Date: Sun, 24 Aug 2014 11:34:58 -0400 Subject: [PATCH 007/126] Add shortcuts Add a shortcut to open the wiki. Shortcut is F1 and is not modifiable. Add a shortcut to access user filters the same way to access share actions. --- app/Controllers/configureController.php | 2 +- app/Models/Configuration.php | 2 + app/i18n/en.php | 2 + app/i18n/fr.php | 2 + app/layout/nav_menu.phtml | 2 +- app/views/configure/shortcut.phtml | 8 ++++ app/views/helpers/javascript_vars.phtml | 7 +++- constants.php | 1 + p/scripts/main.js | 49 ++++++++++++++++++++++++- 9 files changed, 70 insertions(+), 5 deletions(-) diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index bb96bfae3..461cbceda 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -240,7 +240,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'm', 'n', 'o', 'p', 'page_down', 'page_up', 'q', 'r', 'return', 'right', 's', 'space', 't', 'tab', 'u', 'up', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', + '9', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'); $this->view->list_keys = $list_keys; diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 4c804a9fb..989603b20 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -45,6 +45,8 @@ class FreshRSS_Configuration { 'load_more' => 'm', 'auto_share' => 's', 'focus_search' => 'a', + 'user_filter' => 'u', + 'help' => 'f1', ), 'topline_read' => true, 'topline_favorite' => true, diff --git a/app/i18n/en.php b/app/i18n/en.php index be0cdc642..76da5d182 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -180,6 +180,8 @@ return array ( 'auto_share' => 'Share', 'auto_share_help' => 'If there is only one sharing mode, it is used. Else modes are accessible by their number.', 'focus_search' => 'Access search box', + 'user_filter' => 'Access user filters', + 'user_filter_help' => 'If there is only one user filter, it is used. Else filters are accessible by their number.', 'file_to_import' => 'File to import
    (OPML, Json or Zip)', 'file_to_import_no_zip' => 'File to import
    (OPML or Json)', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 08f12234e..a8f7a3a98 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -180,6 +180,8 @@ return array ( 'auto_share' => 'Partager', 'auto_share_help' => 'S’il n’y a qu’un mode de partage, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.', 'focus_search' => 'Accéder à la recherche', + 'user_filter' => 'Accéder aux filtres utilisateur', + 'user_filter_help' => 'S’il n’y a qu’un filtre utilisateur, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.', 'file_to_import' => 'Fichier à importer
    (OPML, Json ou Zip)', 'file_to_import_no_zip' => 'Fichier à importer
    (OPML ou Json)', diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 25833c16d..7cd15c1a3 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -96,7 +96,7 @@ conf->queries as $query) { ?> -
  • +
  • diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index bfb13f003..73ad0ebb8 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -103,6 +103,14 @@ +
    + +
    + + +
    +
    +
    diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 7144c519a..2144f1576 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -4,7 +4,8 @@ echo '"use strict";', "\n"; $mark = $this->conf->mark_when; echo 'var ', - 'hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true', + 'help_url="', FRESHRSS_WIKI, '"', + ',hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true', ',display_order="', Minz_Request::param('order', $this->conf->sort_order), '"', ',auto_mark_article=', $mark['article'] ? 'true' : 'false', ',auto_mark_site=', $mark['site'] ? 'true' : 'false', @@ -25,7 +26,9 @@ echo ',shortcuts={', 'collapse_entry:"', $s['collapse_entry'], '",', 'load_more:"', $s['load_more'], '",', 'auto_share:"', $s['auto_share'], '",', - 'focus_search:"', $s['focus_search'], '"', + 'focus_search:"', $s['focus_search'], '",', + 'user_filter:"', $s['user_filter'], '",', + 'help:"', $s['help'], '"', "},\n"; if (Minz_Request::param ('output') === 'global') { diff --git a/constants.php b/constants.php index e32b8cbc3..483989371 100644 --- a/constants.php +++ b/constants.php @@ -1,6 +1,7 @@ Date: Sun, 24 Aug 2014 11:51:23 -0400 Subject: [PATCH 008/126] Fix category CSS Before, when the category is closed by default, the active CSS class wasn't applied. Thus breaking the shortcuts for navigation. Now it works the way it is supposed. --- app/layout/aside_flux.phtml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 432d6fdb7..357aa1cd3 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -42,21 +42,19 @@ $feeds = $cat->feeds (); if (!empty ($feeds)) { $c_active = false; - if ($this->conf->display_categories) { - if ($this->get_c == $cat->id () && $this->get_f) { - $c_active = true; - } - } else { - if ($this->get_c == $cat->id ()) { - $c_active = true; + $c_show = false; + if ($this->get_c == $cat->id ()) { + $c_active = true; + if (!$this->conf->display_categories || $this->get_f) { + $c_show = true; } } ?>
  • >
        id (); $nbEntries = $feed->nbEntries (); From f8a522d0c1f3b26c44e566506d6079fabefc8972 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sun, 24 Aug 2014 12:46:40 -0400 Subject: [PATCH 009/126] Change wiki url --- constants.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.php b/constants.php index 483989371..90c7a38e4 100644 --- a/constants.php +++ b/constants.php @@ -1,7 +1,7 @@ Date: Mon, 25 Aug 2014 17:32:51 -0400 Subject: [PATCH 010/126] Add a link to filter categories In the category configuration page, I added a filter link on each category. It works the same way than the "filter" link on the feed configuration page. See #514 --- app/views/configure/categorize.phtml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/configure/categorize.phtml b/app/views/configure/categorize.phtml index 9bae99b39..2f0e554ca 100644 --- a/app/views/configure/categorize.phtml +++ b/app/views/configure/categorize.phtml @@ -18,6 +18,9 @@ nbFeed () > 0) { ?> + + +
  • From 80acd3a0070fb309d57898c14ccd3a3a5658e236 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 27 Aug 2014 21:02:24 -0400 Subject: [PATCH 011/126] Add configuration for help configuration --- app/Controllers/configureController.php | 2 +- app/i18n/en.php | 1 + app/i18n/fr.php | 1 + app/views/configure/shortcut.phtml | 7 +++++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 461cbceda..bb96bfae3 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -240,7 +240,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'm', 'n', 'o', 'p', 'page_down', 'page_up', 'q', 'r', 'return', 'right', 's', 'space', 't', 'tab', 'u', 'up', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', + '9', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'); $this->view->list_keys = $list_keys; diff --git a/app/i18n/en.php b/app/i18n/en.php index 76da5d182..bcd7a3da5 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -182,6 +182,7 @@ return array ( 'focus_search' => 'Access search box', 'user_filter' => 'Access user filters', 'user_filter_help' => 'If there is only one user filter, it is used. Else filters are accessible by their number.', + 'help' => 'Display documentation', 'file_to_import' => 'File to import
    (OPML, Json or Zip)', 'file_to_import_no_zip' => 'File to import
    (OPML or Json)', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index a8f7a3a98..395f2b5d2 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -182,6 +182,7 @@ return array ( 'focus_search' => 'Accéder à la recherche', 'user_filter' => 'Accéder aux filtres utilisateur', 'user_filter_help' => 'S’il n’y a qu’un filtre utilisateur, celui ci est utilisé automatiquement. Sinon ils sont accessibles par leur numéro.', + 'help' => 'Afficher la documentation', 'file_to_import' => 'Fichier à importer
    (OPML, Json ou Zip)', 'file_to_import_no_zip' => 'Fichier à importer
    (OPML ou Json)', diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index 73ad0ebb8..a4029b676 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -111,6 +111,13 @@
    +
    + +
    + +
    +
    +
    From 366550057db8f5f86afd64c99f3a399016c97a36 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 27 Aug 2014 21:07:47 -0400 Subject: [PATCH 012/126] Fix Screwdriver theme Before, it wasn't possible to hide categories when they had no articles to read. I applied the same rule than the one described in here (https://github.com/marienfressinaud/FreshRSS/commit/d19824b919289ad63743f27da7861f2422da5baa). Now, the categories are hidden when they have no articles to read. See #594 --- p/themes/Screwdriver/template.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/p/themes/Screwdriver/template.css b/p/themes/Screwdriver/template.css index bf421e322..ddb3f376f 100644 --- a/p/themes/Screwdriver/template.css +++ b/p/themes/Screwdriver/template.css @@ -309,6 +309,9 @@ a.btn { list-style: none; margin: 0; } +.state_unread li:not(.active)[data-unread="0"] { + display: none; +} .category { display: block; overflow: hidden; From a126d99b3c87c12d6da86a32f0615ad36ec99d60 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 30 Aug 2014 18:31:50 +0200 Subject: [PATCH 013/126] Bug referer for systems with non-standard HTTP port Now tests also for the scheme and port, which must be identical to the ones in the referer. https://github.com/marienfressinaud/FreshRSS/issues/565#issuecomment-53916915 https://github.com/marienfressinaud/FreshRSS/issues/554 --- app/FreshRSS.php | 3 +-- lib/Minz/Request.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 30f711e20..cf6390f68 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,8 +6,7 @@ class FreshRSS extends Minz_FrontController { } $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - if (Minz_Request::isPost() && (empty($_SERVER['HTTP_REFERER']) || - Minz_Request::getDomainName() !== parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST))) { + if (Minz_Request::isPost() && !Minz_Request::isRefererFromSameDomain()) { $loginOk = false; //Basic protection against XSRF attacks Minz_Error::error( 403, diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 52f53012f..ec4e25a6b 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -84,6 +84,20 @@ 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 From f002dbe4ceb8505b237bd67b66365d636bddd4b2 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Mon, 1 Sep 2014 20:58:05 -0400 Subject: [PATCH 014/126] Add average on repartition charts. It needs some verification on the value used to calculate the averages. --- app/Controllers/statsController.php | 3 ++ app/Models/StatsDAO.php | 62 +++++++++++++++++++++++++++++ app/views/stats/repartition.phtml | 48 +++++++++++++++++++--- 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 98f46f0d2..000b41dd2 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -67,8 +67,11 @@ class FreshRSS_stats_Controller extends Minz_ActionController { $this->view->days = $statsDAO->getDays(); $this->view->months = $statsDAO->getMonths(); $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id); + $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id); $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id); + $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id); $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); + $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id); } public function firstAction() { diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index 89be76a26..bd4271ba8 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -151,6 +151,68 @@ SQL; return $this->convertToSerie($repartition); } + /** + * Calculates the average number of article per hour per feed + * + * @param integer $feed id + * @return integer + */ + public function calculateEntryAveragePerFeedPerHour($feed = null) { + return $this->calculateEntryAveragePerFeedPerPeriod(1/24, $feed); + } + + /** + * Calculates the average number of article per day of week per feed + * + * @param integer $feed id + * @return integer + */ + public function calculateEntryAveragePerFeedPerDayOfWeek($feed = null) { + return $this->calculateEntryAveragePerFeedPerPeriod(7, $feed); + } + + /** + * Calculates the average number of article per month per feed + * + * @param integer $feed id + * @return integer + */ + public function calculateEntryAveragePerFeedPerMonth($feed = null) { + return $this->calculateEntryAveragePerFeedPerPeriod(30, $feed); + } + + /** + * Calculates the average number of article per feed + * + * @param float $period number used to divide the number of day in the period + * @param integer $feed id + * @return integer + */ + protected function calculateEntryAveragePerFeedPerPeriod($period, $feed = null) { + if ($feed) { + $restrict = "WHERE e.id_feed = {$feed}"; + } else { + $restrict = ''; + } + $sql = <<prefix}entry AS e +{$restrict} +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetch(PDO::FETCH_NAMED); + $date_min = new \DateTime(); + $date_min->setTimestamp($res['date_min']); + $date_max = new \DateTime(); + $date_max->setTimestamp($res['date_max']); + $interval = $date_max->diff($date_min, true); + + return round($res['count'] / ($interval->format('%a') / ($period)), 2); + } + /** * Initialize an array for statistics depending on a range * diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 9d2eb28e4..2331db78c 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -56,11 +56,22 @@ function initStats() { return; } // Entry per hour + var avg_h = []; + for (var i = -1; i <= 24; i++) { + avg_h.push([i, averageHour?>]); + } Flotr.draw(document.getElementById('statsEntryPerHour'), - [repartitionHour ?>], + [{ + data: repartitionHour ?>, + bars: {horizontal: false, show: true} + }, { + data: avg_h, + lines: {show: true}, + label: averageHour?>, + yaxis: 2 + }], { grid: {verticalLines: false}, - bars: {horizontal: false, show: true}, xaxis: {noTicks: 23, tickFormatter: function(x) { var x = parseInt(x); @@ -70,14 +81,26 @@ function initStats() { max: 23.9, tickDecimals: 0}, yaxis: {min: 0}, + y2axis: {showLabels: false}, mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} }); // Entry per day of week + var avg_dow = []; + for (var i = -1; i <= 7; i++) { + avg_dow.push([i, averageDayOfWeek?>]); + } Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'), - [repartitionDayOfWeek ?>], + [{ + data: repartitionDayOfWeek ?>, + bars: {horizontal: false, show: true} + }, { + data: avg_dow, + lines: {show: true}, + label: averageDayOfWeek?>, + yaxis: 2 + }], { grid: {verticalLines: false}, - bars: {horizontal: false, show: true}, xaxis: {noTicks: 6, tickFormatter: function(x) { var x = parseInt(x), @@ -88,14 +111,26 @@ function initStats() { max: 6.9, tickDecimals: 0}, yaxis: {min: 0}, + y2axis: {showLabels: false}, mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} }); // Entry per month + var avg_m = []; + for (var i = 0; i <= 13; i++) { + avg_m.push([i, averageMonth?>]); + } Flotr.draw(document.getElementById('statsEntryPerMonth'), - [repartitionMonth ?>], + [{ + data: repartitionMonth ?>, + bars: {horizontal: false, show: true} + }, { + data: avg_m, + lines: {show: true}, + label: averageMonth?>, + yaxis: 2 + }], { grid: {verticalLines: false}, - bars: {horizontal: false, show: true}, xaxis: {noTicks: 12, tickFormatter: function(x) { var x = parseInt(x), @@ -106,6 +141,7 @@ function initStats() { max: 12.9, tickDecimals: 0}, yaxis: {min: 0}, + y2axis: {showLabels: false}, mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}} }); From a2ba5e2a21ad79e065925d7642f62c5cf4083212 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Tue, 2 Sep 2014 18:50:41 +0200 Subject: [PATCH 015/126] Add a first version of i18n for German All the strings are not completed yet, but it's a good start! --- app/Models/Configuration.php | 1 + app/i18n/de.php | 326 +++++++++++++++++++++++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 app/i18n/de.php diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 4c804a9fb..e815561a9 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -61,6 +61,7 @@ class FreshRSS_Configuration { ); private $available_languages = array( + 'de' => 'Deutsch', 'en' => 'English', 'fr' => 'Français', ); diff --git a/app/i18n/de.php b/app/i18n/de.php new file mode 100644 index 000000000..4301a8b6d --- /dev/null +++ b/app/i18n/de.php @@ -0,0 +1,326 @@ + 'Login', + 'login_with_persona' => 'Login mit Persona', + 'logout' => 'Logout', + 'search' => 'Suche nach Worten oder #tags', + 'search_short' => 'Suche', + + 'configuration' => 'Konfiguration', + 'users' => 'Nutzer', + 'categories' => 'Kategorien', + 'category' => 'Kategorie', + 'feed' => 'Feed', + 'feeds' => 'Feeds', + 'shortcuts' => 'Shortcuts', + 'about' => 'Über', + 'stats' => 'Statistiken', + + 'your_rss_feeds' => 'Ihre RSS Feeds', + 'add_rss_feed' => 'RSS-Feed hinzufügen', + 'no_rss_feed' => 'Kein RSS Feed', + 'import_export_opml' => 'Import / Export (OPML)', + + 'subscription_management' => 'Abonnementsverwaltung', + 'main_stream' => 'Haupt-Nachrichtenfluß', + 'all_feeds' => 'Alle Feeds', + 'favorite_feeds' => 'Favoriten (%d)', + 'not_read' => '%d ungelesen', + 'not_reads' => '%d ungelesen', + + 'filter' => 'Filter', + 'see_website' => 'Website ansehen', + 'administration' => 'Verwaltung', + 'actualize' => 'Aktualisierung', + + 'mark_read' => 'Als gelesen markieren', + 'mark_favorite' => 'Als Favoriten markieren', + 'mark_all_read' => 'Alle als gelesen markieren', + 'mark_feed_read' => 'Feed als gelesen markieren', + 'mark_cat_read' => 'Kategorie als gelesen markieren', + 'before_one_day' => 'Vor einem Tag', + 'before_one_week' => 'Vor einer Woche', + 'display' => 'Anzeige', + 'normal_view' => 'Normale Anzeige', + 'reader_view' => 'Leseanzeige-Modus', + 'global_view' => 'Globale Anzeige', + 'rss_view' => 'RSS-Feed', + 'show_all_articles' => 'zeige alle Artikel', + 'show_not_reads' => 'zeige nicht gelesene', + 'show_read' => 'zeige nur gelesene', + 'show_favorite' => 'Favoriten anzeigen', + 'older_first' => 'Älteste zuerst', + 'newer_first' => 'Neuere zuerst', + + // Pagination + 'first' => 'Erste', + 'previous' => 'Vorherige', + 'next' => 'Nächste', + 'last' => 'Letzte', + + // CONTROLLERS + 'article_published_on' => 'Dieser Artikel erschien im Original bei %s', + 'article_published_on_author' => 'Dieser Artikel erschien im Original bei %s von %s', + + 'access_denied' => 'Sie haben nicht die Berechtigung, diese Seite aufzurufen', + 'page_not_found' => 'Sie suchen nach einer Seite, die es nicht gibt', + 'error_occurred' => 'Es gab einen Fehler', + 'error_occurred_update' => 'Es wurde nichts geändert', + + 'default_category' => 'Unkategorisiert', + 'categories_updated' => 'Kategorien wurden aktualisiert', + 'categories_management' => 'Kategorienverwaltung', + 'feed_updated' => 'Der Feed wurde aktualisiert', + 'rss_feed_management' => 'Verwaltung der RSS Feeds', + 'configuration_updated' => 'Die Konfiguration wurde aktualisiert', + 'sharing_management' => 'Verwaltung der Optionen für das Teilen', + 'bad_opml_file' => 'Ihre OPML-Datei ist ungültig', + 'shortcuts_updated' => 'Shortcuts wurden aktualisiert', + 'shortcuts_management' => 'Verwaltung der Shortcuts', + 'shortcuts_navigation' => 'Navigation', + 'shortcuts_navigation_help' => 'Mit der "Shift" Taste gelten die Navigations-Shortcuts für Feeds.
    Mit der "Alt" Taste gelten die Navigations-Shortcuts für Kategorien.', + 'shortcuts_article_action' => 'Artikelaktionen', + 'shortcuts_other_action' => 'Andere Aktionen', + 'feeds_marked_read' => 'Die Feeds wurden als gelesen markiert', + 'updated' => 'Die Änderungen wurden aktualisiert', + + 'already_subscribed' => 'Sie haben bereits %s abonniert', + 'feed_added' => 'Der RSS Feed %s wurde hinzugefügt', + 'feed_not_added' => '%s konnte nicht hinzugefügt werden', + 'internal_problem_feed' => 'Der RSS Feed konnte nicht hinzugefügt werden. überprüfen Sie die Protokolldateien von FressRSS für weitere Informationen.', + 'invalid_url' => 'URL %s ist ungültig', + 'feed_actualized' => '%s wurde aktualisiert', + 'n_feeds_actualized' => '%d Feeds wurden aktualisiert', + 'feeds_actualized' => 'RSS Feeds wurden aktualisiert', + 'no_feed_actualized' => 'Es wurden keine RSS Feeds aktualisiert', + 'n_entries_deleted' => '%d Artikel wurden gelöscht', + 'feeds_imported_with_errors' => 'Ihre Feeds wurden importiert, es gab aber einige Fehler', + 'feeds_imported' => 'Ihre Feeds wurden importiert und werden jetzt aktualisiert', + 'category_emptied' => 'Die Kategorie wurde geleert', + 'feed_deleted' => 'Der Feed wurde gelöscht', + 'feed_validator' => '&Üuml;berprüfen Sie die Gültigkeit des Feeds', + + 'optimization_complete' => 'Die Optimierung ist beendet', + + 'your_rss_feeds' => 'Ihre RSS Feeds', + 'your_favorites' => 'Ihre Favoriten', + 'public' => 'Öffentlich', + 'invalid_login' => 'Das Login ist ungültig', + + // VIEWS + 'save' => 'Speichern', + 'delete' => 'Löschen', + 'cancel' => 'Abbrechen', + + 'back_to_rss_feeds' => '← Zurück zu den RSS Feeds gehen', + 'feeds_moved_category_deleted' => 'Wenn Sie eine Kategorie löschen, werden deren Feeds automatisch in die Kategorie %s eingefügt.', + 'category_number' => 'Kategorie n°%d', + 'ask_empty' => 'Leeren?', + 'number_feeds' => '%d Feeds', + 'can_not_be_deleted' => 'Kann nicht gelöscht werden', + 'add_category' => 'Füge eine Kategorie hinzu', + 'new_category' => 'Neue Kategorie', + + 'javascript_for_shortcuts' => 'JavaScript muss ermöglicht werden, wenn Shortcuts verwendet werden sollen', + 'javascript_should_be_activated'=> 'JavaScript muss ermöglicht werden', + 'shift_for_all_read' => '+ shift um alle Artikel als gelesen zu markieren', + 'see_on_website' => 'Auf der Originalwebseite anschauen', + 'next_article' => 'Zum nächsten Artikel springen', + 'last_article' => 'Zum letzten Artikel springen', + 'previous_article' => 'Zum vorherigen Artikel springen', + 'first_article' => 'Zum ersten Artikel springen', + 'next_page' => 'Zur nächsten Seite springen', + 'previous_page' => 'Zur vorherigen Seite springen', + 'collapse_article' => 'Zusammenfalten', + 'auto_share' => 'Teilen', + 'auto_share_help' => 'Wenn es nur eine Option zum Teilen gibt, wird die verwendet. Ansonsten werden die Optionen über die Nummer ausgewählt.', + + 'file_to_import' => 'Datei zum importieren', + 'import' => 'Import', + 'export' => 'Export', + 'or' => 'oder', + + 'informations' => 'Information', + 'damn' => 'Verdammt!', + 'feed_in_error' => 'Dieser Feed hat ein Problem verursacht. Bitte stellen Sie sicher, dass er immer lesbar ist und aktualisieren Sie ihn dann.', + 'feed_empty' => 'Dieser Feed ist leer. Bitte stellen Sie sicher, dass er noch gepflegt wird.', + 'feed_description' => 'Beschreibung', + 'website_url' => 'Webseiten-Adresse URL', + 'feed_url' => 'Feed URL', + 'articles' => 'Artikel', + 'number_articles' => 'Anzahl der Artikel', + 'by_feed' => 'per Feed', + 'by_default' => 'Als Vorgabe', + 'keep_history' => 'Kleinste Anzahl der Artikel, die behalten werden', + 'categorize' => 'In einer Kategorie speichern', + 'truncate' => 'Alle Artikel löschen', + 'advanced' => 'Erweitert', + 'show_in_all_flux' => 'Im Hauptstrom anzeigen', + 'yes' => 'Ja', + 'no' => 'Nein', + 'css_path_on_website' => 'Pfad zur CSS-Datei des Artikels auf der Original Webseite', + 'retrieve_truncated_feeds' => 'Gekürzte RSS Feeds abrufen (Achtung, benötigt mehr Zeit!)', + 'http_authentication' => 'HTTP Authentifizierung', + 'http_username' => 'HTTP Nutzername', + 'http_password' => 'HTTP Passwort', + 'blank_to_disable' => 'Zum Ausschalten frei lassen', + 'not_yet_implemented' => 'Noch nicht implementiert', + 'access_protected_feeds' => 'Die Verbindung erlaubt Zugriff zu HTTP-geschützten RSS Feeds', + 'no_selected_feed' => 'Kein Feed ausgewählt.', + 'think_to_add' => 'Sie können Feeds hinzufügen.', + + 'current_user' => 'Aktuelle Nutzung', + 'default_user' => 'Nutzername des Standardnutzers (maximal 16 Zeichen - alphanumerisch)', + 'password_form' => 'Passwort
    (für die Anmeldemethode per Webformular)', + 'persona_connection_email' => 'Login E-Mail Adresse
    (für Mozilla Persona)', + 'allow_anonymous' => 'Anonymes lesen der Artikel des Standardnutzers (%s) wird erlaubt', + 'allow_anonymous_refresh' => 'Aktualisieren der Artikel wird anonymen Nutzern erlaubt', + 'auth_token' => 'Authentifizierungs-Token', + 'explain_token' => 'Erlaube den Zugriff auf die RSS-Ausgabe des Standardnutzers ohne Authentifizierung.
    %s?output=rss&token=%s', + 'login_configuration' => 'Login', + 'is_admin' => 'ist Administrator', + 'auth_type' => 'Authentifizierungsmethode', + 'auth_none' => 'Keine (gefährlich)', + 'auth_form' => 'Webformular (traditionell, JavaScript wird benötigt)', + 'http_auth' => 'HTTP (mit HTTPS für erfahrene Nutzer)', + 'auth_persona' => 'Mozilla Persona (modern, JavaScript wird benötigt)', + 'users_list' => 'Liste der Nutzer', + 'create_user' => 'Neuen Nutzer erstellen', + 'username' => 'Nutzername', + 'password' => 'Passwort', + 'create' => 'Erstellen', + 'user_created' => 'Nutzer %s wurde erstellt', + 'user_deleted' => 'Nutzer %s wurde gelöscht', + + 'language' => 'Sprache', + 'month' => 'Monate', + 'archiving_configuration' => 'Archivieren', + 'delete_articles_every' => 'Entfernen von Artikeln nach', + 'purge_now' => 'Jetzt bereinigen', + 'purge_completed' => 'Die Bereinigung ist abgeschlossen (%d Artikel wurden gelöscht)', + 'archiving_configuration_help' => 'Es gibt weitere Optionen bei den Einstellungen der individuellen Nachrichtenströme', + 'reading_configuration' => 'Lesen', + 'articles_per_page' => 'Anzahl der Artikel pro Seite', + 'default_view' => 'Standard-Ansicht', + 'sort_order' => 'Sortierreihenfolge', + 'auto_load_more' => 'Die nächsten Artikel am Seitenende laden', + 'display_articles_unfolded' => 'Die Artikel als Standard zusammen gefaltet anzeigen', + 'after_onread' => 'Nach “als gelesen markieren”', + 'jump_next' => 'springe zum nächsten ungelesenen Geschwisterelement (Feed oder Kategorie)', + 'reading_icons' => 'Lese Symbol', + 'top_line' => 'Kopfzeile', + 'bottom_line' => 'Fusszeile', + 'img_with_lazyload' => 'Verwende die "träge laden" Methode zum laden von Bildern', + 'auto_read_when' => 'Artikel als gelesen markieren…', + 'article_selected' => 'wenn der Artikel ausgewählt ist', + 'article_open_on_website' => 'wenn der Artikel auf der Originalwebseite geöffnet ist', + 'scroll' => 'während des Seiten-Scrollens', + 'upon_reception' => 'beim Empfang des Artikels', + 'your_shaarli' => 'Ihr Shaarli', + 'your_wallabag' => 'Ihr wallabag', + 'your_diaspora_pod' => 'Ihr Diaspora* pod', + 'sharing' => 'Teilen', + 'share' => 'teile', + 'by_email' => 'Per E-Mail', + 'optimize_bdd' => 'Datenbank optimieren', + 'optimize_todo_sometimes' => 'Sollte gelegentlich gemacht werden, um die Größe der Datenbank zu reduzieren', + 'theme' => 'Thema', + 'more_information' => 'Weitere Informationen', + 'activate_sharing' => 'Teilen aktivieren', + 'shaarli' => 'Shaarli', + 'wallabag' => 'wallabag', + 'diaspora' => 'Diaspora*', + 'twitter' => 'Twitter', + 'g+' => 'Google+', + 'facebook' => 'Facebook', + 'email' => 'E-Mail', + 'print' => 'Drucken', + + 'article' => 'Artikel', + 'title' => 'Titel', + 'author' => 'Autor', + 'publication_date' => 'Datum der Veröffentlichung', + 'by' => 'von', + + 'load_more' => 'Weitere Artikel laden', + 'nothing_to_load' => 'Es gibt keine weiteren Artikel', + + 'rss_feeds_of' => 'RSS Feed von %s', + + 'refresh' => 'Aktualisieren', + 'no_feed_to_refresh' => 'Es gibt keinen Feed zum aktualisieren', + + 'today' => 'Heute', + 'yesterday' => 'Gestern', + 'before_yesterday' => 'vor Gestern', + 'new_article' => 'Es gibt neue Artikel. Bitte klicken Sie hier, um die Seite erneut zu laden.', + 'by_author' => 'Von %s', + 'related_tags' => 'Verwandte tags', + 'no_feed_to_display' => 'Es gibt keinen Artikel zum anzeigen.', + + 'about_freshrss' => 'Über FreshRSS', + 'project_website' => 'Projekt Webseite', + 'lead_developer' => 'Hauptentwickler', + 'website' => 'Webseite', + 'bugs_reports' => 'Fehlerberichte', + 'github_or_email' => 'auf Github oder per Mail', + 'license' => 'Lizenz', + 'agpl3' => 'AGPL 3', + 'freshrss_description' => 'FreshRSS ist ein RSS Feedsaggregator zum selbst hosten wie zum Beispiel Kriss Feed oder Leed. Es ist leicht und einfach zu handhaben und gleichzeitig ein leistungsstark und konfigurierbares Werkzeug.', + 'credits' => 'Credits', + 'credits_content' => 'Einige Designelemente sind von Bootstrap obwohl FreshRSS dieses Framework nicht nutzt. Icons sind vom GNOME Projekt. Open Sans Font police wurde von Steve Matteson erstellt. Favicons wurden mit getFavicon API gesammelt. FreshRSS basiert auf Minz, einem PHP Framework.', + 'version' => 'Version', + + 'logs' => 'Protokolle', + 'logs_empty' => 'Die Protokolldatei ist leer', + 'clear_logs' => 'Protokolldateien leeren', + + 'forbidden_access' => 'Der Zugriff ist verboten!', + 'login_required' => 'Das Login ist nötig:', + + 'confirm_action' => 'Sind Sie sicher, dass Sie diese Aktion durchführen wollen? Die Aktion kann nicht abgebrochen werden!', + + // DATE + 'january' => 'januar', + 'february' => 'februar', + 'march' => 'märz', + 'april' => 'april', + 'may' => 'mai', + 'june' => 'juni', + 'july' => 'juli', + 'august' => 'august', + 'september' => 'september', + 'october' => 'oktober', + 'november' => 'november', + 'december' => 'dezember', + // special format for date() function + 'Jan' => '\J\a\n\u\a\r', + 'Feb' => '\F\e\b\r\u\a\r', + 'Mar' => '\M\a\e\r\z', + 'Apr' => '\A\p\r\i\l', + 'May' => '\M\a\i', + 'Jun' => '\J\u\n\i', + 'Jul' => '\J\u\l\i', + 'Aug' => '\A\u\g\u\s\t', + 'Sep' => '\S\e\p\t\e\m\b\e\r', + 'Oct' => '\O\k\t\o\b\e\r', + 'Nov' => '\N\o\v\e\m\b\e\r', + 'Dec' => '\D\e\z\e\m\b\e\r', + // format for date() function, %s allows to indicate month in letter + 'format_date' => 'd\.\ %s Y', + 'format_date_hour' => 'd\.\ %s Y \u\m H\:i', + + 'status_favorites' => 'Favoriten', + 'status_read' => 'Gelesen', + 'status_unread' => 'Ungelesen', + 'status_total' => 'Gesamt', + + 'stats_entry_repartition' => 'Verteilung der Einträge', + 'stats_entry_per_day' => 'Einträge pro Tag (während der letzten 30 Tage)', + 'stats_feed_per_category' => 'Feeds pro Kategorie', + 'stats_entry_per_category' => 'Einträge pro Kategorie', + 'stats_top_feed' => 'Top 10 Feeds', + 'stats_entry_count' => 'Zähler für Einträge', +); From 5d718b5c3bc183441b7629377367f23f8773045a Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 3 Sep 2014 21:33:29 -0400 Subject: [PATCH 016/126] Fix collapse shortcut behavior Before, the collapse shortcut was marking all articles as read when used. No matter what configuration you use. Now, the collapse shortcut marks articles only if the appropriate configuration is used (when article is viewed). --- p/scripts/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/scripts/main.js b/p/scripts/main.js index c37f9f6f2..7bd746f75 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -373,7 +373,7 @@ function collapse_entry() { var flux_current = $(".flux.current"); flux_current.toggleClass("active"); - if (isCollapsed) { + if (isCollapsed && auto_mark_article) { mark_read(flux_current, true); } } From 657b1ffe278c2bbd1c23ae906786810ce9c609f4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 4 Sep 2014 20:30:15 +0200 Subject: [PATCH 017/126] Bug HTML stats Categories containing a space were not displayed properly https://github.com/marienfressinaud/FreshRSS/issues/547 --- app/views/stats/repartition.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 2331db78c..d9dc4c89d 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -10,12 +10,12 @@ categories as $category) { $feeds = $category->feeds(); if (!empty($feeds)) { - echo ''; + echo ''; foreach ($feeds as $feed) { if ($this->feed && $feed->id() == $this->feed->id()){ - echo ''; + echo ''; } else { - echo ''; + echo ''; } } echo ''; From 88ec538ef5a444c65d0d34603d108257538b537d Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Thu, 4 Sep 2014 23:45:40 -0400 Subject: [PATCH 018/126] Fix navigation on hidden categories and feeds Before, when navigating with the keyboard, hidden categories and feeds where shown. Now, they stay hidden. See #604 --- p/scripts/main.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/p/scripts/main.js b/p/scripts/main.js index c37f9f6f2..ee95a1b98 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -297,7 +297,7 @@ function next_entry() { function prev_feed() { var active_feed = $("#aside_flux .feeds li.active"); if (active_feed.length > 0) { - active_feed.prev().find('a.feed').each(function(){this.click();}); + active_feed.prevAll(':visible:first').find('a.feed').each(function(){this.click();}); } else { last_feed(); } @@ -306,21 +306,21 @@ function prev_feed() { function next_feed() { var active_feed = $("#aside_flux .feeds li.active"); if (active_feed.length > 0) { - active_feed.next().find('a.feed').each(function(){this.click();}); + active_feed.nextAll(':visible:first').find('a.feed').each(function(){this.click();}); } else { first_feed(); } } function first_feed() { - var feed = $("#aside_flux .feeds.active li:first"); + var feed = $("#aside_flux .feeds.active li:visible:first"); if (feed.length > 0) { feed.find('a')[1].click(); } } function last_feed() { - var feed = $("#aside_flux .feeds.active li:last"); + var feed = $("#aside_flux .feeds.active li:visible:last"); if (feed.length > 0) { feed.find('a')[1].click(); } @@ -330,7 +330,7 @@ function prev_category() { var active_cat = $("#aside_flux .category.stick.active"); if (active_cat.length > 0) { - var prev_cat = active_cat.parent('li').prev().find('.category.stick a.btn'); + var prev_cat = active_cat.parent('li').prevAll(':visible:first').find('.category.stick a.btn'); if (prev_cat.length > 0) { prev_cat[0].click(); } @@ -344,7 +344,7 @@ function next_category() { var active_cat = $("#aside_flux .category.stick.active"); if (active_cat.length > 0) { - var next_cat = active_cat.parent('li').next().find('.category.stick a.btn'); + var next_cat = active_cat.parent('li').nextAll(':visible:first').find('.category.stick a.btn'); if (next_cat.length > 0) { next_cat[0].click(); } @@ -355,14 +355,14 @@ function next_category() { } function first_category() { - var cat = $("#aside_flux .category.stick:first"); + var cat = $("#aside_flux .category.stick:visible:first"); if (cat.length > 0) { cat.find('a.btn')[0].click(); } } function last_category() { - var cat = $("#aside_flux .category.stick:last"); + var cat = $("#aside_flux .category.stick:visible:last"); if (cat.length > 0) { cat.find('a.btn')[0].click(); } From c3fd8877c021b86180b3bea4d4260e6478f0558e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 6 Sep 2014 16:04:49 +0200 Subject: [PATCH 019/126] Bug warning in case of invalid CDATA --- lib/SimplePie/SimplePie/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index 9300b4ba9..7fb7bd9be 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -142,7 +142,7 @@ class SimplePie_Parser $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; - $dom->loadXML($data); + @$dom->loadXML($data); $this->encoding = $encoding = $dom->encoding = 'UTF-8'; $data2 = $dom->saveXML(); if (function_exists('mb_convert_encoding')) From 83832a39c4651bd3e4bfa007f75c91d040db79d3 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sat, 6 Sep 2014 11:32:52 -0400 Subject: [PATCH 020/126] Add a new configuration option Before, when you selected the option to display only unread articles, it shows read articles if no unread article were found. Now, this option is renamed to include information on fallback behavior and a new option is created to have the "only unread" behavior See #551 It is missing the german translation --- app/Models/Configuration.php | 13 ++++++++++++- app/Models/Entry.php | 2 ++ app/Models/EntryDAO.php | 3 +++ app/i18n/en.php | 2 ++ app/i18n/fr.php | 2 ++ app/views/configure/reading.phtml | 19 +++++++++++-------- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 3a408faa5..92bab6ad9 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -141,7 +141,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; + endswitch; } 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/i18n/en.php b/app/i18n/en.php index bcd7a3da5..ea4e3d02a 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -92,6 +92,7 @@ return array ( 'rss_view' => 'RSS feed', 'show_all_articles' => 'Show all articles', 'show_not_reads' => 'Show only unread', + 'show_not_reads_with_fallback' => 'Show only unread or read if no unread', 'show_read' => 'Show only read', 'show_favorite' => 'Show only favorites', 'show_not_favorite' => 'Show all but favorites', @@ -267,6 +268,7 @@ return array ( 'display_configuration' => 'Display', 'articles_per_page' => 'Number of articles per page', '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', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 395f2b5d2..c23e291ab 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -92,6 +92,7 @@ return array ( 'rss_view' => 'Flux RSS', 'show_all_articles' => 'Afficher tous les articles', 'show_not_reads' => 'Afficher les non lus', + 'show_not_reads_with_fallback' => 'Afficher les non lus ou les lus si aucun non lus', 'show_read' => 'Afficher les lus', 'show_favorite' => 'Afficher les favoris', 'show_not_favorite' => 'Afficher tout sauf les favoris', @@ -267,6 +268,7 @@ return array ( 'display_configuration' => 'Affichage', 'articles_per_page' => 'Nombre d’articles par page', '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', diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index 5a26501a4..df3b5277e 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -31,14 +31,17 @@ - - +
    +
    + +
    + +
    +
    From 48f91da2e5d7da4d8a4f11987116bfc8cbf049ca Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 8 Sep 2014 19:52:21 +0200 Subject: [PATCH 021/126] Check FRESHRSS_PATH is writable. FRESHRSS_PATH needs to be writable before performing update. --- app/Controllers/updateController.php | 41 ++++++++++------------------ app/i18n/en.php | 2 ++ app/i18n/fr.php | 2 ++ 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 857d975b2..5d5ec3586 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -15,15 +15,19 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function indexAction() { - if (file_exists(UPDATE_FILENAME)) { + if (file_exists(UPDATE_FILENAME) && !is_writable(FRESHRSS_PATH)) { + $this->view->message = array( + 'status' => 'bad', + 'title' => _t('damn'), + 'body' => _t('file_is_nok', FRESHRSS_PATH) + ); + } elseif (file_exists(UPDATE_FILENAME)) { // There is an update file to apply! $this->view->message = array( 'status' => 'good', 'title' => _t('ok'), 'body' => _t('update_can_apply', _url('update', 'apply')) ); - - return; } } @@ -33,11 +37,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { if (file_exists(UPDATE_FILENAME)) { // There is already an update file to apply: we don't need to check // the webserver! - $this->view->message = array( - 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) - ); + Minz_Request::forward(array('c' => 'update')); return; } @@ -73,11 +73,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { $script = $res_array[1]; if (file_put_contents(UPDATE_FILENAME, $script) !== false) { - $this->view->message = array( - 'status' => 'good', - 'title' => _t('ok'), - 'body' => _t('update_can_apply', _url('update', 'apply')) - ); + Minz_Request::forward(array('c' => 'update')); } else { $this->view->message = array( 'status' => 'bad', @@ -88,7 +84,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function applyAction() { - if (!file_exists(UPDATE_FILENAME)) { + if (!file_exists(UPDATE_FILENAME) || !is_writable(FRESHRSS_PATH)) { Minz_Request::forward(array('c' => 'update'), true); } @@ -104,21 +100,12 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); - // TODO: record last update + // TODO: record last update_finished - Minz_Session::_param('notification', array( - 'type' => 'good', - 'content' => Minz_Translate::t('update_finished') - )); - - Minz_Request::forward(array(), true); + Minz_Request::good(_t('update_finished')); } else { - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('update_problem', $res) - )); - - Minz_Request::forward(array('c' => 'update'), true); + Minz_Request::bad(_t('update_problem', $res), + array('c' => 'update', 'a' => 'index')); } } } diff --git a/app/i18n/en.php b/app/i18n/en.php index 95356af2c..c5911cde7 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -152,6 +152,8 @@ return array ( 'public' => 'Public', 'invalid_login' => 'Login is invalid', + 'file_is_nok' => 'Check permissions on %s directory. HTTP server must have rights to write into.', + // VIEWS 'save' => 'Save', 'delete' => 'Delete', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 8437e872e..789a0bb98 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -152,6 +152,8 @@ return array ( 'public' => 'Public', 'invalid_login' => 'L’identifiant est invalide !', + 'file_is_nok' => 'Veuillez vérifier les droits sur le répertoire %s. Le serveur HTTP doit être capable d’écrire dedans.', + // VIEWS 'save' => 'Enregistrer', 'delete' => 'Supprimer', From d59eebf5423afb94ff68550aa9218674889ab4ad Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 8 Sep 2014 20:07:09 +0200 Subject: [PATCH 022/126] Add data/last_update.txt Remember last update timestamp. --- app/Controllers/updateController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 5d5ec3586..5424792f4 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -11,7 +11,11 @@ class FreshRSS_update_Controller extends Minz_ActionController { } Minz_View::prependTitle(_t('update_system') . ' · '); - $this->view->last_update_time = 'unknown'; // TODO + $this->view->last_update_time = 'unknown'; + $timestamp = (int)@file_get_contents(DATA_PATH . '/last_update.txt'); + if (is_numeric($timestamp) && $timestamp > 0) { + $this->view->last_update_time = timestamptodate($timestamp); + } } public function indexAction() { @@ -99,8 +103,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { if ($res === true) { @unlink(UPDATE_FILENAME); - - // TODO: record last update_finished + @file_put_contents(DATA_PATH . '/last_update.txt', time()); Minz_Request::good(_t('update_finished')); } else { From 0dba48caef537d52beba347bf6e889196e1b9828 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 8 Sep 2014 20:15:35 +0200 Subject: [PATCH 023/126] CHANGELOG Add new update system. See https://github.com/marienfressinaud/FreshRSS/issues/411 --- CHANGELOG | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index df43fe1d1..5f7d79e7e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,12 @@ # Journal des modifications -## 2014-08-xx FreshRSS 0.7.4 +## 2014-09-xx FreshRSS 0.8.0 + +* Features + * New automatic update system + + +## 2014-08-24 FreshRSS 0.7.4 * UI * Hide categories/feeds with unread articles when showing only unread articles From 4989302447b23d76429ad98d93900e3e1350bb06 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Mon, 8 Sep 2014 20:19:40 +0200 Subject: [PATCH 024/126] Add last_update.txt in .gitignore See https://github.com/marienfressinaud/FreshRSS/issues/411 --- data/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/data/.gitignore b/data/.gitignore index afb16d9aa..b1cc0121a 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -6,3 +6,4 @@ touch.txt no-cache.txt *.bak.php *.lock.txt +last_update.txt From 40d176cc247f03a3b95fcf0389de8858e577b7cb Mon Sep 17 00:00:00 2001 From: plopoyop Date: Wed, 10 Sep 2014 09:14:36 +0200 Subject: [PATCH 025/126] add css class or id to elements without --- app/layout/aside_flux.phtml | 2 +- app/layout/nav_menu.phtml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 357aa1cd3..9688a3273 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -4,7 +4,7 @@
      loginOk) { ?>
    • -
      +
      diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 25833c16d..b177cf7c8 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -7,7 +7,7 @@ loginOk) { ?> -
      +
      From 4211539c24e36531f2c5440ad454c4e208131047 Mon Sep 17 00:00:00 2001 From: plopoyop Date: Thu, 18 Sep 2014 10:46:34 +0200 Subject: [PATCH 066/126] Add timeout option for HTML5 notification --- app/Controllers/configureController.php | 1 + app/Models/Configuration.php | 7 +++++++ app/i18n/de.php | 2 ++ app/i18n/en.php | 2 ++ app/i18n/fr.php | 2 ++ app/views/configure/display.phtml | 7 +++++++ app/views/helpers/javascript_vars.phtml | 2 ++ p/scripts/main.js | 6 ++++++ 8 files changed, 29 insertions(+) diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index bb96bfae3..b1cd45014 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -157,6 +157,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_bottomline_tags(Minz_Request::param('bottomline_tags', false)); $this->view->conf->_bottomline_date(Minz_Request::param('bottomline_date', false)); $this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false)); + $this->view->conf->_html5_notif_timeout(Minz_Request::param('html5_notif_timeout', 0)); $this->view->conf->save(); Minz_Session::_param('language', $this->view->conf->language); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index e4408df73..830d39f0d 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -60,6 +60,7 @@ class FreshRSS_Configuration { 'bottomline_link' => true, 'sharing' => array(), 'queries' => array(), + 'html5_notif_timeout' => 0, ); private $available_languages = array( @@ -269,6 +270,12 @@ class FreshRSS_Configuration { $this->data['content_width'] = 'thin'; } } + + public function _html5_notif_timeout ($value) { + $value = intval($value); + $this->data['html5_notif_timeout'] = $value >= 0 ? $value : 0; + } + public function _token($value) { $this->data['token'] = $value; } diff --git a/app/i18n/de.php b/app/i18n/de.php index 4301a8b6d..3f7d8ca27 100644 --- a/app/i18n/de.php +++ b/app/i18n/de.php @@ -212,6 +212,8 @@ return array ( 'reading_icons' => 'Lese Symbol', 'top_line' => 'Kopfzeile', 'bottom_line' => 'Fusszeile', + 'html5_notif_timeout' => 'HTML5 notification timeout', + 'seconds_(0_means_no_timeout)' => 'seconds (0 means no timeout)', 'img_with_lazyload' => 'Verwende die "träge laden" Methode zum laden von Bildern', 'auto_read_when' => 'Artikel als gelesen markieren…', 'article_selected' => 'wenn der Artikel ausgewählt ist', diff --git a/app/i18n/en.php b/app/i18n/en.php index 8f39115ad..1e842a1e1 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -280,6 +280,8 @@ return array ( 'article_icons' => 'Article icons', 'top_line' => 'Top line', 'bottom_line' => 'Bottom line', + 'html5_notif_timeout' => 'HTML5 notification timeout', + 'seconds_(0_means_no_timeout)' => 'seconds (0 means no timeout)', 'img_with_lazyload' => 'Use "lazy load" mode to load pictures', 'sticky_post' => 'Stick the article to the top when opened', 'reading_confirm' => 'Display a confirmation dialog on “mark all as read” actions', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 48b4c1732..bbfa71f66 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -280,6 +280,8 @@ return array ( 'article_icons' => 'Icônes d’article', 'top_line' => 'Ligne du haut', 'bottom_line' => 'Ligne du bas', + 'html5_notif_timeout' => 'Temps d\'affichage de la notification HTML5', + 'seconds_(0_means_no_timeout)' => 'secondes (0 signifie aucun timeout ) ', 'img_with_lazyload' => 'Utiliser le mode “chargement différé” pour les images', 'sticky_post' => 'Aligner l’article en haut quand il est ouvert', 'reading_confirm' => 'Afficher une confirmation lors des actions “marquer tout comme lu”', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 955fc6747..8eb3a156b 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -91,6 +91,13 @@
      + +
      + +
      + +
      +
      diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 2144f1576..bf0ffdb76 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -54,6 +54,8 @@ echo 'authType="', $authType, '",', echo 'str_confirmation="', Minz_Translate::t('confirm_action'), '"', ",\n"; echo 'str_notif_title_articles="', Minz_Translate::t('notif_title_new_articles'), '"', ",\n"; echo 'str_notif_body_articles="', Minz_Translate::t('notif_body_new_articles'), '"', ",\n"; +echo 'html5_notif_timeout=', $this->conf->html5_notif_timeout,",\n"; + $autoActualise = Minz_Session::param('actualize_feeds', false); echo 'auto_actualize_feeds=', $autoActualise ? 'true' : 'false', ";\n"; diff --git a/p/scripts/main.js b/p/scripts/main.js index fd49d62ba..c568bac35 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -866,6 +866,12 @@ function notifs_html5_show(nb) { notification.onclick = function() { window.location.reload(); } + + if (html5_notif_timeout !== 0){ + setTimeout(function() { + notification.close(); + }, html5_notif_timeout * 1000); + } } function init_notifs_html5() { From 0c012b006c868db294b1c227f82fb5861075001f Mon Sep 17 00:00:00 2001 From: plopoyop Date: Thu, 18 Sep 2014 12:39:44 +0200 Subject: [PATCH 067/126] Alignement des => --- app/i18n/de.php | 2 +- app/i18n/en.php | 2 +- app/i18n/fr.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/i18n/de.php b/app/i18n/de.php index 3f7d8ca27..3dc1536de 100644 --- a/app/i18n/de.php +++ b/app/i18n/de.php @@ -212,7 +212,7 @@ return array ( 'reading_icons' => 'Lese Symbol', 'top_line' => 'Kopfzeile', 'bottom_line' => 'Fusszeile', - 'html5_notif_timeout' => 'HTML5 notification timeout', + 'html5_notif_timeout' => 'HTML5 notification timeout', 'seconds_(0_means_no_timeout)' => 'seconds (0 means no timeout)', 'img_with_lazyload' => 'Verwende die "träge laden" Methode zum laden von Bildern', 'auto_read_when' => 'Artikel als gelesen markieren…', diff --git a/app/i18n/en.php b/app/i18n/en.php index 1e842a1e1..ca31c4bfc 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -280,7 +280,7 @@ return array ( 'article_icons' => 'Article icons', 'top_line' => 'Top line', 'bottom_line' => 'Bottom line', - 'html5_notif_timeout' => 'HTML5 notification timeout', + 'html5_notif_timeout' => 'HTML5 notification timeout', 'seconds_(0_means_no_timeout)' => 'seconds (0 means no timeout)', 'img_with_lazyload' => 'Use "lazy load" mode to load pictures', 'sticky_post' => 'Stick the article to the top when opened', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index bbfa71f66..d6c0118e7 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -280,7 +280,7 @@ return array ( 'article_icons' => 'Icônes d’article', 'top_line' => 'Ligne du haut', 'bottom_line' => 'Ligne du bas', - 'html5_notif_timeout' => 'Temps d\'affichage de la notification HTML5', + 'html5_notif_timeout' => 'Temps d\'affichage de la notification HTML5', 'seconds_(0_means_no_timeout)' => 'secondes (0 signifie aucun timeout ) ', 'img_with_lazyload' => 'Utiliser le mode “chargement différé” pour les images', 'sticky_post' => 'Aligner l’article en haut quand il est ouvert', From 23609ad858552c54fc3fe8d9f8e2f7d966fd28a1 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 14:18:00 +0200 Subject: [PATCH 068/126] Add page for reset auth type [NOT WORKING] See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/Controllers/indexController.php | 77 +++++++++++++++++++++++++++++ app/views/index/resetAuth.phtml | 33 +++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 app/views/index/resetAuth.phtml diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 1d74a570b..fccf16ecf 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -420,4 +420,81 @@ 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('reset_auth') . ' · '); + + $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; + } + + 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_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('invalid_login') + )); + return; + } + + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + + try { + $s = $conf->passwordHash; + $ok = password_verify($nonce . $s, $c); + if (!$ok) { + Minz_Log::debug('Password mismatch for user ' . $username . + ', nonce=' . $nonce . ', c=' . $c); + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => Minz_Translate::t('invalid_login') + )); + return; + } + + Minz_Configuration::_authType('form'); + $ok = Minz_Configuration::writeFile(); + + if ($ok) { + Minz_Request::good(_t('auth_form_set')); + } else { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => _t('auth_form_not_set') + )); + } + } catch (Minz_Exception $e) { + Minz_Log::warning('Login failure: ' . $e->getMessage()); + } + } + } } diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml new file mode 100644 index 000000000..7f3b54bdb --- /dev/null +++ b/app/views/index/resetAuth.phtml @@ -0,0 +1,33 @@ +
      +

      + + message)) { ?> +

      + message['title']; ?> + message['body']; ?> +

      + + + no_form) { ?> +
      +

      + + +

      + +
      + + +
      +
      + + +
      + +
      +
      + +
      +
      + +
      From f727a1383639d5bdc762f73dfe93b9a5d577cb41 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 14:50:54 +0200 Subject: [PATCH 069/126] Improve reset auth system - Add bcrypt.js in resetAuthAction() - Rename init_loginForm() in init_crypto_form() - Load init_crypto_form() everytime (if no #crypto-form, do nothing) --- app/Controllers/indexController.php | 3 +++ app/views/index/formLogin.phtml | 2 +- app/views/index/resetAuth.phtml | 2 +- p/scripts/main.js | 27 +++++++++++++++------------ 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index fccf16ecf..86863cc84 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -423,6 +423,9 @@ class FreshRSS_index_Controller extends Minz_ActionController { public function resetAuthAction() { Minz_View::prependTitle(_t('reset_auth') . ' · '); + 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! diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index b79c1b614..34f10de4a 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -3,7 +3,7 @@ switch (Minz_Configuration::authType()) { case 'form': - ?>
      + ?>
      diff --git a/app/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml index 7f3b54bdb..78cc527b3 100644 --- a/app/views/index/resetAuth.phtml +++ b/app/views/index/resetAuth.phtml @@ -9,7 +9,7 @@ no_form) { ?> - +

      diff --git a/p/scripts/main.js b/p/scripts/main.js index fd49d62ba..04151c30d 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -977,7 +977,7 @@ function init_load_more(box) { } // -// +// function poormanSalt() { //If crypto.getRandomValues is not available var text = '$2a$04$', base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; @@ -987,20 +987,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(), @@ -1027,11 +1031,12 @@ function init_loginForm() { }).fail(function() { alert('Communication error!'); }); - $('#loginButton').removeAttr('disabled'); + + $submit_button.removeAttr('disabled'); return success; }); } -// +// // function init_persona() { @@ -1233,14 +1238,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(); From c0d0279b03c4cd9f3b529da0b24db58cfb3520c1 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 14:57:34 +0200 Subject: [PATCH 070/126] Use openNotification instead of alert (main.js) --- p/scripts/main.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/p/scripts/main.js b/p/scripts/main.js index 04151c30d..01bd0c734 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -1012,7 +1012,7 @@ function init_crypto_form() { 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'), @@ -1020,16 +1020,16 @@ function init_crypto_form() { 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'); }); $submit_button.removeAttr('disabled'); From d2799d168e0d885cb6de24cf012e2a909215fcd8 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 15:09:47 +0200 Subject: [PATCH 071/126] Improve resetAuth redirections See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/Controllers/indexController.php | 39 +++++++++++------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 86863cc84..26e2618f0 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -451,6 +451,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { return; } + invalidateHttpCache(); + if (Minz_Request::isPost()) { $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); @@ -460,43 +462,32 @@ class FreshRSS_index_Controller extends Minz_ActionController { ' user=' . $username . ' challenge=' . $c . ' nonce=' . $nonce); - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('invalid_login') - )); - return; + Minz_Request::bad(_t('invalid_login'), + array('c' => 'index', 'a' => 'resetAuth')); } if (!function_exists('password_verify')) { include_once(LIB_PATH . '/password_compat.php'); } - try { - $s = $conf->passwordHash; - $ok = password_verify($nonce . $s, $c); - if (!$ok) { - Minz_Log::debug('Password mismatch for user ' . $username . - ', nonce=' . $nonce . ', c=' . $c); - Minz_Session::_param('notification', array( - 'type' => 'bad', - 'content' => Minz_Translate::t('invalid_login') - )); - return; - } - + $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_Session::_param('notification', array( - 'type' => 'bad', - 'content' => _t('auth_form_not_set') - )); + Minz_Request::bad(_t('auth_form_not_set'), + array('c' => 'index', 'a' => 'resetAuth')); } - } catch (Minz_Exception $e) { - Minz_Log::warning('Login failure: ' . $e->getMessage()); + } else { + Minz_Log::debug('Password mismatch for user ' . $username . + ', nonce=' . $nonce . ', c=' . $c); + + Minz_Request::bad(_t('invalid_login'), + array('c' => 'index', 'a' => 'resetAuth')); } } } From bafe851f9073faedc52b4de1ff4fbe5a680dab2b Mon Sep 17 00:00:00 2001 From: plopoyop Date: Thu, 18 Sep 2014 15:29:16 +0200 Subject: [PATCH 072/126] Ajout du tag sur la notification HTML5 --- p/scripts/main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p/scripts/main.js b/p/scripts/main.js index c568bac35..9d2d83a32 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -860,7 +860,8 @@ function notifs_html5_show(nb) { var notification = new window.Notification(str_notif_title_articles, { icon: "../themes/icons/favicon-256.png", - body: str_notif_body_articles.replace("\d", nb) + body: str_notif_body_articles.replace("\d", nb), + tag: "freshRssNewArticles" }); notification.onclick = function() { From 94915f1c0a477c240e6072b6ccc2f71152181510 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 15:32:59 +0200 Subject: [PATCH 073/126] Reset auth system: i18n and style See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/Controllers/indexController.php | 2 +- app/i18n/en.php | 10 ++++++++++ app/i18n/fr.php | 10 ++++++++++ app/views/index/resetAuth.phtml | 12 ++++++------ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 26e2618f0..b69c09127 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -422,7 +422,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function resetAuthAction() { - Minz_View::prependTitle(_t('reset_auth') . ' · '); + Minz_View::prependTitle(_t('auth_reset') . ' · '); Minz_View::appendScript(Minz_Url::display( '/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js') )); diff --git a/app/i18n/en.php b/app/i18n/en.php index b14b36b32..11edc603f 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -159,6 +159,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 %s.', @@ -204,6 +205,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', @@ -255,6 +257,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', @@ -434,4 +437,11 @@ return array ( 'no_update' => 'No update to apply', '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 3dc429bbb..279cb89fa 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -159,6 +159,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 %s.', @@ -204,6 +205,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', @@ -255,6 +257,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éé.', @@ -434,4 +437,11 @@ return array ( '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/views/index/resetAuth.phtml b/app/views/index/resetAuth.phtml index 78cc527b3..6d4282c14 100644 --- a/app/views/index/resetAuth.phtml +++ b/app/views/index/resetAuth.phtml @@ -1,9 +1,9 @@ -

      -

      +
      +

      message)) { ?>

      - message['title']; ?> + message['title']; ?>
      message['body']; ?>

      @@ -11,12 +11,12 @@ no_form) { ?>

      - +

      - +
      @@ -26,7 +26,7 @@
      - +
      From 70df99c1fcd88b30a6a9d71954af1e31dfade489 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 15:40:23 +0200 Subject: [PATCH 074/126] Add a link on Persona page to reset auth system See https://github.com/marienfressinaud/FreshRSS/issues/521 --- app/i18n/en.php | 1 + app/i18n/fr.php | 1 + app/views/index/formLogin.phtml | 11 +++++++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/i18n/en.php b/app/i18n/en.php index 11edc603f..a012783a3 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 (1 month)', 'login_with_persona' => 'Login with Persona', + 'login_persona_problem' => 'Problem of connection with Persona?', 'logout' => 'Logout', 'search' => 'Search words or #tags', 'search_short' => 'Search', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 279cb89fa..8ea81c906 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -5,6 +5,7 @@ return array ( 'login' => 'Connexion', 'keep_logged_in' => 'Rester connecté (1 mois)', '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', diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml index 34f10de4a..b05cdced4 100644 --- a/app/views/index/formLogin.phtml +++ b/app/views/index/formLogin.phtml @@ -29,8 +29,15 @@ case 'persona': ?>

      - - +

      + + + + +

      From 9a9d7a5a315eb34bb83442c2967fe271d500db89 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 16:22:36 +0200 Subject: [PATCH 075/126] Fix alignment of tables --- p/themes/Dark/dark.css | 1 - p/themes/Flat/flat.css | 1 - p/themes/Origine/origine.css | 1 - p/themes/Screwdriver/screwdriver.css | 1 - p/themes/base-theme/template.css | 9 +++++++++ 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/p/themes/Dark/dark.css b/p/themes/Dark/dark.css index 669f4ce42..a5c499643 100644 --- a/p/themes/Dark/dark.css +++ b/p/themes/Dark/dark.css @@ -872,7 +872,6 @@ a.btn { .stat > table td, .stat > table th { border-bottom: 1px solid #333; - text-align: center; } /*=== LOGS */ diff --git a/p/themes/Flat/flat.css b/p/themes/Flat/flat.css index a942df0e8..e1cf35954 100644 --- a/p/themes/Flat/flat.css +++ b/p/themes/Flat/flat.css @@ -859,7 +859,6 @@ a.btn { .stat > table td, .stat > table th { border-bottom: 1px solid #ddd; - text-align: center; } /*=== LOGS */ diff --git a/p/themes/Origine/origine.css b/p/themes/Origine/origine.css index 2f7baee7d..4195bc260 100644 --- a/p/themes/Origine/origine.css +++ b/p/themes/Origine/origine.css @@ -913,7 +913,6 @@ a.btn { .stat > table td, .stat > table th { border-bottom: 1px solid #ddd; - text-align: center; } /*=== LOGS */ diff --git a/p/themes/Screwdriver/screwdriver.css b/p/themes/Screwdriver/screwdriver.css index 1d84753c7..5475d2fd9 100644 --- a/p/themes/Screwdriver/screwdriver.css +++ b/p/themes/Screwdriver/screwdriver.css @@ -1025,7 +1025,6 @@ opacity: 1; border-bottom: 1px solid #ccc; background: rgba(255,255,255,0.38); box-shadow: 0 1px #fff; - text-align: center; } /*=== LOGS */ diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css index 2a9deb0ea..733dd6e02 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 */ From af3739a45c2f48d8578a40619078a6fe0030ed98 Mon Sep 17 00:00:00 2001 From: plopoyop Date: Thu, 18 Sep 2014 17:21:11 +0200 Subject: [PATCH 076/126] Add Pafat theme --- p/themes/Pafat/README.md | 4 + p/themes/Pafat/icons/add.svg | 30 + p/themes/Pafat/icons/all.svg | 32 + p/themes/Pafat/icons/bookmark.svg | 5 + p/themes/Pafat/icons/category.svg | 31 + p/themes/Pafat/icons/close.svg | 28 + p/themes/Pafat/icons/configure.svg | 31 + p/themes/Pafat/icons/down-white.svg | 31 + p/themes/Pafat/icons/down.svg | 31 + p/themes/Pafat/icons/icon.svg | 12 + p/themes/Pafat/icons/key.svg | 7 + p/themes/Pafat/icons/link.svg | 7 + p/themes/Pafat/icons/next.svg | 31 + p/themes/Pafat/icons/non-starred.svg | 5 + p/themes/Pafat/icons/prev.svg | 31 + p/themes/Pafat/icons/read.svg | 5 + p/themes/Pafat/icons/refresh.svg | 31 + p/themes/Pafat/icons/rss.svg | 6 + p/themes/Pafat/icons/search.svg | 32 + p/themes/Pafat/icons/share.svg | 8 + p/themes/Pafat/icons/starred.svg | 5 + p/themes/Pafat/icons/tag.svg | 5 + p/themes/Pafat/icons/unread.svg | 6 + p/themes/Pafat/icons/up-white.svg | 31 + p/themes/Pafat/icons/up.svg | 31 + p/themes/Pafat/icons/view-global.svg | 1 + p/themes/Pafat/icons/view-normal.svg | 1 + p/themes/Pafat/icons/view-reader.svg | 1 + p/themes/Pafat/loader.gif | Bin 0 -> 2608 bytes p/themes/Pafat/metadata.json | 7 + p/themes/Pafat/pafat.css | 1066 ++++++++++++++++++++++++++ p/themes/Pafat/template.css | 695 +++++++++++++++++ 32 files changed, 2247 insertions(+) create mode 100644 p/themes/Pafat/README.md create mode 100755 p/themes/Pafat/icons/add.svg create mode 100755 p/themes/Pafat/icons/all.svg create mode 100755 p/themes/Pafat/icons/bookmark.svg create mode 100755 p/themes/Pafat/icons/category.svg create mode 100755 p/themes/Pafat/icons/close.svg create mode 100755 p/themes/Pafat/icons/configure.svg create mode 100755 p/themes/Pafat/icons/down-white.svg create mode 100755 p/themes/Pafat/icons/down.svg create mode 100755 p/themes/Pafat/icons/icon.svg create mode 100755 p/themes/Pafat/icons/key.svg create mode 100755 p/themes/Pafat/icons/link.svg create mode 100755 p/themes/Pafat/icons/next.svg create mode 100755 p/themes/Pafat/icons/non-starred.svg create mode 100755 p/themes/Pafat/icons/prev.svg create mode 100755 p/themes/Pafat/icons/read.svg create mode 100755 p/themes/Pafat/icons/refresh.svg create mode 100755 p/themes/Pafat/icons/rss.svg create mode 100755 p/themes/Pafat/icons/search.svg create mode 100755 p/themes/Pafat/icons/share.svg create mode 100755 p/themes/Pafat/icons/starred.svg create mode 100755 p/themes/Pafat/icons/tag.svg create mode 100755 p/themes/Pafat/icons/unread.svg create mode 100755 p/themes/Pafat/icons/up-white.svg create mode 100755 p/themes/Pafat/icons/up.svg create mode 100755 p/themes/Pafat/icons/view-global.svg create mode 100755 p/themes/Pafat/icons/view-normal.svg create mode 100755 p/themes/Pafat/icons/view-reader.svg create mode 100644 p/themes/Pafat/loader.gif create mode 100755 p/themes/Pafat/metadata.json create mode 100755 p/themes/Pafat/pafat.css create mode 100755 p/themes/Pafat/template.css diff --git a/p/themes/Pafat/README.md b/p/themes/Pafat/README.md new file mode 100644 index 000000000..8fe037c5a --- /dev/null +++ b/p/themes/Pafat/README.md @@ -0,0 +1,4 @@ +Pafat +===== + +Thème Pafat pour FreshRss diff --git a/p/themes/Pafat/icons/add.svg b/p/themes/Pafat/icons/add.svg new file mode 100755 index 000000000..446b10bab --- /dev/null +++ b/p/themes/Pafat/icons/add.svg @@ -0,0 +1,30 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/all.svg b/p/themes/Pafat/icons/all.svg new file mode 100755 index 000000000..eff8263c2 --- /dev/null +++ b/p/themes/Pafat/icons/all.svg @@ -0,0 +1,32 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/bookmark.svg b/p/themes/Pafat/icons/bookmark.svg new file mode 100755 index 000000000..70d0c81fb --- /dev/null +++ b/p/themes/Pafat/icons/bookmark.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/category.svg b/p/themes/Pafat/icons/category.svg new file mode 100755 index 000000000..37ee46c0a --- /dev/null +++ b/p/themes/Pafat/icons/category.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/close.svg b/p/themes/Pafat/icons/close.svg new file mode 100755 index 000000000..629fda7ff --- /dev/null +++ b/p/themes/Pafat/icons/close.svg @@ -0,0 +1,28 @@ + + + + + Gnome Symbolic Icon Theme + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/configure.svg b/p/themes/Pafat/icons/configure.svg new file mode 100755 index 000000000..52df8bca7 --- /dev/null +++ b/p/themes/Pafat/icons/configure.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/down-white.svg b/p/themes/Pafat/icons/down-white.svg new file mode 100755 index 000000000..1082c07a1 --- /dev/null +++ b/p/themes/Pafat/icons/down-white.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/down.svg b/p/themes/Pafat/icons/down.svg new file mode 100755 index 000000000..2c2a8de07 --- /dev/null +++ b/p/themes/Pafat/icons/down.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/icon.svg b/p/themes/Pafat/icons/icon.svg new file mode 100755 index 000000000..b489e18da --- /dev/null +++ b/p/themes/Pafat/icons/icon.svg @@ -0,0 +1,12 @@ + + Logo FreshRSS + + + + + + + + + + diff --git a/p/themes/Pafat/icons/key.svg b/p/themes/Pafat/icons/key.svg new file mode 100755 index 000000000..fe7811bd4 --- /dev/null +++ b/p/themes/Pafat/icons/key.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/p/themes/Pafat/icons/link.svg b/p/themes/Pafat/icons/link.svg new file mode 100755 index 000000000..cc30f9f8b --- /dev/null +++ b/p/themes/Pafat/icons/link.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/next.svg b/p/themes/Pafat/icons/next.svg new file mode 100755 index 000000000..9a3182565 --- /dev/null +++ b/p/themes/Pafat/icons/next.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/non-starred.svg b/p/themes/Pafat/icons/non-starred.svg new file mode 100755 index 000000000..948963220 --- /dev/null +++ b/p/themes/Pafat/icons/non-starred.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/prev.svg b/p/themes/Pafat/icons/prev.svg new file mode 100755 index 000000000..eb8396aaa --- /dev/null +++ b/p/themes/Pafat/icons/prev.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/read.svg b/p/themes/Pafat/icons/read.svg new file mode 100755 index 000000000..d9382bf66 --- /dev/null +++ b/p/themes/Pafat/icons/read.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/refresh.svg b/p/themes/Pafat/icons/refresh.svg new file mode 100755 index 000000000..11728fc5d --- /dev/null +++ b/p/themes/Pafat/icons/refresh.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/rss.svg b/p/themes/Pafat/icons/rss.svg new file mode 100755 index 000000000..2a8713be3 --- /dev/null +++ b/p/themes/Pafat/icons/rss.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/search.svg b/p/themes/Pafat/icons/search.svg new file mode 100755 index 000000000..acfb364cc --- /dev/null +++ b/p/themes/Pafat/icons/search.svg @@ -0,0 +1,32 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/share.svg b/p/themes/Pafat/icons/share.svg new file mode 100755 index 000000000..5010082cb --- /dev/null +++ b/p/themes/Pafat/icons/share.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/starred.svg b/p/themes/Pafat/icons/starred.svg new file mode 100755 index 000000000..d62c52134 --- /dev/null +++ b/p/themes/Pafat/icons/starred.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/tag.svg b/p/themes/Pafat/icons/tag.svg new file mode 100755 index 000000000..9908d4665 --- /dev/null +++ b/p/themes/Pafat/icons/tag.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/unread.svg b/p/themes/Pafat/icons/unread.svg new file mode 100755 index 000000000..7ca55de89 --- /dev/null +++ b/p/themes/Pafat/icons/unread.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/p/themes/Pafat/icons/up-white.svg b/p/themes/Pafat/icons/up-white.svg new file mode 100755 index 000000000..45276c749 --- /dev/null +++ b/p/themes/Pafat/icons/up-white.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/up.svg b/p/themes/Pafat/icons/up.svg new file mode 100755 index 000000000..3b01e6ac2 --- /dev/null +++ b/p/themes/Pafat/icons/up.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Pafat/icons/view-global.svg b/p/themes/Pafat/icons/view-global.svg new file mode 100755 index 000000000..d81e79790 --- /dev/null +++ b/p/themes/Pafat/icons/view-global.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/p/themes/Pafat/icons/view-normal.svg b/p/themes/Pafat/icons/view-normal.svg new file mode 100755 index 000000000..c35b101df --- /dev/null +++ b/p/themes/Pafat/icons/view-normal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/p/themes/Pafat/icons/view-reader.svg b/p/themes/Pafat/icons/view-reader.svg new file mode 100755 index 000000000..3243aed6e --- /dev/null +++ b/p/themes/Pafat/icons/view-reader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/p/themes/Pafat/loader.gif b/p/themes/Pafat/loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..a3d327a185cc86b6584fd813af4c01f54be5a69e GIT binary patch literal 2608 zcmdVcdr(tX9tZGC9yiG~=H_*Q-0%n(lu>!CRf^T^KyG+t1%gl;Hrf&{2?v*Ha|aqz2nA&b#iugPLrzJ@xJDxPx6OH?v)=qdA|9gKDX%A zt5-)$j%DoKZ#LgwSXkJTsaL7ffM0ESEG~-*iHpU8r6KEl{fQ3tgFOC$V(ChSPMIXw ztI??iiWEiW2fGzXzM5UC-GIjei3ZtstzEj(5&{4&2I;loTsoCZ)F&E+Zru&K%phU& zyFtpUw!pVz8*}wOsfO*vt2S@A{$mSvr}jIpyqwTzA!hBizq zHDRzeTzhWdtTsYhY6h5E$u|R_#X7D8$-`FY-e{9XM zu-CM{Nuvm8M{gV*TJ?Uca5_atzRL`W@7r8-Dbkp?kH=9Ly*N~}IyfE`ROM3YBLel7 zwzR@hqa*+)wVx{(j6&c*KUU{U6{(Q@A?evDjqvkEol*gv^pg?rLs8(CFQ6NaXI)oO zi@wzw{gWTiOrquLPL6W#qpKF{UsZqyJqFsDDSyqwgkXjoKoEhOI5Ei%l)`3ZR#Z5< zizqo7ou;;yA=)NqadeoQ>v_ZlAgE4#iGclp-x7@cuc;p=$j7QzFmCB>9kFr(LM8%1U4@3<<)TsKV3RAI@N7aPx zB^P2kc;-(e#B}$tgdd~41kNyJ(7CJ(?LU!7VMx|yn=ho%!WdlzW#gv{x7ntT(|v83 zt`V(uy_~m#?IZScL-hXwWqc_9Pah>+gZ-p)NJfgwsYubN-UEJ=;hd6F?J(8N7+V#J zGumSQBoD*k4l#^}<8>wokHCvfA|@^w83BhZmyn4GF8TJjlXb$eQg+kAn?an3?G`@z zjP@n|RR{PCx!EL>{r$zD(Usys2{lMY?o^iO4y{jnLF#nLhcT$#SgMYx4Kuc85+^+* z?m@Uz0vR73b`4zJG8@wcu5{VB%p-AKo=H~0D95j8Ycqtsr7NSKkQ{z9L`Uw~eZH1Q zc8~YSquR%fvgperS8!>Q>|DnM{31Z9ZFbYYD|Kl5E_9@FIrvr_z)cB1^QMgLQ$;aZFLQ~Aye8aSGD+Zul?Zn%83Ba? zjDW6q@`r*F@338?wfPRza=|;hTJ)GgopR_72r6{kuek z&suM4nlC>HRkMVWww#19 zs;hy@*SX}s z@y@9}5I;J_oF?OEhsWiSI6PynBjSviep$5Y?FfDVSXAM|TIt=jLFJ?;>L2FN${!-Y z?~>S=1h1#jjXQUSD&oygVubrWeL~_dJZ6NfTTYE=AwITM{658EKCt4}HfPS8ena7B zJsZx1>UE>Fw=b z)sjFIJ#V`yqxhJbcv21bVxgat)2oJ{bTXLE91=0pLF>@C z+Wyy4Z@D);s*X$_w2o*@>K^Ou9nl>_UFoO$MX_sSz8kLrb)}D*?ylL`0tDja0)l@` z9q1LfLCm`8%W(649?&@K<$I#$Z$mvL%vwx=wym|b!qw!%)RtM%t;uerEZZ#H .btn, +.stick input + .btn, +.stick input + input, +.stick input + .dropdown > .btn, +.stick .dropdown + .btn, +.stick .dropdown + input, +.stick .dropdown + .dropdown > .btn { + border-left: none; + +} + +.stick .btn + .dropdown > .btn { + border-left: none; + border-radius: 0 3px 3px 0; +} + +.btn { + display: inline-block; + min-height: 27px; + min-width: 15px; + margin: 0; + padding: 1px 5px; + background: #fff; + border-radius: 3px; + border: 1px solid #aaa; + color: #666; + font-size: 0.9rem; + vertical-align: middle; + cursor: pointer; + overflow: hidden; +} + +a.btn { + min-height: 25px; + line-height: 25px; +} + + +a.btn { + min-height: 25px; + line-height: 25px; +} + +.read_all.btn { + height:29px; +} + +.btn:hover { + background: #f0f0f0; + text-decoration: none; +} + + +.category.stick .btn { + background:#5bc0de; + color : #FFF; + border-color :#5bc0de; +} + +.category.stick .btn:first-child:hover, .category.stick .btn:last-child:hover, .category.stick .btn.active:first-child, .category.stick.active .btn:last-child { + background:#39b3d7; + border-color : #39b3d7; +} + + +.btn.active, +.btn:active, +.dropdown-target:target ~ .btn.dropdown-toggle { + background: #eee; +} + +.category.all > .btn { + background: #428bca; + color : #FFF; + border-color : #428bca; +} + +.category.all > .btn:hover { + background: #3276b1; + border-color : #3276b1; +} + +.category.favorites > .btn { + background:#f0ad4e; + border-color: #f0ad4e; + color : #fff; +} + +.category.favorites > .btn:hover { + background: #ed9c28; + border-color : #ed9c28; + color : white; +} + +.btn-important { + background: #5cb85c; + color: #fff; + border-color: #5cb85c; + font-weight: normal; +} +.btn-important:hover, .btn-important:active { + background:#47a447; + border-color : #47a447; + box-shadow: none; +} + +.btn-attention { + background: #d9534f; + color: #fff; + border: 1px solid #d9534f; + outline-color : #aaa; +} +.btn-attention:hover { + background: #d2322d; + border-color : #d2322d; +} +.btn-attention:active { + background: #d2322d; + box-shadow: none; +} + +/*=== Navigation */ +.nav-list .nav-header, +.nav-list .item { + height: 2.5em; + line-height: 2.5em; + font-size: 0.9rem; +} +.nav-list .item:hover { + background: #fafafa; +} +.nav-list .item:hover a { + color: #003388; +} +.nav-list .item.active { + background: #3498DB; + color: #fff; +} +.nav-list .item.active a { + color: #fff; +} +.nav-list .disable { + color: #aaa; + background: #fafafa; + text-align: center; +} +.nav-list .item > a { + padding: 0 10px; +} +.nav-list a:hover { + text-decoration: none; +} +.nav-list .item.empty a { + color: #f39c12; +} +.nav-list .item.active.empty a { + color: #fff; + background: #f39c12; +} +.nav-list .item.error a { + color: #BD362F; +} +.nav-list .item.active.error a { + color: #fff; + background: #BD362F; +} + +.nav-list .nav-header { + padding: 0 10px; + color: #888; + background: #f4f4f4; + border-bottom: 1px solid #ddd; + font-weight: bold; +} + +.nav-list .nav-form { + padding: 3px; + text-align: center; +} + +.nav-head { + margin: 0; + background: #fff; + background: linear-gradient(to bottom, #fff, #f0f0f0); + background: -moz-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -o-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%); + border-bottom: 1px solid #ddd; + text-align: right; +} +.nav-head .item { + padding: 5px 10px; + font-size: 0.9rem; + line-height: 1.5rem; +} + +/*=== Horizontal-list */ +.horizontal-list { + margin: 0; + padding: 0; +} +.horizontal-list .item { + vertical-align: middle; +} + +/*=== Dropdown */ +.dropdown-menu { + margin: 5px 0 0; + padding: 5px 0; + border: 1px solid #aaa; + border-radius: 5px; + font-size: 0.8rem; + text-align: left; +} +.dropdown-menu:after { + content: ""; + position: absolute; + top: -6px; + right: 13px; + width: 10px; + height: 10px; + background: #fff; + border-top: 1px solid #ddd; + border-left: 1px solid #ddd; + z-index: -10; + transform: rotate(45deg); + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); +} +.dropdown-header { + padding: 0 5px 5px; + color: #888; + font-weight: bold; + text-align: left; +} +.dropdown-menu > .item { +} +.dropdown-menu > .item > a { + padding: 0 25px; + line-height: 2.5em; + color: #666; +} +.dropdown-menu > .item > span { + padding: 0 25px; + line-height: 2em; +} +.dropdown-menu > .item:hover { + background: #eee; + color: #666; +} +.dropdown-menu > .item[aria-checked="true"] > a:before { + font-weight: bold; + margin: 0 0 0 -14px; +} +.dropdown-menu > .item:hover > a { + color: #666; + text-decoration: none; +} +.dropdown-menu .input select, +.dropdown-menu .input input { + margin: 0 auto 5px; + padding: 2px 5px; + border-radius: 3px; +} + +.separator { + margin: 5px 0; + border-bottom: 1px solid #ddd; +} + +/*=== Alerts */ +.alert { + margin: 15px auto; + padding: 10px 15px; + background: #f4f4f4; + border: 1px solid #ccc; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + border-radius: 5px; + color: #aaa; + font-size: 0.9em; +} +.alert-head { + font-size: 1.15em; +} +.alert > a { + color: inherit; + text-decoration: underline; +} +.alert-warn { + background: #ffe; + border: 1px solid #eeb; + color: #c95; +} +.alert-success { + background: #dfd; + border: 1px solid #cec; + color: #484; +} +.alert-error { + background: #fdd; + border: 1px solid #ecc; + color: #844; +} + +/*=== Pagination */ +.pagination { + background: #fff; + text-align: center; + color: #41444f; + font-size: 0.8em; +} +.content .pagination { + margin: 0; + padding: 0; +} +.pagination .item.pager-current { + font-weight: bold; + font-size: 1.5em; +} +.pagination .item a { + display: block; + color: #41444f; + font-style: italic; + line-height: 3em; + text-decoration: none; +} +.pagination .item a:hover { + background: #ddd; +} +.pagination:first-child .item { + border-bottom: 1px solid #aaa; +} +.pagination:last-child .item { + border-top: 1px solid #aaa; +} + +.pagination .loading, +.pagination a:hover.loading { + background: url("loader.gif") center center no-repeat #fff; + font-size: 0; +} + +/*=== STRUCTURE */ +/*===============*/ +/*=== Header */ +.header { + height: 85px; + background: #41444f; +} +.header > .item { + padding: 10px; + border-bottom: 1px solid #aaa; + vertical-align: middle; + text-align: center; +} +.header > .item.title{ + width: 230px; +} +.header > .item.title h1 { + margin: 0.5em 0; +} + +.header > .item.title h1 a, a.signin { + text-decoration: none; + color : #C5C6CA; +} + +.header > .item.search input { + width: 230px; + height : 29px; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.header > .item.search button { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height : 29px; +} + +.header .item.search input:focus { + width: 350px; +} + +/*=== Body */ +#global { + height: calc(100% - 85px); +} +.aside { + border-right: 1px solid #aaa; + background: #fff; +} +.aside.aside_flux { + padding: 10px 0 50px; +} + +/*=== Aside main page (categories) */ +.categories { + text-align: center; +} +.category { + width: 215px; + margin: 10px auto; + text-align: left; +} +.category .btn:first-child { + position: relative; + width: 203px; +} +.category.stick .btn:first-child { + width: 176px; +} +.category .btn:first-child:not([data-unread="0"]):after { + position: absolute; + top: 2px; right: 3px; + padding: 0px 3px; + border: 1px solid ; + border-radius: 3px; + font-size:10pt; + line-height : 20px; +} + +/*=== Aside main page (feeds) */ +.categories .feeds .item.active { + background: #5cb85c; +} +.categories .feeds .item.active .feed { + color: #fff; +} +.categories .feeds .item.empty .feed { + color: #e67e22; +} +.categories .feeds .item.empty.active { + background: #e67e22; +} +.categories .feeds .item.empty.active .feed { + color: #fff; +} +.categories .feeds .item.error .feed { + color: #BD362F; +} +.categories .feeds .item .feed { + margin: 0; + width: 165px; + line-height: 3em; + font-size: 0.8em; + text-align: left; + text-decoration: none; +} +.categories .feeds .feed:not([data-unread="0"]) { + font-weight: bold; +} +.categories .feeds .dropdown-menu:after { + left: 2px; +} +.categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, +.categories .feeds .item:hover .dropdown-toggle > .icon, +.categories .feeds .item.active .dropdown-toggle > .icon { + background-color: #fff; + border-radius: 3px; + vertical-align: middle; +} + +/*=== Configuration pages */ +.post { + padding: 10px 50px; + font-size: 0.9em; +} +.post form { + margin: 10px 0; +} +.post.content { + max-width: 550px; +} + +/*=== Prompt (centered) */ +.prompt { + text-align: center; +} +.prompt label { + text-align: left; +} +.prompt form { + margin: 10px auto 20px auto; + width: 200px; +} +.prompt input { + margin: 5px auto; + width: 100%; +} +.prompt p { + margin: 20px 0; +} + +/*=== New article notification */ +#new-article { + background: #428bca; + text-align: center; + font-size: 0.9em; +} +#new-article:hover { + background: #3276b1; +} +#new-article > a { + line-height: 3em; + color: #fff; + font-weight: bold; +} +#new-article > a:hover { + text-decoration: none; +} + +/*=== Day indication */ +.day { + padding: 0 10px; + font-weight: bold; + line-height: 3em; + background: #fff; + border-top: 1px solid #aaa; + border-bottom: 1px solid #aaa; + color : #666; +} +#new-article + .day { + border-top: none; +} +.day .name { + padding: 0 10px 0 0; + color : #666; + font-size: 1.8em; + opacity: 0.3; + font-style: italic; + text-align: right; +} + +/*=== Index menu */ +.nav_menu { + background: #fafafa; + border-bottom: 1px solid #aaa; + text-align: center; + padding: 5px 0; +} + +/*=== Feed articles */ +.flux { + border-left: 3px solid #5cb85c; + background: #fafafa; +} +.flux:hover { + background: #fff; +} +.flux.current { + border-left: 3px solid #39b3d7; +} +.flux.not_read { + border-left: 3px solid #d9534f; +} +.flux .item.title a, .flux.not_read:not(.current):hover .item.title { + color : #333; +} +.flux.favorite { + border-left: 2px solid #428bca; + background: #FFF6DA; +} +.flux.favorite:not(.current):hover .item.title { + background: #FFF6DA; +} +.flux.current { + background: #fff; +} + + +.flux_header { + border-top: 1px solid #ddd; + font-size: 0.8rem; + cursor: pointer; +} +.flux_header .title { + font-size: 0.9rem; +} +.flux .website .favicon { + padding: 5px; +} +.flux .date { + color: #666; + font-size: 0.7rem; +} + +.flux .bottom { + font-size: 0.8rem; + text-align: center; +} + +/*=== Content of feed articles */ +.content { + padding: 20px 10px; +} +.content > h1.title > a { + color: #333; +} + +.content hr { + margin: 30px 10px; + height: 1px; + background: #ddd; + border: 0; + box-shadow: 0 2px 5px #ccc; +} + +.content pre { + margin: 10px auto; + padding: 10px 20px; + overflow: auto; + background: #222; + color: #fff; + font-size: 0.9rem; + border-radius: 3px; +} +.content code { + padding: 2px 5px; + color: #dd1144; + background: #fafafa; + border: 1px solid #eee; + border-radius: 3px; +} +.content pre code { + background: transparent; + color: #fff; + border: none; +} + +.content blockquote { + display: block; + margin: 0; + padding: 5px 20px; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + background: #fafafa; + color: #41444f; +} +.content blockquote p { + margin: 0; +} + +/*=== Notification and actualize notification */ +.notification { + padding: 0 0 0 5px; + text-align: center; + border: 1px solid #eeb; + border-radius: 3px; + box-shadow: 0 0 5px #ddd; + font-weight: bold; + font-size: 0.9em; + line-height: 3em; + z-index: 10; + vertical-align: middle; +} +.notification.good { + background: #ffe; + border: 1px solid #eeb; + color: #c95; +} +.notification.bad { + background: #fdd; + border: 1px solid #ecc; + color: #844; +} +.notification a.close { + padding: 0 15px; + line-height: 3em; +} +.notification.good a.close:hover { + background: #eeb; +} +.notification.bad a.close:hover { + background: #ecc; +} + +.notification#actualizeProgress { + line-height: 2em; +} + +/*=== "Load more" part */ +#bigMarkAsRead { + text-align: center; + text-decoration: none; + color: #666; + background: #fafafa; +} +#bigMarkAsRead:hover { + color: #000; + background: #f0f0f0; +} + +#bigMarkAsRead:hover .bigTick { +/* text-shadow: 0 0 10px #666;*/ +} + +/*=== Navigation menu (for articles) */ +#nav_entries { + margin: 0; + background: #fff; + border-top: 1px solid #ddd; + text-align: center; + line-height: 3em; + table-layout: fixed; +} + +#nav_entries .item:hover { + background:#eee ; +} +/*=== READER VIEW */ +/*================*/ +#stream.reader .flux { + padding: 0 0 50px; + border: none; + background: #f0f0f0; + color: #41444f; +} +#stream.reader .flux .author { + margin: 0 0 10px; + font-size: 90%; + color: #666; +} + +/*=== GLOBAL VIEW */ +/*================*/ +#stream.global .box-category { + background: #fff; + border:none; + text-align: left; + +} + +#stream.global .category { + margin: 0; +} + +#stream.global .category:first-child { + margin: 0; +} + + +#stream.global .btn { + width: auto; + height: 2em; + margin: 0; + padding: 0 10px; + background: #f6f6f6; + border-bottom: 1px solid #aaa; + border-radius: 5px 5px 0 0; + line-height: 2em; + font-size: 1.2rem; +} + +#stream.global .btn:not([data-unread="0"]) { + background: #5bc0de; + border-color : #5bc0de; + color: #fff; + font-weight: bold; + text-shadow: none; + +} + + +#stream.global .btn:first-child:not([data-unread="0"]):after { + top: 0; right: 5px; + border: 0; + background: none; + color: #fff; + font-weight: bold; + box-shadow: none; + text-shadow: none; +} + +#stream.global .box-category .feeds { + max-height: 250px; + width: 302px; + border : solid #aaa 1px; + border-top : none; +} + +#stream.global .box-category .feeds .item { + padding: 2px 10px; + font-size: 0.9rem; +} + +/*=== DIVERS */ +/*===========*/ +.aside.aside_feed .nav-form input, +.aside.aside_feed .nav-form select { + width: 140px; +} +.aside.aside_feed .nav-form .dropdown .dropdown-menu { + right: -20px; +} +.aside.aside_feed .nav-form .dropdown .dropdown-menu:after { + right: 33px; +} + +/*=== STATISTICS */ +/*===============*/ +.stat { + margin: 10px 0 20px; +} + +.stat th, +.stat td, +.stat tr { + border: none; +} +.stat > table td, +.stat > table th { + border-bottom: 1px solid #ddd; + text-align: center; +} + +/*=== LOGS */ +/*=========*/ +.logs { + border: 1px solid #aaa; + border-radius: 5px; + overflow: hidden; +} +.log { + padding: 5px 10px; + background: #fafafa; + color: #41444f; + font-size: 0.8rem; +} +.log+.log { + border-top: 1px solid #aaa; +} +.log .date { + display: block; + font-weight: bold; +} +.log.error { + background: #fdd; + color: #844; +} +.log.warning { + background: #ffe; + color: #c95; +} +.log.notice { + background: #f4f4f4; + color: #aaa; +} +.log.debug { + background: #41444f; + color: #eee; +} + +/*=== MOBILE */ +/*===========*/ +@media(max-width: 840px) { + .aside { + box-shadow: 3px 0 3px #aaa; + transition: width 200ms linear; + -moz-transition: width 200ms linear; + -webkit-transition: width 200ms linear; + -o-transition: width 200ms linear; + -ms-transition: width 200ms linear; + } + .aside .toggle_aside, + #panel .close { + position: absolute; + display: block; + top: 0; right: 0; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; + background: #f6f6f6; + border-left: 1px solid #ddd; + border-bottom: 1px solid #ddd; + border-radius: 0 0 0 5px; + } + + .nav_menu .btn { + margin: 5px 10px; + } + .nav_menu .stick { + margin: 0 10px; + } + .nav_menu .stick .btn { + margin: 5px 0; + } + .nav_menu .search { + display: inline-block; + max-width: 97%; + } + .nav_menu .search input { + max-width: 97%; + width: 90px; + } + .nav_menu .search input:focus { + width: 400px; + } + + .day .name { + font-size: 1.1rem; + text-shadow: none; + } + + .pagination { + margin: 0 0 3.5em; + } + + .notification a.close { + display: block; + left: 0; + background: transparent; + } + .notification a.close:hover { + opacity: 0.5; + } + .notification a.close .icon { + display: none; + } +} \ No newline at end of file diff --git a/p/themes/Pafat/template.css b/p/themes/Pafat/template.css new file mode 100755 index 000000000..09ecaf685 --- /dev/null +++ b/p/themes/Pafat/template.css @@ -0,0 +1,695 @@ +@charset "UTF-8"; + +/*=== GENERAL */ +/*============*/ +html, body { + margin: 0; + padding: 0; + font-size: 100%; +} + +/*=== Links */ +a { + text-decoration: none; +} +a:hover { + text-decoration: underline; +} + +/*=== Lists */ +ul, ol, dd { + margin: 0; + padding: 0; +} + +/*=== Titles */ +h1 { + margin: 0.6em 0 0.3em; + font-size: 1.5em; + line-height: 1.6em; +} +h2 { + margin: 0.5em 0 0.25em; + font-size: 1.3em; + line-height: 2em; +} +h3 { + margin: 0.5em 0 0.25em; + font-size: 1.1em; + line-height: 2em; +} + +/*=== Paragraphs */ +p { + margin: 1em 0 0.5em; + font-size: 1em; +} + +/*=== Images */ +img { + height: auto; + max-width: 100%; +} +img.favicon { + height: 16px; + width: 16px; + vertical-align: middle; +} + +/*=== Videos */ +iframe, embed, object, video { + max-width: 100%; +} + +/*=== Forms */ +legend { + display: block; + width: 100%; + clear: both; +} +label { + display: block; +} +input { + width: 180px; +} +textarea { + width: 300px; +} +input, select, textarea { + display: inline-block; + max-width: 100%; +} +input[type="radio"], +input[type="checkbox"] { + width: 15px !important; + min-height: 15px !important; +} +input.extend:focus { + width: 300px; +} + +/*=== COMPONENTS */ +/*===============*/ +/*=== Forms */ +.form-group:after { + content: ""; + display: block; + clear: both; +} +.form-group.form-actions { + min-width: 250px; +} +.form-group .group-name { + display: block; + float: left; + width: 200px; +} +.form-group .group-controls { + min-width: 250px; + margin: 0 0 0 220px; +} +.form-group .group-controls .control { + display: block; +} + +/*=== Buttons */ +.stick { + display: inline-block; + white-space: nowrap; +} +.btn, +a.btn { + display: inline-block; + cursor: pointer; + overflow: hidden; +} +.btn-important { + font-weight: bold; +} + +/*=== Navigation */ +.nav-list .nav-header, +.nav-list .item { + display: block; +} +.nav-list .item, +.nav-list .item > a { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.nav-head { + display: block; +} +.nav-head .item { + display: inline-block; +} + +/*=== Horizontal-list */ +.horizontal-list { + display: table; + table-layout: fixed; + width: 100%; +} +.horizontal-list .item { + display: table-cell; +} + +/*=== Dropdown */ +.dropdown { + position: relative; + display: inline-block; +} +.dropdown-target { + display: none; +} +.dropdown-menu { + display: none; + min-width: 200px; + margin: 0; + position: absolute; + right: 0; + background: #fff; + border: 1px solid #aaa; +} +.dropdown-header { + display: block; +} +.dropdown-menu > .item { + display: block; +} +.dropdown-menu > .item > a, +.dropdown-menu > .item > span { + display: block; +} +.dropdown-menu > .item[aria-checked="true"] > a:before { + content: '✓'; +} +.dropdown-menu .input { + display: block; +} +.dropdown-menu .input select, +.dropdown-menu .input input { + display: block; + max-width: 95%; +} +.dropdown-target:target ~ .dropdown-menu { + display: block; + z-index: 10; +} +.dropdown-close { + display: inline; +} +.dropdown-close a { + font-size: 0; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + display: block; + z-index: -10; +} +.separator { + display: block; + height: 0; + border-bottom: 1px solid #aaa; +} + +/*=== Alerts */ +.alert { + display: block; + width: 90%; +} +.group-controls .alert { + width: 100% +} +.alert-head { + margin: 0; + font-weight: bold; +} +.alert ul { + margin: 5px 20px; +} + +/*=== Icons */ +.icon { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + line-height: 16px; +} + +/*=== Pagination */ +.pagination { + display: table; + width: 100%; + margin: 0; + padding: 0; + table-layout: fixed; +} +.pagination .item { + display: table-cell; +} +.pagination .pager-first, +.pagination .pager-previous, +.pagination .pager-next, +.pagination .pager-last { + width: 100px; +} + +/*=== STRUCTURE */ +/*===============*/ +/*=== Header */ +.header { + display: table; + width: 100%; + table-layout: fixed; +} +.header > .item { + display: table-cell; +} +.header > .item.title { + width: 250px; + white-space: nowrap; +} +.header > .item.title h1 { + display: inline-block; +} +.header > .item.title .logo { + display: inline-block; + height: 32px; + width: 32px; + vertical-align: middle; +} +.header > .item.configure { + width: 100px; +} + +/*=== Body */ +#global { + display: table; + width: 100%; + height: 100%; + table-layout: fixed; +} +.aside { + display: table-cell; + height: 100%; + width: 250px; + vertical-align: top; +} +.aside.aside_flux { + background: #fff; +} + +/*=== Aside main page (categories) */ +.categories { + list-style: none; + margin: 0; +} +.category { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.category .btn:not([data-unread="0"]):after { + content: attr(data-unread); +} + +/*=== Aside main page (feeds) */ +.categories .feeds { + width: 100%; + list-style: none; +} +.categories .feeds:not(.active) { + display: none; +} +.categories .feeds .feed { + display: inline-block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + vertical-align: middle; +} +.categories .feeds .feed:not([data-unread="0"]):before { + content: "(" attr(data-unread) ") "; +} +.categories .feeds .dropdown-menu { + left: 0; +} +.categories .feeds .item .dropdown-toggle > .icon { + visibility: hidden; + cursor: pointer; + vertical-align: top; +} +.categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, +.categories .feeds .item:hover .dropdown-toggle > .icon, +.categories .feeds .item.active .dropdown-toggle > .icon { + visibility: visible; +} + +/*=== New article notification */ +#new-article { + display: none; +} +#new-article > a { + display: block; +} + +/*=== Day indication */ +.day .name { + position: absolute; + right: 0; + width: 50%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +/*=== Feed article header and footer */ +.flux_header { + position: relative; +} +.flux .item { + line-height: 40px; + white-space: nowrap; +} +.flux .item.manage, +.flux .item.link { + width: 40px; + text-align: center; +} +.flux .item.website { + width: 200px; +} +.flux.not_read .item.title, +.flux.current .item.title { + font-weight: bold; +} +.flux:not(.current):hover .item.title { + position: absolute; + max-width: calc(100% - 320px); + background: #fff; +} +.flux .item.title a { + color: #000; + text-decoration: none; +} +.flux .item.date { + width: 145px; + text-align: right; +} +.flux .item > a { + display: block; +} +.flux .item > a { + display: block; + text-decoration: none; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +.flux .item.share > a { + display: list-item; + list-style-position: inside; + list-style-type: decimal; +} + +/*=== Feed article content */ +.hide_posts > .flux:not(.active) > .flux_content { + display: none; +} +.content { + min-height: 20em; + margin: auto; + line-height: 1.7em; + word-wrap: break-word; +} +.content.large { + max-width: 1000px; +} +.content.medium { + max-width: 800px; +} +.content.thin { + max-width: 550px; +} +.content ul, +.content ol, +.content dd { + margin: 0 0 0 15px; + padding: 0 0 5px 15px; +} +.content pre { + overflow: auto; +} + +/*=== Notification and actualize notification */ +.notification { + position: absolute; + top: 1em; + left: 25%; right: 25%; + z-index: 10; + background: #fff; + border: 1px solid #aaa; +} +.notification.closed { + display: none; +} +.notification a.close { + position: absolute; + top: 0; bottom: 0; + right: 0; + display: inline-block; +} + +#actualizeProgress { + position: fixed; +} +#actualizeProgress progress { + max-width: 100%; + vertical-align: middle; +} +#actualizeProgress .progress { + vertical-align: middle; +} + +/*=== Navigation menu (for articles) */ +#nav_entries { + position: fixed; + bottom: 0; left: 0; + display: table; + width: 250px; + background: #fff; + table-layout: fixed; +} +#nav_entries .item { + display: table-cell; + width: 30%; +} +#nav_entries a { + display: block; +} + +/*=== "Load more" part */ +#load_more { + min-height: 40px; +} +.loading { + background: url("loader.gif") center center no-repeat; + font-size: 0; +} +#bigMarkAsRead { + display: block; + padding: 3em 0; + text-align: center; +} +.bigTick { + font-size: 7em; + line-height: 1.6em; +} + +/*=== Statistiques */ +.stat > table { + width: 100%; +} + +/*=== GLOBAL VIEW */ +/*================*/ +/*=== Category boxes */ +#stream.global .box-category { + display: inline-block; + width: 19em; + max-width: 95%; + margin: 20px 10px; + border: 1px solid #ccc; + vertical-align: top; +} +#stream.global .category { + width: 100%; +} +#stream.global .btn { + display: block; +} +#stream.global .box-category .feeds { + display: block; + overflow: auto; +} +#stream.global .box-category .feed { + width: 19em; + max-width: 90%; +} + +/*=== Panel */ +#overlay { + display: none; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + background: rgba(0, 0, 0, 0.9); +} +#panel { + display: none; + position: fixed; + top: 1em; bottom: 1em; + left: 2em; right: 2em; + overflow: auto; + background: #fff; +} +#panel .close { + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + display: block; +} +#panel .close img { + display: none; +} + +/*=== DIVERS */ +/*===========*/ +.nav-login, +.nav_menu .search, +.nav_menu .toggle_aside { + display: none; +} + +.aside .toggle_aside { + position: absolute; + right: 0; + display: none; + width: 30px; + height: 30px; + line-height: 30px; + text-align: center; +} + +/*=== MOBILE */ +/*===========*/ +@media(max-width: 840px) { + .header, + .aside .btn-important, + .aside .feeds .dropdown, + .flux_header .item.website span, + .item.date, .day .date, + .dropdown-menu > .no-mobile, + .no-mobile { + display: none; + } + .nav-login { + display: block; + } + .nav_menu .toggle_aside, + .aside .toggle_aside, + .nav_menu .search, + #panel .close img { + display: inline-block; + } + + .aside { + position: fixed; + top: 0; bottom: 0; + left: 0; + width: 0; + overflow: hidden; + z-index: 100; + } + .aside:target { + width: 90%; + overflow: auto; + } + .aside .categories { + margin: 10px 0 75px; + } + + .flux_header .item.website { + width: 40px; + } + + .flux:not(.current):hover .item.title { + position: relative; + width: auto; + white-space: nowrap; + } + + .notification { + top: 0; + left: 0; + right: 0; + } + + #nav_entries { + width: 100%; + } + + #stream.global .box-category { + margin: 10px 0; + } + + #panel { + top: 0; bottom: 0; + left: 0; right: 0; + } + #panel .close { + top: 0; right: 0; + left: auto; bottom: auto; + display: inline-block; + width: 30px; + height: 30px; + } +} + +/*=== PRINTER */ +/*============*/ +@media print { + .header, .aside, + .nav_menu, .day, + .flux_header, + .flux_content .bottom, + .pagination, + #nav_entries { + display: none; + } + html, body { + background: #fff; + color: #000; + font-family: Serif; + } + #global, + .flux_content { + display: block !important; + } + .flux_content .content { + width: 100% !important; + } + .flux_content .content a { + color: #000; + } + .flux_content .content a:after { + content: " [" attr(href) "] "; + font-style: italic; + } +} From 097703f23e6799f9c20d282459c461b0462dc737 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 18 Sep 2014 17:39:07 +0200 Subject: [PATCH 077/126] Improve stat design --- app/views/stats/idle.phtml | 19 +++++++++--- app/views/stats/index.phtml | 46 ++++++++++++++-------------- app/views/stats/repartition.phtml | 8 ++--- p/themes/Dark/dark.css | 12 ++++++++ p/themes/Flat/flat.css | 12 ++++++++ p/themes/Origine/origine.css | 12 ++++++++ p/themes/Screwdriver/screwdriver.css | 12 ++++++++ p/themes/base-theme/base.css | 12 ++++++++ p/themes/base-theme/template.css | 8 +++++ 9 files changed, 109 insertions(+), 32 deletions(-) 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 @@ partial('aside_stats'); ?> -
      +

      @@ -12,11 +12,20 @@

      -
        - -
      • - + + + +
          +
        • +
          + + + +
          +
        • +
        +
      partial('aside_stats'); ?> -
      +
      - +

      -
      +

      @@ -38,26 +38,9 @@
      -
      - -
      -

      -
      -
      - -
      -

      -
      -
      -
      - -
      -

      -
      -
      -
      - -
      +

      @@ -78,6 +61,23 @@
      + +
      +

      +
      +
      + +
      +

      +
      +
      +
      +

      +
      +
      +
      diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 03f496d1f..af2323325 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -187,12 +187,12 @@ Minz_Session::_param('markReadUrl', $markReadUrl); ?> - +
    • @@ -218,13 +218,13 @@ ?>
    • diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index 1b15cc632..cea338364 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -6,6 +6,8 @@ Minz_Session::_param('markReadUrl', false); ?> + +
      • nextId)) { ?> @@ -19,7 +21,7 @@