From 75632e70f0d49048f4ce72a0fa8bbcbcd7b2d312 Mon Sep 17 00:00:00 2001 From: Marien Fressinaud Date: Thu, 29 Aug 2019 12:02:05 +0200 Subject: Provide email address verification feature (#2481) * Add an email field to the profile page I reuse the `mail_login` from the configuration. I'm not sure if it's useful today (I would say it was used when Persona login was available). A good improvement would be to rename `mail_login` into `email` so it would be more intuitive to use. * Add boolean to the conf to force email validation This commit only adds a configuration item. * Add email during registration if email must be validated * Set email token to validate when email changes * Block access to FreshRSS if email is not validated * Send email when address is changed * Allow to resend the validation email * Allow the user to change its email while blocked * Document the email validation feature * fixup! Allow the user to change its email while blocked * tec: Autoload PHPMailer lib * Validate email address format * Add feedback on validation email resend action * Allow to logout when user is blocked * fix: Change default email "from" * Reorganize i18n keys * Complete all the locales with default english * Hide sidebar (profile page) if email is not validated * Check email requirements on registration * Allow admin to specify email when creating users * Don't check email format if value is empty * Remove trailing comma in userController Co-Authored-By: Alexandre Alapetite * Set PHPMailer validator to html5 before sending email * fixup! Remove trailing comma in userController --- lib/Minz/Request.php | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib/Minz/Request.php') diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 912c354ac..01feece52 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -98,6 +98,13 @@ class Minz_Request { self::initJSON(); } + public static function is($controller_name, $action_name) { + return ( + self::$controller_name === $controller_name && + self::$action_name === $action_name + ); + } + /** * Return true if the request is over HTTPS, false otherwise (HTTP) */ -- cgit v1.2.3 From cc0db9af4f980829faa4bf0960617807b32fb4fa Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 23 Oct 2019 00:52:15 +0200 Subject: Feature/new archiving (#2335) * Change archiving config page layout I've changed some wording and moved actions into a maintenance section. * Update purge action Now we have more control on the purge action. The configuration allows us to choose what to keep and what to discard in a more precise way. At the moment, the configuration applies for all feeds. * Add purge configuration on feed level Now the extend purge configuration is available on feed level. It is stored as attributes and will be used in the purge action. * Update purge action Now the purge action uses the feed configuration if it exists and defaults on user configuration if not. * Add empty option in period list * Fix configuration warnings * Add archiving configuration on categories See #2369 * Add user info back * Add explanations in UI * Fixes for SQLite + error + misc. * Fix invalid feed reference * Short array syntax Only for new code, so far * Fix prefix error * Query performance, default values Work in progress * Fix default values and confirm before leaving Form cancel and confirm changes before leaving were broken. And start taking advantage of the short echo syntax `` as we have moved to PHP 5.4+ * More work * Tuning SQL * Fix MariaDB + performance issue * SQL performance * Fix SQLite bug * Fix some attributes JSON encoding bugs Especially for SQLite export/import * More uniform, fix bugs More uniform between global, category, feed settings * Drop special cases for old articles during refresh Instead will use lastSeen date with the new archiving logic. This was generating problems anyway https://github.com/FreshRSS/FreshRSS/issues/2154 * Draft drop index keep_history Not needed anymore * MySQL typo Now properly tested with MySQL, PostgreSQL, SQLite * More work for legacy values Important to avoid overriding user's preference and risking deleting data erroneously * Fix PHP 7.3 / 7.4 warnings @aledeg "Trying to use values of type null, bool, int, float or resource as an array (such as $null["key"]) will now generate a notice. " https://php.net/migration74.incompatible * Reintroduce min articles and take care of legacy parameters * A few changes forgotten * Draft of migration + DROP of feed.keep_history * Fix several errors And give up using const for SQL to allow multiple database types (and we cannot redefine a const) * Add keep_min to categories + factorise archiving logic * Legacy fix * Fix bug yield from * Minor: Use JSON_UNESCAPED_SLASHE for attributes And make more uniform * Fix sign and missing variable * Fine tune the logic --- app/Controllers/configureController.php | 43 +++++++++- app/Controllers/entryController.php | 20 +---- app/Controllers/feedController.php | 24 +----- app/Controllers/subscriptionController.php | 58 +++++++++++++- app/Models/Category.php | 24 ++++++ app/Models/CategoryDAO.php | 122 ++++++++++++++++++++++++++--- app/Models/CategoryDAOSQLite.php | 17 ++++ app/Models/ConfigurationSetter.php | 10 --- app/Models/Context.php | 19 +++++ app/Models/DatabaseDAO.php | 12 +-- app/Models/DatabaseDAOSQLite.php | 2 +- app/Models/EntryDAO.php | 65 ++++++++++----- app/Models/Factory.php | 8 +- app/Models/Feed.php | 38 ++++++--- app/Models/FeedDAO.php | 20 ++--- app/Models/FeedDAOSQLite.php | 2 +- app/Models/Tag.php | 2 +- app/Models/TagDAO.php | 14 +++- app/Models/UserDAO.php | 11 ++- app/SQL/install.sql.mysql.php | 21 +++-- app/SQL/install.sql.pgsql.php | 19 +++-- app/SQL/install.sql.sqlite.php | 17 ++-- app/i18n/cz/conf.php | 12 ++- app/i18n/cz/gen.php | 7 ++ app/i18n/cz/sub.php | 3 +- app/i18n/de/conf.php | 12 ++- app/i18n/de/gen.php | 7 ++ app/i18n/de/sub.php | 3 +- app/i18n/en/conf.php | 12 ++- app/i18n/en/gen.php | 7 ++ app/i18n/en/sub.php | 3 +- app/i18n/es/conf.php | 12 ++- app/i18n/es/gen.php | 7 ++ app/i18n/es/sub.php | 3 +- app/i18n/fr/conf.php | 12 ++- app/i18n/fr/gen.php | 7 ++ app/i18n/fr/sub.php | 3 +- app/i18n/he/conf.php | 12 ++- app/i18n/he/gen.php | 7 ++ app/i18n/he/sub.php | 3 +- app/i18n/it/conf.php | 12 ++- app/i18n/it/gen.php | 7 ++ app/i18n/it/sub.php | 3 +- app/i18n/kr/conf.php | 12 ++- app/i18n/kr/gen.php | 7 ++ app/i18n/kr/sub.php | 3 +- app/i18n/nl/conf.php | 16 +++- app/i18n/nl/gen.php | 7 ++ app/i18n/nl/sub.php | 3 +- app/i18n/oc/conf.php | 11 ++- app/i18n/oc/gen.php | 7 ++ app/i18n/oc/sub.php | 3 +- app/i18n/pt-br/conf.php | 12 ++- app/i18n/pt-br/gen.php | 7 ++ app/i18n/pt-br/sub.php | 3 +- app/i18n/ru/conf.php | 14 +++- app/i18n/ru/gen.php | 7 ++ app/i18n/ru/sub.php | 3 +- app/i18n/sk/conf.php | 2 +- app/i18n/sk/sub.php | 2 +- app/i18n/tr/conf.php | 12 ++- app/i18n/tr/gen.php | 7 ++ app/i18n/tr/sub.php | 3 +- app/i18n/zh-cn/conf.php | 12 ++- app/i18n/zh-cn/gen.php | 7 ++ app/i18n/zh-cn/sub.php | 3 +- app/install.php | 20 +---- app/views/configure/archiving.phtml | 110 ++++++++++++++++++++------ app/views/helpers/category/update.phtml | 116 +++++++++++++++++++++++++++ app/views/helpers/feed/update.phtml | 113 ++++++++++++++++++++++++-- cli/_update-or-create-user.php | 4 +- config-user.default.php | 10 ++- lib/Minz/Request.php | 6 ++ lib/lib_rss.php | 6 +- p/scripts/category.js | 23 ++++++ p/scripts/extra.js | 38 ++++++--- p/themes/base-theme/template.css | 3 + phpcs.xml | 5 -- 78 files changed, 1062 insertions(+), 277 deletions(-) create mode 100644 app/Models/CategoryDAOSQLite.php (limited to 'lib/Minz/Request.php') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 85ca9da39..b38d3289a 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -196,9 +196,31 @@ class FreshRSS_configure_Controller extends Minz_ActionController { */ public function archivingAction() { if (Minz_Request::isPost()) { - FreshRSS_Context::$user_conf->old_entries = Minz_Request::param('old_entries', 3); - FreshRSS_Context::$user_conf->keep_history_default = Minz_Request::param('keep_history_default', 0); + if (!Minz_Request::paramBoolean('enable_keep_max')) { + $keepMax = false; + } elseif (!$keepMax = Minz_Request::param('keep_max')) { + $keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT; + } + if ($enableRetentionPeriod = Minz_Request::paramBoolean('enable_keep_period')) { + $keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD; + if (is_numeric(Minz_Request::param('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::param('keep_period_unit'))) { + $keepPeriod = str_replace('1', Minz_Request::param('keep_period_count'), Minz_Request::param('keep_period_unit')); + } + } else { + $keepPeriod = false; + } + FreshRSS_Context::$user_conf->ttl_default = Minz_Request::param('ttl_default', FreshRSS_Feed::TTL_DEFAULT); + FreshRSS_Context::$user_conf->archiving = [ + 'keep_period' => $keepPeriod, + 'keep_max' => $keepMax, + 'keep_min' => Minz_Request::param('keep_min_default', 0), + 'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'), + 'keep_labels' => Minz_Request::paramBoolean('keep_labels'), + 'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'), + ]; + FreshRSS_Context::$user_conf->keep_history_default = null; //Legacy < FreshRSS 1.15 + FreshRSS_Context::$user_conf->old_entries = null; //Legacy < FreshRSS 1.15 FreshRSS_Context::$user_conf->save(); invalidateHttpCache(); @@ -206,7 +228,20 @@ class FreshRSS_configure_Controller extends Minz_ActionController { array('c' => 'configure', 'a' => 'archiving')); } - Minz_View::prependTitle(_t('conf.archiving.title') . ' · '); + $volatile = [ + 'enable_keep_period' => false, + 'keep_period_count' => '3', + 'keep_period_unit' => 'P1M', + ]; + $keepPeriod = FreshRSS_Context::$user_conf->archiving['keep_period']; + if (preg_match('/^PT?(?P\d+)[YMWDH]$/', $keepPeriod, $matches)) { + $volatile = [ + 'enable_keep_period' => true, + 'keep_period_count' => $matches['count'], + 'keep_period_unit' => str_replace($matches['count'], 1, $keepPeriod), + ]; + } + FreshRSS_Context::$user_conf->volatile = $volatile; $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->nb_total = $entryDAO->count(); @@ -217,6 +252,8 @@ class FreshRSS_configure_Controller extends Minz_ActionController { if (FreshRSS_Auth::hasAccess('admin')) { $this->view->size_total = $databaseDAO->size(true); } + + Minz_View::prependTitle(_t('conf.archiving.title') . ' · '); } /** diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 0215128f4..7881cb3ec 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -181,32 +181,20 @@ class FreshRSS_entry_Controller extends Minz_ActionController { public function purgeAction() { @set_time_limit(300); - $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); - $date_min = time() - (3600 * 24 * 30 * $nb_month_old); - - $entryDAO = FreshRSS_Factory::createEntryDao(); $feedDAO = FreshRSS_Factory::createFeedDao(); $feeds = $feedDAO->listFeeds(); $nb_total = 0; invalidateHttpCache(); - foreach ($feeds as $feed) { - $feed_history = $feed->keepHistory(); - if (FreshRSS_Feed::KEEP_HISTORY_DEFAULT === $feed_history) { - $feed_history = FreshRSS_Context::$user_conf->keep_history_default; - } + $feedDAO->beginTransaction(); - if ($feed_history >= 0) { - $nb = $entryDAO->cleanOldEntries($feed->id(), $date_min, $feed_history); - if ($nb > 0) { - $nb_total += $nb; - Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url(false) . ']'); - } - } + foreach ($feeds as $feed) { + $nb_total += $feed->cleanOldEntries(); } $feedDAO->updateCachedValues(); + $feedDAO->commit(); $databaseDAO = FreshRSS_Factory::createDatabaseDAO(); $databaseDAO->minorDbMaintenance(); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index ea07d96e4..aabeb80ff 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -267,10 +267,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $maxFeeds = 10; } - // Calculate date of oldest entries we accept in DB. - $nb_month_old = max(FreshRSS_Context::$user_conf->old_entries, 1); - $date_min = time() - (3600 * 24 * 30 * $nb_month_old); - // WebSub (PubSubHubbub) support $pubsubhubbubEnabledGeneral = FreshRSS_Context::$system_conf->pubsubhubbub_enabled; $pshbMinAge = time() - (3600 * 24); //TODO: Make a configuration. @@ -323,12 +319,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { continue; } - $feed_history = $feed->keepHistory(); - if ($isNewFeed) { - $feed_history = FreshRSS_Feed::KEEP_HISTORY_INFINITE; - } elseif (FreshRSS_Feed::KEEP_HISTORY_DEFAULT === $feed_history) { - $feed_history = FreshRSS_Context::$user_conf->keep_history_default; - } $needFeedCacheRefresh = false; // We want chronological order and SimplePie uses reverse order. @@ -376,15 +366,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $entryDAO->updateEntry($entry->toArray()); } - } elseif ($feed_history == 0 && $entry_date < $date_min) { - // This entry should not be added considering configuration and date. - $oldGuids[] = $entry->guid(); } else { $id = uTimeString(); $entry->_id($id); - if ($entry_date < $date_min) { - $entry->_isRead(true); //Old article that was not in database. Probably an error, so mark as read - } $entry->applyFilterActions(); @@ -413,17 +397,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $entryDAO->updateLastSeen($feed->id(), $oldGuids, $mtime); } - if ($feed_history >= 0 && mt_rand(0, 30) === 1) { - // TODO: move this function in web cron when available (see entry::purge) - // Remove old entries once in 30. + if (mt_rand(0, 30) === 1) { // Remove old entries once in 30. if (!$entryDAO->inTransaction()) { $entryDAO->beginTransaction(); } - - $nb = $entryDAO->cleanOldEntries($feed->id(), $date_min, max($feed_history, count($entries) + 10)); + $nb = $feed->cleanOldEntries(); if ($nb > 0) { $needFeedCacheRefresh = true; - Minz_Log::debug($nb . ' old entries cleaned in feed [' . $feed->url(false) . ']'); } } diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index f6d5e9457..f9497f0be 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -121,6 +121,32 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { $feed->_attributes('timeout', null); } + if (Minz_Request::paramBoolean('use_default_purge_options')) { + $feed->_attributes('archiving', null); + } else { + if (!Minz_Request::paramBoolean('enable_keep_max')) { + $keepMax = false; + } elseif (!$keepMax = Minz_Request::param('keep_max')) { + $keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT; + } + if ($enableRetentionPeriod = Minz_Request::paramBoolean('enable_keep_period')) { + $keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD; + if (is_numeric(Minz_Request::param('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::param('keep_period_unit'))) { + $keepPeriod = str_replace(1, Minz_Request::param('keep_period_count'), Minz_Request::param('keep_period_unit')); + } + } else { + $keepPeriod = false; + } + $feed->_attributes('archiving', [ + 'keep_period' => $keepPeriod, + 'keep_max' => $keepMax, + 'keep_min' => intval(Minz_Request::param('keep_min', 0)), + 'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'), + 'keep_labels' => Minz_Request::paramBoolean('keep_labels'), + 'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'), + ]); + } + $feed->_filtersAction('read', preg_split('/[\n\r]+/', Minz_Request::param('filteractions_read', ''))); $values = array( @@ -132,7 +158,6 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { 'pathEntries' => Minz_Request::param('path_entries', ''), 'priority' => intval(Minz_Request::param('priority', FreshRSS_Feed::PRIORITY_MAIN_STREAM)), 'httpAuth' => $httpAuth, - 'keep_history' => intval(Minz_Request::param('keep_history', FreshRSS_Feed::KEEP_HISTORY_DEFAULT)), 'ttl' => $ttl * ($mute ? -1 : 1), 'attributes' => $feed->attributes(), ); @@ -165,9 +190,36 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { $this->view->category = $category; if (Minz_Request::isPost()) { - $values = array( + if (Minz_Request::paramBoolean('use_default_purge_options')) { + $category->_attributes('archiving', null); + } else { + if (!Minz_Request::paramBoolean('enable_keep_max')) { + $keepMax = false; + } elseif (!$keepMax = Minz_Request::param('keep_max')) { + $keepMax = FreshRSS_Feed::ARCHIVING_RETENTION_COUNT_LIMIT; + } + if ($enableRetentionPeriod = Minz_Request::paramBoolean('enable_keep_period')) { + $keepPeriod = FreshRSS_Feed::ARCHIVING_RETENTION_PERIOD; + if (is_numeric(Minz_Request::param('keep_period_count')) && preg_match('/^PT?1[YMWDH]$/', Minz_Request::param('keep_period_unit'))) { + $keepPeriod = str_replace(1, Minz_Request::param('keep_period_count'), Minz_Request::param('keep_period_unit')); + } + } else { + $keepPeriod = false; + } + $category->_attributes('archiving', [ + 'keep_period' => $keepPeriod, + 'keep_max' => $keepMax, + 'keep_min' => intval(Minz_Request::param('keep_min', 0)), + 'keep_favourites' => Minz_Request::paramBoolean('keep_favourites'), + 'keep_labels' => Minz_Request::paramBoolean('keep_labels'), + 'keep_unreads' => Minz_Request::paramBoolean('keep_unreads'), + ]); + } + + $values = [ 'name' => Minz_Request::param('name', ''), - ); + 'attributes' => $category->attributes(), + ]; invalidateHttpCache(); diff --git a/app/Models/Category.php b/app/Models/Category.php index 29c0e586b..a0ee1ddaa 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -8,6 +8,7 @@ class FreshRSS_Category extends Minz_Model { private $feeds = null; private $hasFeedsWithError = false; private $isDefault = false; + private $attributes = []; public function __construct($name = '', $feeds = null) { $this->_name($name); @@ -68,6 +69,14 @@ class FreshRSS_Category extends Minz_Model { return $this->hasFeedsWithError; } + public function attributes($key = '') { + if ($key == '') { + return $this->attributes; + } else { + return isset($this->attributes[$key]) ? $this->attributes[$key] : null; + } + } + public function _id($id) { $this->id = $id; if ($id == FreshRSS_CategoryDAO::DEFAULTCATEGORYID) { @@ -87,4 +96,19 @@ class FreshRSS_Category extends Minz_Model { $this->feeds = $values; } + + public function _attributes($key, $value) { + if ($key == '') { + if (is_string($value)) { + $value = json_decode($value, true); + } + if (is_array($value)) { + $this->attributes = $value; + } + } elseif ($value === null) { + unset($this->attributes[$key]); + } else { + $this->attributes[$key] = $value; + } + } } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index dd49b542d..1b8717e83 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -4,15 +4,81 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable const DEFAULTCATEGORYID = 1; + protected function addColumn($name) { + Minz_Log::warning(__method__ . ': ' . $name); + try { + if ('attributes' === $name) { //v1.15.0 + $ok = $this->pdo->exec('ALTER TABLE `_category` ADD COLUMN attributes TEXT') !== false; + + $stm = $this->pdo->query('SELECT * FROM `_feed`'); + $feeds = $stm->fetchAll(PDO::FETCH_ASSOC); + + $stm = $this->pdo->prepare('UPDATE `_feed` SET attributes = :attributes WHERE id = :id'); + foreach ($feeds as $feed) { + if (empty($feed['keep_history']) || empty($feed['id'])) { + continue; + } + $keepHistory = $feed['keep_history']; + $attributes = empty($feed['attributes']) ? [] : json_decode($feed['attributes'], true); + if (is_string($attributes)) { //Legacy risk of double-encoding + $attributes = json_decode($attributes, true); + } + if (!is_array($attributes)) { + $attributes = []; + } + if ($keepHistory > 0) { + $attributes['archiving']['keep_min'] = intval($keepHistory); + } elseif ($keepHistory == -1) { //Infinite + $attributes['archiving']['keep_period'] = false; + $attributes['archiving']['keep_max'] = false; + $attributes['archiving']['keep_min'] = false; + } else { + continue; + } + $stm->bindValue(':id', $feed['id'], PDO::PARAM_INT); + $stm->bindValue(':attributes', json_encode($attributes, JSON_UNESCAPED_SLASHES)); + $stm->execute(); + } + + if ($this->pdo->dbType() !== 'sqlite') { //SQLite does not support DROP COLUMN + $this->pdo->exec('ALTER TABLE `_feed` DROP COLUMN keep_history'); + } else { + $this->pdo->exec('DROP INDEX IF EXISTS feed_keep_history_index'); //SQLite at least drop index + } + return $ok; + } + } catch (Exception $e) { + Minz_Log::error(__method__ . ': ' . $e->getMessage()); + } + return false; + } + + protected function autoUpdateDb($errorInfo) { + if (isset($errorInfo[0])) { + if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) { + foreach (['attributes'] as $column) { + if (stripos($errorInfo[2], $column) !== false) { + return $this->addColumn($column); + } + } + } + } + return false; + } + public function addCategory($valuesTmp) { - $sql = 'INSERT INTO `_category`(name) ' - . 'SELECT * FROM (SELECT TRIM(?)) c2 ' //TRIM() to provide a type hint as text for PostgreSQL + $sql = 'INSERT INTO `_category`(name, attributes) ' + . 'SELECT * FROM (SELECT TRIM(?), ?) c2 ' //TRIM() to provide a type hint as text for PostgreSQL . 'WHERE NOT EXISTS (SELECT 1 FROM `_tag` WHERE name = TRIM(?))'; //No tag of the same name $stm = $this->pdo->prepare($sql); $valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8'); + if (!isset($valuesTmp['attributes'])) { + $valuesTmp['attributes'] = []; + } $values = array( $valuesTmp['name'], + is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES), $valuesTmp['name'], ); @@ -20,7 +86,10 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable return $this->pdo->lastInsertId('`_category_id_seq`'); } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); - Minz_Log::error('SQL error addCategory: ' . $info[2]); + if ($this->autoUpdateDb($info)) { + return $this->addCategory($valuesTmp); + } + Minz_Log::error('SQL error addCategory: ' . json_encode($info)); return false; } } @@ -39,13 +108,17 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function updateCategory($id, $valuesTmp) { - $sql = 'UPDATE `_category` SET name=? WHERE id=? ' + $sql = 'UPDATE `_category` SET name=?, attributes=? WHERE id=? ' . 'AND NOT EXISTS (SELECT 1 FROM `_tag` WHERE name = ?)'; //No tag of the same name $stm = $this->pdo->prepare($sql); $valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE, 'UTF-8'); + if (!isset($valuesTmp['attributes'])) { + $valuesTmp['attributes'] = []; + } $values = array( $valuesTmp['name'], + is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES), $id, $valuesTmp['name'], ); @@ -54,7 +127,10 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable return $stm->rowCount(); } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); - Minz_Log::error('SQL error updateCategory: ' . $info[2]); + if ($this->autoUpdateDb($info)) { + return $this->updateCategory($valuesTmp); + } + Minz_Log::error('SQL error updateCategory: ' . json_encode($info)); return false; } } @@ -70,16 +146,27 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable return $stm->rowCount(); } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); - Minz_Log::error('SQL error deleteCategory: ' . $info[2]); + Minz_Log::error('SQL error deleteCategory: ' . json_encode($info)); return false; } } public function selectAll() { - $sql = 'SELECT id, name FROM `_category`'; + $sql = 'SELECT id, name, attributes FROM `_category`'; $stm = $this->pdo->query($sql); - while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { - yield $row; + if ($stm != false) { + while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + yield $row; + } + } else { + $info = $this->pdo->errorInfo(); + if ($this->autoUpdateDb($info)) { + foreach ($this->selectAll() as $category) { // `yield from` requires PHP 7+ + yield $category; + } + } + Minz_Log::error(__method__ . ' error: ' . json_encode($info)); + return false; } } @@ -116,7 +203,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable public function listCategories($prePopulateFeeds = true, $details = false) { if ($prePopulateFeeds) { - $sql = 'SELECT c.id AS c_id, c.name AS c_name, ' + $sql = 'SELECT c.id AS c_id, c.name AS c_name, c.attributes AS c_attributes, ' . ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.`cache_nbEntries`, f.`cache_nbUnreads`, f.ttl ') . 'FROM `_category` c ' . 'LEFT OUTER JOIN `_feed` f ON f.category=c.id ' @@ -124,9 +211,17 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable . 'GROUP BY f.id, c_id ' . 'ORDER BY c.name, f.name'; $stm = $this->pdo->prepare($sql); - $stm->bindValue(':priority_normal', FreshRSS_Feed::PRIORITY_NORMAL, PDO::PARAM_INT); - $stm->execute(); - return self::daoToCategoryPrepopulated($stm->fetchAll(PDO::FETCH_ASSOC)); + $values = [ ':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL ]; + if ($stm && $stm->execute($values)) { + return self::daoToCategoryPrepopulated($stm->fetchAll(PDO::FETCH_ASSOC)); + } else { + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); + if ($this->autoUpdateDb($info)) { + return $this->listCategories($prePopulateFeeds, $details); + } + Minz_Log::error('SQL error listCategories: ' . json_encode($info)); + return false; + } } else { $sql = 'SELECT * FROM `_category` ORDER BY name'; $stm = $this->pdo->query($sql); @@ -282,6 +377,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable $dao['name'] ); $cat->_id($dao['id']); + $cat->_attributes('', isset($dao['attributes']) ? $dao['attributes'] : ''); $cat->_isDefault(static::DEFAULTCATEGORYID === intval($dao['id'])); $list[$key] = $cat; } diff --git a/app/Models/CategoryDAOSQLite.php b/app/Models/CategoryDAOSQLite.php new file mode 100644 index 000000000..e32545c90 --- /dev/null +++ b/app/Models/CategoryDAOSQLite.php @@ -0,0 +1,17 @@ +pdo->query("PRAGMA table_info('category')")) { + $columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1); + foreach (['attributes'] as $column) { + if (!in_array($column, $columns)) { + return $this->addColumn($column); + } + } + } + return false; + } + +} diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php index 963d37e2b..b1d271f41 100644 --- a/app/Models/ConfigurationSetter.php +++ b/app/Models/ConfigurationSetter.php @@ -79,11 +79,6 @@ class FreshRSS_ConfigurationSetter { $data['html5_notif_timeout'] = $value >= 0 ? $value : 0; } - private function _keep_history_default(&$data, $value) { - $value = intval($value); - $data['keep_history_default'] = $value >= FreshRSS_Feed::KEEP_HISTORY_INFINITE ? $value : 0; - } - // It works for system config too! private function _language(&$data, $value) { $value = strtolower($value); @@ -94,11 +89,6 @@ class FreshRSS_ConfigurationSetter { $data['language'] = $value; } - private function _old_entries(&$data, $value) { - $value = intval($value); - $data['old_entries'] = $value > 0 ? $value : 3; - } - private function _passwordHash(&$data, $value) { $data['passwordHash'] = ctype_graph($value) && (strlen($value) >= 60) ? $value : ''; } diff --git a/app/Models/Context.php b/app/Models/Context.php index 95dc47c8c..878b72c69 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -51,6 +51,25 @@ class FreshRSS_Context { // Init configuration. self::$system_conf = Minz_Configuration::get('system'); self::$user_conf = Minz_Configuration::get('user'); + + //Legacy + $oldEntries = (int)FreshRSS_Context::$user_conf->param('old_entries', 0); + if ($oldEntries > 0) { //Freshrss < 1.15 + $archiving['keep_period'] = 'P' . $oldEntries . 'M'; + } + + $keepMin = (int)FreshRSS_Context::$user_conf->param('keep_history_default', -5); + if ($keepMin != 0 && $keepMin > -5) { //Freshrss < 1.15 + $archiving = FreshRSS_Context::$user_conf->archiving; + if ($keepMin > 0) { + $archiving['keep_min'] = $keepMin; + } elseif ($keepMin == -1) { //Infinite + $archiving['keep_period'] = false; + $archiving['keep_max'] = false; + $archiving['keep_min'] = false; + } + FreshRSS_Context::$user_conf->archiving = $archiving; + } } /** diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php index f6cd2f756..a36b469b1 100644 --- a/app/Models/DatabaseDAO.php +++ b/app/Models/DatabaseDAO.php @@ -15,11 +15,11 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { const LENGTH_INDEX_UNICODE = 191; public function create() { - require_once(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); $db = FreshRSS_Context::$system_conf->db; try { - $sql = sprintf(SQL_CREATE_DB, empty($db['base']) ? '' : $db['base']); + $sql = sprintf($SQL_CREATE_DB, empty($db['base']) ? '' : $db['base']); return $this->pdo->exec($sql) !== false; } catch (PDOException $e) { $_SESSION['bd_error'] = $e->getMessage(); @@ -86,7 +86,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { public function feedIsCorrect() { return $this->checkTable('feed', array( 'id', 'url', 'category', 'name', 'website', 'description', 'lastUpdate', - 'priority', 'pathEntries', 'httpAuth', 'error', 'keep_history', 'ttl', 'attributes', + 'priority', 'pathEntries', 'httpAuth', 'error', 'ttl', 'attributes', 'cache_nbEntries', 'cache_nbUnreads', )); } @@ -164,11 +164,11 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { public function ensureCaseInsensitiveGuids() { $ok = true; if ($this->pdo->dbType() === 'mysql') { - include_once(APP_PATH . '/SQL/install.sql.mysql.php'); + include(APP_PATH . '/SQL/install.sql.mysql.php'); $ok = false; try { - $ok = $this->pdo->exec(SQL_UPDATE_GUID_LATIN1_BIN) !== false; //FreshRSS 1.12 + $ok = $this->pdo->exec($SQL_UPDATE_GUID_LATIN1_BIN) !== false; //FreshRSS 1.12 } catch (Exception $e) { $ok = false; Minz_Log::error(__METHOD__ . ' error: ' . $e->getMessage()); @@ -243,7 +243,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { Minz_ModelPdo::clean(); $userDAOSQLite = new FreshRSS_UserDAO('', $sqlite); - $categoryDAOSQLite = new FreshRSS_CategoryDAO('', $sqlite); + $categoryDAOSQLite = new FreshRSS_CategoryDAOSQLite('', $sqlite); $feedDAOSQLite = new FreshRSS_FeedDAOSQLite('', $sqlite); $entryDAOSQLite = new FreshRSS_EntryDAOSQLite('', $sqlite); $tagDAOSQLite = new FreshRSS_TagDAOSQLite('', $sqlite); diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php index b1473ab09..413e7ee09 100644 --- a/app/Models/DatabaseDAOSQLite.php +++ b/app/Models/DatabaseDAOSQLite.php @@ -66,6 +66,6 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { } public function optimize() { - return $this->exec('VACUUM') !== false; + return $this->pdo->exec('VACUUM') !== false; } } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 5ff3a5b70..6a8a25b3e 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -26,9 +26,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $this->pdo->commit(); } try { - require_once(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); Minz_Log::warning('SQL CREATE TABLE entrytmp...'); - $ok = $this->pdo->exec(SQL_CREATE_TABLE_ENTRYTMP . SQL_CREATE_INDEX_ENTRY_1) !== false; + $ok = $this->pdo->exec($SQL_CREATE_TABLE_ENTRYTMP . $SQL_CREATE_INDEX_ENTRY_1) !== false; } catch (Exception $ex) { Minz_Log::error(__method__ . ' error: ' . $ex->getMessage()); } @@ -544,32 +544,57 @@ SQL; return $affected; } - public function cleanOldEntries($id_feed, $date_min, $keep = 15) { //Remember to call updateCachedValue($id_feed) or updateCachedValues() just after - $sql = 'DELETE FROM `_entry` ' - . 'WHERE id_feed=:id_feed1 AND id<=:id_max ' - . 'AND is_favorite=0 ' //Do not remove favourites - . 'AND `lastSeen` < (SELECT maxLastSeen FROM (SELECT (MAX(e3.`lastSeen`)-99) AS maxLastSeen FROM `_entry` e3 WHERE e3.id_feed=:id_feed2) recent) ' //Do not remove the most newly seen articles, plus a few seconds of tolerance - . 'AND id NOT IN (SELECT id_entry FROM `_entrytag`) ' //Do not purge tagged entries - . 'AND id NOT IN (SELECT id FROM (SELECT e2.id FROM `_entry` e2 WHERE e2.id_feed=:id_feed3 ORDER BY id DESC LIMIT :keep) keep)'; //Double select: MySQL doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery' - $stm = $this->pdo->prepare($sql); + public function cleanOldEntries($id_feed, $options = []) { //Remember to call updateCachedValue($id_feed) or updateCachedValues() just after + $sql = 'DELETE FROM `_entry` WHERE id_feed = :id_feed1'; //No alias for MySQL / MariaDB + $params = []; + $params[':id_feed1'] = $id_feed; - if ($stm) { - $id_max = intval($date_min) . '000000'; - $stm->bindParam(':id_max', $id_max, PDO::PARAM_STR); - $stm->bindParam(':id_feed1', $id_feed, PDO::PARAM_INT); - $stm->bindParam(':id_feed2', $id_feed, PDO::PARAM_INT); - $stm->bindParam(':id_feed3', $id_feed, PDO::PARAM_INT); - $stm->bindParam(':keep', $keep, PDO::PARAM_INT); + //==Exclusions== + if (!empty($options['keep_favourites'])) { + $sql .= ' AND is_favorite = 0'; + } + if (!empty($options['keep_unreads'])) { + $sql .= ' AND is_read = 1'; + } + if (!empty($options['keep_labels'])) { + $sql .= ' AND NOT EXISTS (SELECT 1 FROM `_entrytag` WHERE id_entry = id)'; + } + if (!empty($options['keep_min']) && $options['keep_min'] > 0) { + $sql .= ' AND `lastSeen` < (SELECT e2.`lastSeen` FROM `_entry` e2 WHERE e2.id_feed = :id_feed2' + . ' ORDER BY e2.`lastSeen` DESC LIMIT 1 OFFSET :keep_min)'; + $params[':id_feed2'] = $id_feed; + $params[':keep_min'] = (int)$options['keep_min']; } + //Keep at least the articles seen at the last refresh + $sql .= ' AND `lastSeen` < (SELECT MAX(e3.`lastSeen`) FROM `_entry` e3 WHERE e3.id_feed = :id_feed3)'; + $params[':id_feed3'] = $id_feed; + + //==Inclusions== + $sql .= ' AND (1=0'; + if (!empty($options['keep_period'])) { + $sql .= ' OR `lastSeen` < :max_last_seen'; + $now = new DateTime('now'); + $now->sub(new DateInterval($options['keep_period'])); + $params[':max_last_seen'] = $now->format('U'); + } + if (!empty($options['keep_max']) && $options['keep_max'] > 0) { + $sql .= ' OR `lastSeen` <= (SELECT e4.`lastSeen` FROM `_entry` e4 WHERE e4.id_feed = :id_feed4' + . ' ORDER BY e4.`lastSeen` DESC LIMIT 1 OFFSET :keep_max)'; + $params[':id_feed4'] = $id_feed; + $params[':keep_max'] = (int)$options['keep_max']; + } + $sql .= ')'; + + $stm = $this->pdo->prepare($sql); - if ($stm && $stm->execute()) { + if ($stm && $stm->execute($params)) { return $stm->rowCount(); } else { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { - return $this->cleanOldEntries($id_feed, $date_min, $keep); + return $this->cleanOldEntries($id_feed, $options); } - Minz_Log::error('SQL error cleanOldEntries: ' . $info[2]); + Minz_Log::error(__method__ . ' error:' . json_encode($info)); return false; } } diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 6f2ca2217..69885c205 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -7,7 +7,13 @@ class FreshRSS_Factory { } public static function createCategoryDao($username = null) { - return new FreshRSS_CategoryDAO($username); + $conf = Minz_Configuration::get('system'); + switch ($conf->db['type']) { + case 'sqlite': + return new FreshRSS_CategoryDAOSQLite($username); + default: + return new FreshRSS_CategoryDAO($username); + } } public static function createFeedDao($username = null) { diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 8aee9d62f..0a45a1f4c 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -7,8 +7,8 @@ class FreshRSS_Feed extends Minz_Model { const TTL_DEFAULT = 0; - const KEEP_HISTORY_DEFAULT = -2; - const KEEP_HISTORY_INFINITE = -1; + const ARCHIVING_RETENTION_COUNT_LIMIT = 10000; + const ARCHIVING_RETENTION_PERIOD = 'P3M'; private $id = 0; private $url; @@ -24,9 +24,8 @@ class FreshRSS_Feed extends Minz_Model { private $pathEntries = ''; private $httpAuth = ''; private $error = false; - private $keep_history = self::KEEP_HISTORY_DEFAULT; private $ttl = self::TTL_DEFAULT; - private $attributes = array(); + private $attributes = []; private $mute = false; private $hash = null; private $lockPath = ''; @@ -110,9 +109,6 @@ class FreshRSS_Feed extends Minz_Model { public function inError() { return $this->error; } - public function keepHistory() { - return $this->keep_history; - } public function ttl() { return $this->ttl; } @@ -230,12 +226,6 @@ class FreshRSS_Feed extends Minz_Model { public function _error($value) { $this->error = (bool)$value; } - public function _keepHistory($value) { - $value = intval($value); - $value = min($value, 1000000); - $value = max($value, self::KEEP_HISTORY_DEFAULT); - $this->keep_history = $value; - } public function _ttl($value) { $value = intval($value); $value = min($value, 100000000); @@ -469,6 +459,28 @@ class FreshRSS_Feed extends Minz_Model { $this->entries = $entries; } + public function cleanOldEntries() { //Remember to call updateCachedValue($id_feed) or updateCachedValues() just after + $archiving = $this->attributes('archiving'); + if ($archiving == null) { + $catDAO = FreshRSS_Factory::createCategoryDao(); + $category = $catDAO->searchById($this->category()); + $archiving = $category == null ? null : $category->attributes('archiving'); + if ($archiving == null) { + $archiving = FreshRSS_Context::$user_conf->archiving; + } + } + if (is_array($archiving)) { + $entryDAO = FreshRSS_Factory::createEntryDao(); + $nb = $entryDAO->cleanOldEntries($this->id(), $archiving); + if ($nb > 0) { + $needFeedCacheRefresh = true; + Minz_Log::debug($nb . ' entries cleaned in feed [' . $this->url(false) . '] with: ' . json_encode($archiving)); + } + return $nb; + } + return false; + } + protected function cacheFilename() { return CACHE_PATH . '/' . md5($this->url) . '.spc'; } diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index d4a91c145..fa0001df7 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -17,7 +17,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { protected function autoUpdateDb($errorInfo) { if (isset($errorInfo[0])) { if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR || $errorInfo[0] === FreshRSS_DatabaseDAOPGSQL::UNDEFINED_COLUMN) { - foreach (array('attributes') as $column) { + foreach (['attributes'] as $column) { if (stripos($errorInfo[2], $column) !== false) { return $this->addColumn($column); } @@ -41,12 +41,11 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { `pathEntries`, `httpAuth`, error, - keep_history, ttl, attributes ) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; $stm = $this->pdo->prepare($sql); $valuesTmp['url'] = safe_ascii($valuesTmp['url']); @@ -54,6 +53,9 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if (!isset($valuesTmp['pathEntries'])) { $valuesTmp['pathEntries'] = ''; } + if (!isset($valuesTmp['attributes'])) { + $valuesTmp['attributes'] = []; + } $values = array( substr($valuesTmp['url'], 0, 511), @@ -66,9 +68,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { mb_strcut($valuesTmp['pathEntries'], 0, 511, 'UTF-8'), base64_encode($valuesTmp['httpAuth']), isset($valuesTmp['error']) ? intval($valuesTmp['error']) : 0, - isset($valuesTmp['keep_history']) ? intval($valuesTmp['keep_history']) : FreshRSS_Feed::KEEP_HISTORY_DEFAULT, isset($valuesTmp['ttl']) ? intval($valuesTmp['ttl']) : FreshRSS_Feed::TTL_DEFAULT, - isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '', + is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES), ); if ($stm && $stm->execute($values)) { @@ -135,7 +136,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($key === 'httpAuth') { $valuesTmp[$key] = base64_encode($v); } elseif ($key === 'attributes') { - $valuesTmp[$key] = json_encode($v); + $valuesTmp[$key] = is_string($valuesTmp[$key]) ? $valuesTmp[$key] : json_encode($valuesTmp[$key], JSON_UNESCAPED_SLASHES); } } $set = substr($set, 0, -2); @@ -246,7 +247,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function selectAll() { $sql = 'SELECT id, url, category, name, website, description, `lastUpdate`, priority, ' - . '`pathEntries`, `httpAuth`, error, keep_history, ttl, attributes ' + . '`pathEntries`, `httpAuth`, error, ttl, attributes ' . 'FROM `_feed`'; $stm = $this->pdo->query($sql); while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { @@ -319,7 +320,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { */ public function listFeedsOrderUpdate($defaultCacheDuration = 3600, $limit = 0) { $this->updateTTL(); - $sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl, attributes ' + $sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes ' . 'FROM `_feed` ' . ($defaultCacheDuration < 0 ? '' : 'WHERE ttl >= ' . FreshRSS_Feed::TTL_DEFAULT . ' AND `lastUpdate` < (' . (time() + 60) @@ -407,7 +408,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'SET `cache_nbEntries`=0, `cache_nbUnreads`=0 WHERE id=:id'; $stm = $this->pdo->prepare($sql); $stm->bindParam(':id', $id, PDO::PARAM_INT); - if (!($stm && $stm->execute($values))) { + if (!($stm && $stm->execute())) { $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error truncate: ' . $info[2]); $this->pdo->rollBack(); @@ -448,7 +449,6 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $myFeed->_pathEntries(isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode($dao['httpAuth']) : ''); $myFeed->_error(isset($dao['error']) ? $dao['error'] : 0); - $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : FreshRSS_Feed::KEEP_HISTORY_DEFAULT); $myFeed->_ttl(isset($dao['ttl']) ? $dao['ttl'] : FreshRSS_Feed::TTL_DEFAULT); $myFeed->_attributes('', isset($dao['attributes']) ? $dao['attributes'] : ''); $myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0); diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php index c56447df6..0f685867a 100644 --- a/app/Models/FeedDAOSQLite.php +++ b/app/Models/FeedDAOSQLite.php @@ -5,7 +5,7 @@ class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO { protected function autoUpdateDb($errorInfo) { if ($tableInfo = $this->pdo->query("PRAGMA table_info('feed')")) { $columns = $tableInfo->fetchAll(PDO::FETCH_COLUMN, 1); - foreach (array('attributes') as $column) { + foreach (['attributes'] as $column) { if (!in_array($column, $columns)) { return $this->addColumn($column); } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 3eb989cc1..0d50e356c 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -3,7 +3,7 @@ class FreshRSS_Tag extends Minz_Model { private $id = 0; private $name; - private $attributes = array(); + private $attributes = []; private $nbEntries = -1; private $nbUnread = -1; diff --git a/app/Models/TagDAO.php b/app/Models/TagDAO.php index 9c0f591c9..5882eee76 100644 --- a/app/Models/TagDAO.php +++ b/app/Models/TagDAO.php @@ -13,14 +13,14 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $this->pdo->commit(); } try { - require_once(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); Minz_Log::warning('SQL ALTER GUID case sensitivity...'); $databaseDAO = FreshRSS_Factory::createDatabaseDAO(); $databaseDAO->ensureCaseInsensitiveGuids(); Minz_Log::warning('SQL CREATE TABLE tag...'); - $ok = $this->pdo->exec(SQL_CREATE_TABLE_TAGS) !== false; + $ok = $this->pdo->exec($SQL_CREATE_TABLE_TAGS) !== false; } catch (Exception $e) { Minz_Log::error('FreshRSS_EntryDAO::createTagTable error: ' . $e->getMessage()); } @@ -48,9 +48,12 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $stm = $this->pdo->prepare($sql); $valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, 63, 'UTF-8'); + if (!isset($valuesTmp['attributes'])) { + $valuesTmp['attributes'] = []; + } $values = array( $valuesTmp['name'], - isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '', + is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES), $valuesTmp['name'], ); @@ -81,9 +84,12 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $stm = $this->pdo->prepare($sql); $valuesTmp['name'] = mb_strcut(trim($valuesTmp['name']), 0, 63, 'UTF-8'); + if (!isset($valuesTmp['attributes'])) { + $valuesTmp['attributes'] = []; + } $values = array( $valuesTmp['name'], - isset($valuesTmp['attributes']) ? json_encode($valuesTmp['attributes']) : '', + is_string($valuesTmp['attributes']) ? $valuesTmp['attributes'] : json_encode($valuesTmp['attributes'], JSON_UNESCAPED_SLASHES), $id, $valuesTmp['name'], ); diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 8e7e977d0..4e824cf01 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -2,14 +2,14 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { public function createUser($insertDefaultFeeds = false) { - require_once(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); try { - $sql = SQL_CREATE_TABLES . SQL_CREATE_TABLE_ENTRYTMP . SQL_CREATE_TABLE_TAGS; + $sql = $SQL_CREATE_TABLES . $SQL_CREATE_TABLE_ENTRYTMP . $SQL_CREATE_TABLE_TAGS; $ok = $this->pdo->exec($sql) !== false; //Note: Only exec() can take multiple statements safely. if ($ok && $insertDefaultFeeds) { $default_feeds = FreshRSS_Context::$system_conf->default_feeds; - $stm = $this->pdo->prepare(SQL_INSERT_FEED); + $stm = $this->pdo->prepare($SQL_INSERT_FEED); foreach ($default_feeds as $feed) { $parameters = [ ':url' => $feed['url'], @@ -38,9 +38,8 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { fwrite(STDERR, 'Deleting SQL data for user “' . $this->current_user . "”…\n"); } - require_once(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); - - $ok = $this->pdo->exec(SQL_DROP_TABLES) !== false; + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); + $ok = $this->pdo->exec($SQL_DROP_TABLES) !== false; if ($ok) { return true; diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index 87b5d1989..1eabfae8b 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -1,12 +1,13 @@ array( '_' => 'Archivace', - 'advanced' => 'Pokročilé', 'delete_after' => 'Smazat články starší než', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Více možností je dostupných v nastavení jednotlivých kanálů', - 'keep_history_by_feed' => 'Zachovat tento minimální počet článků v každém kanálu', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Zachovat tento minimální počet článků v každém kanálu', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Optimalizovat databázi', 'optimize_help' => 'Občasná údržba zmenší velikost databáze', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Vyčistit nyní', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Archivace', 'ttl' => 'Neaktualizovat častěji než', ), diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php index c6dabd555..de1456187 100644 --- a/app/i18n/cz/gen.php +++ b/app/i18n/cz/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Žádné nové články', 'previous' => 'Předchozí', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index b2bdf416b..eaaff9acd 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Kategorie', 'add' => 'Přidat kategorii', + 'archiving' => 'Archivace', 'empty' => 'Vyprázdit kategorii', 'information' => 'Informace', 'new' => 'Nová kategorie', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Informace', - 'keep_history' => 'Zachovat tento minimální počet článků', + 'keep_min' => 'Zachovat tento minimální počet článků', 'moved_category_deleted' => 'Po smazání kategorie budou v ní obsažené kanály automaticky přesunuty do %s.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Nejsou označeny žádné kanály.', diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index 99225da9c..89bbfc10e 100644 --- a/app/i18n/de/conf.php +++ b/app/i18n/de/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Archivierung', - 'advanced' => 'Erweitert', 'delete_after' => 'Entferne Artikel nach', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Weitere Optionen sind in den Einstellungen der individuellen Feeds verfügbar.', - 'keep_history_by_feed' => 'Minimale Anzahl an Artikeln, die pro Feed behalten werden', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Minimale Anzahl an Artikeln, die pro Feed behalten werden', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Datenbank optimieren', 'optimize_help' => 'Sollte gelegentlich durchgeführt werden, um die Größe der Datenbank zu reduzieren.', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Jetzt bereinigen', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Archivierung', 'ttl' => 'Aktualisiere automatisch nicht öfter als', ), diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php index 6cc791d5e..e2dd2a251 100644 --- a/app/i18n/de/gen.php +++ b/app/i18n/de/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Es gibt keine weiteren Artikel', 'previous' => 'Vorherige', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index abc01b954..1227b5559 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Kategorie', 'add' => 'Eine Kategorie hinzufügen', + 'archiving' => 'Archivierung', 'empty' => 'Leere Kategorie', 'information' => 'Information', 'new' => 'Neue Kategorie', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Information', - 'keep_history' => 'Minimale Anzahl an Artikeln, die behalten wird', + 'keep_min' => 'Minimale Anzahl an Artikeln, die behalten wird', 'moved_category_deleted' => 'Wenn Sie eine Kategorie entfernen, werden deren Feeds automatisch in die Kategorie %s eingefügt.', 'mute' => 'Stumm schalten', 'no_selected' => 'Kein Feed ausgewählt.', diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index 1078c736c..2d4e06550 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Archiving', - 'advanced' => 'Advanced', 'delete_after' => 'Remove articles after', + 'exception' => 'Purge exception', 'help' => 'More options are available in the individual feed settings', - 'keep_history_by_feed' => 'Minimum number of articles to keep by feed', + 'keep_favourites' => 'Never delete favourites', + 'keep_min_by_feed' => 'Minimum number of articles to keep by feed', + 'keep_labels' => 'Never delete labels', + 'keep_unreads' => 'Never delete unreads', + 'maintenance' => 'Maintenance', 'optimize' => 'Optimise database', 'optimize_help' => 'Do occasionally to reduce the size of the database', + 'policy' => 'Purge policy', + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', 'purge_now' => 'Purge now', + 'keep_max' => 'Maximum number of articles to keep', + 'keep_period' => 'Maximum age of articles to keep', 'title' => 'Archiving', 'ttl' => 'Do not automatically refresh more often than', ), diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index a6ddcbb60..fc1bd587a 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -163,6 +163,13 @@ return array( 'nothing_to_load' => 'There are no more articles', 'previous' => 'Previous', ), + 'period' => array( + 'days' => 'days', + 'hours' => 'hours', + 'months' => 'months', + 'weeks' => 'weeks', + 'years' => 'years', + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index fde01f9df..04ca793ec 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Category', 'add' => 'Add a category', + 'archiving' => 'Archiving', 'empty' => 'Empty category', 'information' => 'Information', 'new' => 'New category', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', ), 'information' => 'Information', - 'keep_history' => 'Minimum number of articles to keep', + 'keep_min' => 'Minimum number of articles to keep', 'moved_category_deleted' => 'When you delete a category, its feeds are automatically classified under %s.', 'mute' => 'mute', 'no_selected' => 'No feed selected.', diff --git a/app/i18n/es/conf.php b/app/i18n/es/conf.php index 6aaad8d13..7a93a87de 100755 --- a/app/i18n/es/conf.php +++ b/app/i18n/es/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Archivo', - 'advanced' => 'Avanzado', 'delete_after' => 'Eliminar artículos tras', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Hay más opciones disponibles en los ajustes de la fuente', - 'keep_history_by_feed' => 'Número mínimo de artículos a conservar por fuente', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Número mínimo de artículos a conservar por fuente', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Optimizar la base de datos', 'optimize_help' => 'Ejecuta la optimización de vez en cuando para reducir el tamaño de la base de datos', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Limpiar ahora', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Archivo', 'ttl' => 'No actualizar automáticamente más de', ), diff --git a/app/i18n/es/gen.php b/app/i18n/es/gen.php index 4affecc51..538ddc8fe 100755 --- a/app/i18n/es/gen.php +++ b/app/i18n/es/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'No hay más artículos', 'previous' => 'Anterior', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php index 7d33c59fa..96be76c6c 100755 --- a/app/i18n/es/sub.php +++ b/app/i18n/es/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Categoría', 'add' => 'Añadir a la categoría', + 'archiving' => 'Archivo', 'empty' => 'Vaciar categoría', 'information' => 'Información', 'new' => 'Nueva categoría', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Información', - 'keep_history' => 'Número mínimo de artículos a conservar', + 'keep_min' => 'Número mínimo de artículos a conservar', 'moved_category_deleted' => 'Al borrar una categoría todas sus fuentes pasan automáticamente a la categoría %s.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'No hay funentes seleccionadas.', diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index dcd623b5a..020c94085 100644 --- a/app/i18n/fr/conf.php +++ b/app/i18n/fr/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Archivage', - 'advanced' => 'Avancé', 'delete_after' => 'Supprimer les articles après', + 'exception' => 'Exception de nettoyage', 'help' => 'D’autres options sont disponibles dans la configuration individuelle des flux.', - 'keep_history_by_feed' => 'Nombre minimum d’articles à conserver par flux', + 'keep_favourites' => 'Ne jamais supprimer les articles favoris', + 'keep_min_by_feed' => 'Nombre minimum d’articles à conserver par flux', + 'keep_labels' => 'Ne jamais supprimer les articles étiquetés', + 'keep_unreads' => 'Ne jamais supprimer les articles non lus', + 'maintenance' => 'Maintenance', 'optimize' => 'Optimiser la base de données', 'optimize_help' => 'À faire de temps en temps pour réduire la taille de la BDD', + 'policy' => 'Politique de nettoyage', + 'policy_warning' => 'Si aucune politique de nettoyage n’est sélectionnée, tous les articles seront conservés.', 'purge_now' => 'Purger maintenant', + 'keep_max' => 'Nombre maximum d’articles à conserver', + 'keep_period' => 'Âge maximum des articles à conserver', 'title' => 'Archivage', 'ttl' => 'Ne pas automatiquement rafraîchir plus souvent que', ), diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index 01b66d316..a6875dd05 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Fin des articles', 'previous' => 'Précédent', ), + 'period' => array( + 'days' => 'jours', + 'hours' => 'heures', + 'months' => 'mois', + 'weeks' => 'semaines', + 'years' => 'années', + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index df44150c2..d09a19e5a 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Catégorie', 'add' => 'Ajouter une catégorie', + 'archiving' => 'Archivage', 'empty' => 'Catégorie vide', 'information' => 'Informations', 'new' => 'Nouvelle catégorie', @@ -40,7 +41,7 @@ return array( 'help' => 'Écrivez une recherche par ligne.', ), 'information' => 'Informations', - 'keep_history' => 'Nombre minimum d’articles à conserver', + 'keep_min' => 'Nombre minimum d’articles à conserver', 'moved_category_deleted' => 'Lors de la suppression d’une catégorie, ses flux seront automatiquement classés dans %s.', 'mute' => 'muet', 'no_selected' => 'Aucun flux sélectionné.', diff --git a/app/i18n/he/conf.php b/app/i18n/he/conf.php index 7e764b944..b987f21f4 100644 --- a/app/i18n/he/conf.php +++ b/app/i18n/he/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'ארכוב', - 'advanced' => 'מתקדם', 'delete_after' => 'מחיקת מאמרים לאחר', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'אפשרויות נוספות זמינות בזרמים ספציפיים', - 'keep_history_by_feed' => 'Minimum number of articles to keep by feed', //TODO - Translation + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Minimum number of articles to keep by feed', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'מיטוב בסיס הנתונים', 'optimize_help' => 'ביצוע לעיתים קרובות על מנת למטב את בסיס הנתונים', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'ניקוי עכשיו', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'ארכוב', 'ttl' => 'אין לרענן אוטומטית יותר מ', ), diff --git a/app/i18n/he/gen.php b/app/i18n/he/gen.php index 158f11e5b..34e6d77de 100644 --- a/app/i18n/he/gen.php +++ b/app/i18n/he/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'אין מאמרים נוספים', 'previous' => 'הקודם', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php index 8a629defb..15965d9e2 100644 --- a/app/i18n/he/sub.php +++ b/app/i18n/he/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'קטגוריה', 'add' => 'הוספת קטגוריה', + 'archiving' => 'ארכוב', 'empty' => 'Empty category', //TODO - Translation 'information' => 'מידע', 'new' => 'קטגוריה חדשה', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'מידע', - 'keep_history' => 'מסםר מינימלי של מאמרים לשמור', + 'keep_min' => 'מסםר מינימלי של מאמרים לשמור', 'moved_category_deleted' => 'כאשר הקטגוריה נמחקת ההזנות שבתוכה אוטומטית מקוטלגות תחת %s.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'אף הזנה לא נבחרה.', diff --git a/app/i18n/it/conf.php b/app/i18n/it/conf.php index f06302c72..4bdaad33d 100644 --- a/app/i18n/it/conf.php +++ b/app/i18n/it/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Archiviazione', - 'advanced' => 'Avanzate', 'delete_after' => 'Rimuovi articoli dopo', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Altre opzioni sono disponibili nelle impostazioni dei singoli feed', - 'keep_history_by_feed' => 'Numero minimo di articoli da mantenere per feed', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Numero minimo di articoli da mantenere per feed', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Ottimizza database', 'optimize_help' => 'Da fare occasionalmente per ridurre le dimensioni del database', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Cancella ora', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Archiviazione', 'ttl' => 'Non effettuare aggiornamenti per più di', ), diff --git a/app/i18n/it/gen.php b/app/i18n/it/gen.php index 604cc6941..50d4b4e6c 100644 --- a/app/i18n/it/gen.php +++ b/app/i18n/it/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Non ci sono altri articoli', 'previous' => 'Precedente', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index 50738d9e3..22cd36986 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Categoria', 'add' => 'Aggiungi una categoria', + 'archiving' => 'Archiviazione', 'empty' => 'Categoria vuota', 'information' => 'Informazioni', 'new' => 'Nuova categoria', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Informazioni', - 'keep_history' => 'Numero minimo di articoli da mantenere', + 'keep_min' => 'Numero minimo di articoli da mantenere', 'moved_category_deleted' => 'Cancellando una categoria i feed al suo interno verranno classificati automaticamente come %s.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Nessun feed selezionato.', diff --git a/app/i18n/kr/conf.php b/app/i18n/kr/conf.php index 397d57418..1e77d0098 100644 --- a/app/i18n/kr/conf.php +++ b/app/i18n/kr/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => '보관', - 'advanced' => '고급 설정', 'delete_after' => '다음 기간보다 오래된 글 삭제', + 'exception' => 'Purge exception', //TODO - Translation 'help' => '더 자세한 옵션은 개별 피드 설정에 있습니다', - 'keep_history_by_feed' => '피드별 최소 유지 글 개수', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => '피드별 최소 유지 글 개수', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => '데이터베이스 최적화', 'optimize_help' => '데이터베이스 크기를 줄이기 위해 가끔씩 수행해주세요', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => '지금 삭제', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => '보관', 'ttl' => '다음 시간이 지나기 전에 새로고침 금지', ), diff --git a/app/i18n/kr/gen.php b/app/i18n/kr/gen.php index 55fea3d66..fdc95d431 100644 --- a/app/i18n/kr/gen.php +++ b/app/i18n/kr/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => '더 이상 글이 없습니다', 'previous' => '이전', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/kr/sub.php b/app/i18n/kr/sub.php index f8eccfa27..2586395f2 100644 --- a/app/i18n/kr/sub.php +++ b/app/i18n/kr/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => '카테고리', 'add' => '카테고리 추가', + 'archiving' => '보관', 'empty' => '빈 카테고리', 'information' => '정보', 'new' => '새 카테고리', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => '정보', - 'keep_history' => '최소 유지 글 개수', + 'keep_min' => '최소 유지 글 개수', 'moved_category_deleted' => '카테고리를 삭제하면, 해당 카테고리 아래에 있던 피드들은 자동적으로 %s 아래로 분류됩니다.', 'mute' => '무기한 새로고침 금지', 'no_selected' => '선택된 피드가 없습니다.', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index ec219d051..22302ccc0 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -1,15 +1,23 @@ array( '_' => 'Archivering', - 'advanced' => 'Geavanceerd', 'delete_after' => 'Verwijder artikelen na', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Meer opties zijn beschikbaar in de persoonlijke stroom instellingen', - 'keep_history_by_feed' => 'Minimum aantal te behouden artikelen in de feed', - 'optimize' => 'Optimaliseer database', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Minimum aantal te behouden artikelen in de feed', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation + 'optimize' => 'Optimaliseer database', //TODO - Translation 'optimize_help' => 'Doe dit zo af en toe om de omvang van de database te verkleinen', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Schoon nu op', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Archivering', 'ttl' => 'Vernieuw niet automatisch meer dan', ), diff --git a/app/i18n/nl/gen.php b/app/i18n/nl/gen.php index 0dcb3010a..4854e806e 100644 --- a/app/i18n/nl/gen.php +++ b/app/i18n/nl/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Er zijn geen artikelen meer', 'previous' => 'Vorige', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'email' => 'Email', 'Known' => 'Known-gebaseerde sites', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 8ceb5aa28..6b498132f 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Categorie', 'add' => 'Voeg categorie toe', + 'archiving' => 'Archiveren', 'empty' => 'Lege categorie', 'information' => 'Informatie', 'new' => 'Nieuwe categorie', @@ -40,7 +41,7 @@ return array( 'help' => 'Voer één zoekfilter per lijn in.', ), 'information' => 'Informatie', - 'keep_history' => 'Minimum aantal artikelen om te houden', + 'keep_min' => 'Minimum aantal artikelen om te houden', 'moved_category_deleted' => 'Als u een categorie verwijderd, worden de feeds automatisch geclassificeerd onder %s.', 'mute' => 'demp', 'no_selected' => 'Geen feed geselecteerd.', diff --git a/app/i18n/oc/conf.php b/app/i18n/oc/conf.php index 76c41911e..e8de3b089 100644 --- a/app/i18n/oc/conf.php +++ b/app/i18n/oc/conf.php @@ -5,11 +5,20 @@ return array( '_' => 'Archius', 'advanced' => 'Avançat', 'delete_after' => 'Levar los articles aprèp', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Mai d’opcions son disponiblas dins la configuracion individuala dels fluxes', - 'keep_history_by_feed' => 'Nombre minimum d’articles de servar per flux', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Nombre minimum d’articles de servar per flux', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Optimizar la basa de donada', 'optimize_help' => 'De far de temps en temps per redusir la talha de la basa de donadas', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Purgar ara', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Archius', 'ttl' => 'Actualizar pas automaticament mai sovent que', ), diff --git a/app/i18n/oc/gen.php b/app/i18n/oc/gen.php index 7ab56368f..928377997 100644 --- a/app/i18n/oc/gen.php +++ b/app/i18n/oc/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'I a pas mai d’articles', 'previous' => 'Precedent', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/oc/sub.php b/app/i18n/oc/sub.php index eae9dff29..0f465d7ca 100644 --- a/app/i18n/oc/sub.php +++ b/app/i18n/oc/sub.php @@ -12,6 +12,7 @@ return array( 'category' => array( '_' => 'Categoria', 'add' => 'Ajustar una categoria', + 'archiving' => 'Archivar', 'empty' => 'Categoria voida', 'information' => 'Informacions', 'new' => 'Nòva categoria', @@ -39,7 +40,7 @@ return array( 'help' => 'Escrivètz una recèrca per linha.', ), 'information' => 'Informacions', - 'keep_history' => 'Nombre minimum d’articles de servar', + 'keep_min' => 'Nombre minimum d’articles de servar', 'moved_category_deleted' => 'Quand escafatz una categoria, sos fluxes son automaticament classats dins %s.', 'mute' => 'mut', 'no_selected' => 'Cap de flux pas seleccionat.', diff --git a/app/i18n/pt-br/conf.php b/app/i18n/pt-br/conf.php index eb067e58a..5e43cc373 100644 --- a/app/i18n/pt-br/conf.php +++ b/app/i18n/pt-br/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Arquivar', - 'advanced' => 'Avançado', 'delete_after' => 'Remover artigos depois', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Mais opções estão disponíveis nas configurações individuais do feed', - 'keep_history_by_feed' => 'Número mínimo de artigos para deixar no feed', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Número mínimo de artigos para deixar no feed', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Otimizar banco de dados', 'optimize_help' => 'Faça ocasionalmente para reduzir o tamanho do banco de dados', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Purge agora', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Arquivar', 'ttl' => 'Não atualize automaticamente mais frequente que', ), diff --git a/app/i18n/pt-br/gen.php b/app/i18n/pt-br/gen.php index b327937d5..0e7f367ee 100644 --- a/app/i18n/pt-br/gen.php +++ b/app/i18n/pt-br/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Não há mais artigos', 'previous' => 'Anterior', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php index d4bea33c4..c4c28bd6c 100644 --- a/app/i18n/pt-br/sub.php +++ b/app/i18n/pt-br/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Categoria', 'add' => 'Adicionar uma categoria', + 'archiving' => 'Arquivar', 'empty' => 'Categoria vazia', 'information' => 'Informações', 'new' => 'Nova categoria', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Informações', - 'keep_history' => 'Número mínimo de artigos para manter', + 'keep_min' => 'Número mínimo de artigos para manter', 'moved_category_deleted' => 'Quando você deleta uma categoria, seus feeds são automaticamente classificados como %s.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Nenhum feed selecionado.', diff --git a/app/i18n/ru/conf.php b/app/i18n/ru/conf.php index af6f3b5f6..7a80587f8 100644 --- a/app/i18n/ru/conf.php +++ b/app/i18n/ru/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Архивация', - 'advanced' => 'Продвинутые настройки', 'delete_after' => 'Удалять статьи после', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Каждую подписку можно настроить более гибко', - 'keep_history_by_feed' => 'Minimum number of articles to keep by feed', //TODO - Translation + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Minimum number of articles to keep by feed', //TODO - Translation + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Оптимизировать базу данных', - 'optimize_help' => 'To do occasionally to reduce the size of the database', //TODO - Translation + 'optimize_help' => 'To do occasionally to reduce the size of the database', //TODO - Translation + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Очистить сейчас', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Архивация', 'ttl' => 'Не обновлять чаще чем', ), diff --git a/app/i18n/ru/gen.php b/app/i18n/ru/gen.php index cc1d7e00e..5200a7005 100644 --- a/app/i18n/ru/gen.php +++ b/app/i18n/ru/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'There are no more articles', //TODO - Translation 'previous' => 'Previous', //TODO - Translation ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index a2c4e4690..f4bda385d 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Category', //TODO - Translation 'add' => 'Add a category', //TODO - Translation + 'archiving' => 'Archivage', //TODO - Translation 'empty' => 'Empty category', //TODO - Translation 'information' => 'Information', //TODO - Translation 'new' => 'New category', //TODO - Translation @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Information', //TODO - Translation - 'keep_history' => 'Minimum number of articles to keep', //TODO - Translation + 'keep_min' => 'Minimum number of articles to keep', //TODO - Translation 'moved_category_deleted' => 'When you delete a category, its feeds are automatically classified under %s.', //TODO - Translation 'mute' => 'mute', //TODO - Translation 'no_selected' => 'No feed selected.', //TODO - Translation diff --git a/app/i18n/sk/conf.php b/app/i18n/sk/conf.php index f704fd4be..2e2289b79 100644 --- a/app/i18n/sk/conf.php +++ b/app/i18n/sk/conf.php @@ -6,7 +6,7 @@ return array( 'advanced' => 'Pokročilé', 'delete_after' => 'Vymazať články po', 'help' => 'Viac možností nájdete v nastaveniach kanála', - 'keep_history_by_feed' => 'Minimálny počet článkov kanála na zachovanie', + 'keep_min_by_feed' => 'Minimálny počet článkov kanála na zachovanie', 'optimize' => 'Optimalizovať databázu', 'optimize_help' => 'Občas vykonajte na zmenšenie veľkosti databázy', 'purge_now' => 'Vyčistiť teraz', diff --git a/app/i18n/sk/sub.php b/app/i18n/sk/sub.php index 4dcd09f57..2167e1817 100644 --- a/app/i18n/sk/sub.php +++ b/app/i18n/sk/sub.php @@ -40,7 +40,7 @@ return array( 'help' => 'Napíšte jeden výraz hľadania na riadok.', ), 'information' => 'Informácia', - 'keep_history' => 'Minimálny počet článkov na uchovanie', + 'keep_min' => 'Minimálny počet článkov na uchovanie', 'moved_category_deleted' => 'Keď vymažete kategóriu, jej kanály sa automaticky zaradia pod %s.', 'mute' => 'stíšiť', 'no_selected' => 'Nevybrali ste kanál.', diff --git a/app/i18n/tr/conf.php b/app/i18n/tr/conf.php index 2bf1e8a6a..c8ea78efa 100644 --- a/app/i18n/tr/conf.php +++ b/app/i18n/tr/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => 'Arşiv', - 'advanced' => 'Gelişmiş', 'delete_after' => 'Makelelerin tutulacağı süre', + 'exception' => 'Purge exception', //TODO - Translation 'help' => 'Akış ayarlarında daha çok ayar bulabilirsiniz', - 'keep_history_by_feed' => 'Akışta en az tutulacak makale sayısı', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => 'Akışta en az tutulacak makale sayısı', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => 'Veritabanı optimize et', 'optimize_help' => 'Bu işlem bazen veritabanı boyutunu düşürmeye yardımcı olur', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => 'Şimdi temizle', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => 'Arşiv', 'ttl' => 'Şu süreden sık otomatik yenileme yapma', ), diff --git a/app/i18n/tr/gen.php b/app/i18n/tr/gen.php index 5e361affb..ccc5b9ee6 100644 --- a/app/i18n/tr/gen.php +++ b/app/i18n/tr/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => 'Başka makale yok', 'previous' => 'Önceki', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index 858d15758..f6f40d3f7 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => 'Kategori', 'add' => 'Kategori ekle', + 'archiving' => 'Arşiv', 'empty' => 'Boş kategori', 'information' => 'Bilgi', 'new' => 'Yeni kategori', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'Bilgi', - 'keep_history' => 'En az tutulacak makale sayısı', + 'keep_min' => 'En az tutulacak makale sayısı', 'moved_category_deleted' => 'Bir kategoriyi silerseniz, içerisindeki akışlar %s içerisine yerleşir.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Hiçbir akış seçilmedi.', diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php index 2960cd6b1..a7404bc58 100644 --- a/app/i18n/zh-cn/conf.php +++ b/app/i18n/zh-cn/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => array( '_' => '存档', - 'advanced' => '高级', 'delete_after' => '文章保留', + 'exception' => 'Purge exception', //TODO - Translation 'help' => '详细选项位于单独的 RSS 源设置', - 'keep_history_by_feed' => '至少保存的文章数', + 'keep_favourites' => 'Never delete favourites', //TODO - Translation + 'keep_min_by_feed' => '至少保存的文章数', + 'keep_labels' => 'Never delete labels', //TODO - Translation + 'keep_unreads' => 'Never delete unreads', //TODO - Translation + 'maintenance' => 'Maintenance', //TODO - Translation 'optimize' => '优化数据库', 'optimize_help' => '偶尔执行优化可以减少数据库大小', + 'policy' => 'Purge policy', //TODO - Translation + 'policy_warning' => 'If no purge policy is selected, every article will be kept.', //TODO - Translation 'purge_now' => '立即清除', + 'keep_max' => 'Maximum number of articles to keep', //TODO - Translation + 'keep_period' => 'Maximum age of articles to keep', //TODO - Translation 'title' => '存档', 'ttl' => '最小自动更新时间', ), diff --git a/app/i18n/zh-cn/gen.php b/app/i18n/zh-cn/gen.php index 7ae156573..31817260e 100644 --- a/app/i18n/zh-cn/gen.php +++ b/app/i18n/zh-cn/gen.php @@ -162,6 +162,13 @@ return array( 'nothing_to_load' => '没有更多文章了', 'previous' => '上一页', ), + 'period' => array( + 'days' => 'days', //TODO - Translation + 'hours' => 'hours', //TODO - Translation + 'months' => 'months', //TODO - Translation + 'weeks' => 'weeks', //TODO - Translation + 'years' => 'years', //TODO - Translation + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php index bf517756b..f6f3a0f7a 100644 --- a/app/i18n/zh-cn/sub.php +++ b/app/i18n/zh-cn/sub.php @@ -13,6 +13,7 @@ return array( 'category' => array( '_' => '分类', 'add' => '添加分类', + 'archiving' => '存档', 'empty' => '空分类', 'information' => '信息', 'new' => '新分类', @@ -40,7 +41,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => '信息', - 'keep_history' => '至少保存的文章数', + 'keep_min' => '至少保存的文章数', 'moved_category_deleted' => '删除分类时,其中的 RSS 源会自动归类到 %s', 'mute' => '暂停', 'no_selected' => '未选择 RSS 源。', diff --git a/app/install.php b/app/install.php index 366fb0cfc..f8bc6dd4e 100644 --- a/app/install.php +++ b/app/install.php @@ -86,7 +86,6 @@ function saveStep1() { // Then, we set $_SESSION vars $_SESSION['title'] = $system_conf->title; $_SESSION['auth_type'] = $system_conf->auth_type; - $_SESSION['old_entries'] = $user_conf->old_entries; $_SESSION['default_user'] = $current_user; $_SESSION['passwordHash'] = $user_conf->passwordHash; @@ -184,14 +183,12 @@ function saveStep3() { if (!empty($_POST)) { $system_default_config = Minz_Configuration::get('default_system'); $_SESSION['title'] = $system_default_config->title; - $_SESSION['old_entries'] = param('old_entries', $user_default_config->old_entries); $_SESSION['auth_type'] = param('auth_type', 'form'); if (FreshRSS_user_Controller::checkUsername(param('default_user', ''))) { $_SESSION['default_user'] = param('default_user', ''); } - if (empty($_SESSION['old_entries']) || - empty($_SESSION['auth_type']) || + if (empty($_SESSION['auth_type']) || empty($_SESSION['default_user'])) { return false; } @@ -208,10 +205,6 @@ function saveStep3() { FreshRSS_Context::$system_conf->default_user = $_SESSION['default_user']; FreshRSS_Context::$system_conf->save(); - if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { - $_SESSION['old_entries'] = $user_default_config->old_entries; - } - // Create default user files but first, we delete previous data to // avoid access right problems. recursive_unlink(USERS_PATH . '/' . $_SESSION['default_user']); @@ -225,7 +218,6 @@ function saveStep3() { '', [ 'language' => $_SESSION['language'], - 'old_entries' => $_SESSION['old_entries'], ] ); } catch (Exception $e) { @@ -317,8 +309,7 @@ function checkStep2() { } function checkStep3() { - $conf = !empty($_SESSION['old_entries']) && - !empty($_SESSION['default_user']); + $conf = !empty($_SESSION['default_user']); $form = isset($_SESSION['auth_type']); @@ -593,13 +584,6 @@ function printStep3() {
-
- -
- -
-
-
diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index 09be55fd9..0387a2b96 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -8,23 +8,6 @@

-
- -
- -   -
-
-
- -
- () -
-
@@ -47,6 +30,76 @@
+

+ +

+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
@@ -55,9 +108,9 @@
-
+ + -
@@ -66,21 +119,30 @@
-
-
- size_total); ?> +
+ -
+
+ +
-
+ + +
+ +
+ size_total); ?> +
+
+
diff --git a/app/views/helpers/category/update.phtml b/app/views/helpers/category/update.phtml index b64bd786a..31482f163 100644 --- a/app/views/helpers/category/update.phtml +++ b/app/views/helpers/category/update.phtml @@ -33,5 +33,121 @@
+ + + category->attributes('archiving'); + if (empty($archiving)) { + $archiving = [ 'default' => true ]; + } else { + $archiving['default'] = false; + } + $volatile = [ + 'enable_keep_period' => false, + 'keep_period_count' => '3', + 'keep_period_unit' => 'P1M', + ]; + if (!empty($archiving['keep_period'])) { + if (preg_match('/^PT?(?P\d+)[YMWDH]$/', $archiving['keep_period'], $matches)) { + $volatile['enable_keep_period'] = true; + $volatile['keep_period_count'] = $matches['count']; + $volatile['keep_period_unit'] = str_replace($matches['count'], '1', $archiving['keep_period']); + } + } + //Defaults + if (!isset($archiving['keep_max'])) { + $archiving['keep_max'] = false; + } + if (!isset($archiving['keep_favourites'])) { + $archiving['keep_favourites'] = true; + } + if (!isset($archiving['keep_labels'])) { + $archiving['keep_labels'] = true; + } + if (!isset($archiving['keep_unreads'])) { + $archiving['keep_unreads'] = false; + } + if (!isset($archiving['keep_min'])) { + $archiving['keep_min'] = 50; + } + ?> + +

+ +

+ +
+ +
+ +
+
+ + + + + + +
+
+ + +
+
diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 620806d7b..84461ed03 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -78,6 +78,7 @@
+
+ feed->attributes('archiving'); + if (empty($archiving)) { + $archiving = [ 'default' => true ]; + } else { + $archiving['default'] = false; + } + $volatile = [ + 'enable_keep_period' => false, + 'keep_period_count' => '3', + 'keep_period_unit' => 'P1M', + ]; + if (!empty($archiving['keep_period'])) { + if (preg_match('/^PT?(?P\d+)[YMWDH]$/', $archiving['keep_period'], $matches)) { + $volatile['enable_keep_period'] = true; + $volatile['keep_period_count'] = $matches['count']; + $volatile['keep_period_unit'] = str_replace($matches['count'], '1', $archiving['keep_period']); + } + } + //Defaults + if (!isset($archiving['keep_max'])) { + $archiving['keep_max'] = false; + } + if (!isset($archiving['keep_min'])) { + $archiving['keep_min'] = 50; + } + if (!isset($archiving['keep_favourites'])) { + $archiving['keep_favourites'] = true; + } + if (!isset($archiving['keep_labels'])) { + $archiving['keep_labels'] = true; + } + if (!isset($archiving['keep_unreads'])) { + $archiving['keep_unreads'] = false; + } + ?> + +

+ +

+
- +
- +
+ + + + + + +
@@ -143,6 +243,7 @@
+
diff --git a/cli/_update-or-create-user.php b/cli/_update-or-create-user.php index eda597f19..43b86a4a9 100644 --- a/cli/_update-or-create-user.php +++ b/cli/_update-or-create-user.php @@ -45,8 +45,8 @@ $values = array( 'language' => strParam('language'), 'mail_login' => strParam('email'), 'token' => strParam('token'), - 'old_entries' => intParam('purge_after_months'), - 'keep_history_default' => intParam('feed_min_articles_default'), + 'old_entries' => intParam('purge_after_months'), //TODO: Update with new mechanism + 'keep_history_default' => intParam('feed_min_articles_default'), //TODO: Update with new mechanism 'ttl_default' => intParam('feed_ttl_default'), 'since_hours_posts_per_rss' => intParam('since_hours_posts_per_rss'), 'min_posts_per_rss' => intParam('min_posts_per_rss'), diff --git a/config-user.default.php b/config-user.default.php index 950bef045..5b49d689a 100644 --- a/config-user.default.php +++ b/config-user.default.php @@ -5,8 +5,14 @@ # override. return array ( 'language' => 'en', - 'old_entries' => 3, - 'keep_history_default' => 50, + 'archiving' => [ + 'keep_period' => 'P3M', + 'keep_max' => 200, + 'keep_min' => 50, + 'keep_favourites' => true, + 'keep_labels' => true, + 'keep_unreads' => false, + ], 'ttl_default' => 3600, 'mail_login' => '', 'email_validation_token' => '', diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 01feece52..9235f873a 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -52,6 +52,12 @@ class Minz_Request { } return null; } + public static function paramBoolean($key) { + if (null === $value = self::paramTernary($key)) { + return false; + } + return $value; + } public static function defaultControllerName() { return self::$default_controller_name; } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 854126b54..2a230e6f8 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -300,7 +300,11 @@ function invalidateHttpCache($username = '') { Minz_Session::_param('touch', uTimeString()); $username = Minz_Session::param('currentUser', '_'); } - return touch(join_path(DATA_PATH, 'users', $username, 'log.txt')); + $ok = @touch(DATA_PATH . '/users/' . $username . '/log.txt'); + if (!$ok) { + //TODO: Display notification error on front-end + } + return $ok; } function listUsers() { diff --git a/p/scripts/category.js b/p/scripts/category.js index c01b1fdd7..c5d36e900 100644 --- a/p/scripts/category.js +++ b/p/scripts/category.js @@ -137,11 +137,34 @@ function init_draggable() { }; } +function archiving() { + const slider = document.getElementById('slider'); + slider.addEventListener('change', function (e) { + if (e.target.id === 'use_default_purge_options') { + slider.querySelectorAll('.archiving').forEach(function (element) { + element.hidden = e.target.checked; + }); + } + }); + slider.addEventListener('click', function (e) { + if (e.target.closest('button[type=reset]')) { + const archiving = document.getElementById('use_default_purge_options'); + if (archiving) { + slider.querySelectorAll('.archiving').forEach(function (element) { + element.hidden = archiving.getAttribute('data-leave-validation') == 1; + }); + } + } + }); +} + if (document.readyState && document.readyState !== 'loading') { init_draggable(); + archiving(); } else if (document.addEventListener) { document.addEventListener('DOMContentLoaded', function () { init_draggable(); + archiving(); }, false); } // @license-end diff --git a/p/scripts/extra.js b/p/scripts/extra.js index bba2e8e2b..1fd8a19de 100644 --- a/p/scripts/extra.js +++ b/p/scripts/extra.js @@ -184,12 +184,32 @@ function init_slider_observers() { }; closer.onclick = function (ev) { - closer.classList.remove('active'); - slider.classList.remove('active'); - return false; + if (data_leave_validation() || confirm(context.i18n.confirmation_default)) { + slider.querySelectorAll('form').forEach(function (f) { f.reset(); }); + closer.classList.remove('active'); + slider.classList.remove('active'); + return true; + } else { + return false; + } }; } +function data_leave_validation() { + const ds = document.querySelectorAll('[data-leave-validation]'); + for (let i = ds.length - 1; i >= 0; i--) { + const input = ds[i]; + if (input.type === 'checkbox' || input.type === 'radio') { + if (input.checked != input.getAttribute('data-leave-validation')) { + return false; + } + } else if (input.value != input.getAttribute('data-leave-validation')) { + return false; + } + } + return true; +} + function init_configuration_alert() { window.onsubmit = function (e) { window.hasSubmit = true; @@ -198,16 +218,8 @@ function init_configuration_alert() { if (window.hasSubmit) { return; } - const ds = document.querySelectorAll('[data-leave-validation]'); - for (let i = ds.length - 1; i >= 0; i--) { - const input = ds[i]; - if (input.type === 'checkbox' || input.type === 'radio') { - if (input.checked != input.getAttribute('data-leave-validation')) { - return false; - } - } else if (input.value != input.getAttribute('data-leave-validation')) { - return false; - } + if (!data_leave_validation()) { + return false; } }; } diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css index 889d33c4e..2d76c9b4d 100644 --- a/p/themes/base-theme/template.css +++ b/p/themes/base-theme/template.css @@ -101,6 +101,9 @@ label { input { width: 180px; } +input[type=number] { + width: 6em; +} textarea, input[type="file"], diff --git a/phpcs.xml b/phpcs.xml index c69f53ea4..fba5624a8 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -3,8 +3,6 @@ Created with the PHP Coding Standard Generator. https://edorian.github.com/php-coding-standard-generator/ - ./static - ./vendor ./lib/SimplePie/ ./lib/PHPMailer/ ./lib/http-conditional.php @@ -28,9 +26,6 @@ ./app/install.php ./tests/app/ - - ./app/SQL/install.sql.mysql.php - ./app/SQL/install.sql.pgsql.php -- cgit v1.2.3