diff options
Diffstat (limited to 'app')
189 files changed, 5407 insertions, 2415 deletions
diff --git a/app/.htaccess b/app/.htaccess index 9e768397d..32eca30f7 100644 --- a/app/.htaccess +++ b/app/.htaccess @@ -1,3 +1,11 @@ -Order Allow,Deny -Deny from all -Satisfy all +# Apache 2.2 +<IfModule !mod_authz_core.c> + Order Allow,Deny + Deny from all + Satisfy all +</IfModule> + +# Apache 2.4 +<IfModule mod_authz_core.c> + Require all denied +</IfModule> diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index ca44b1a96..e2e1aaa22 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -169,10 +169,6 @@ class FreshRSS_auth_Controller extends Minz_ActionController { return; } - if (!function_exists('password_verify')) { - include_once(LIB_PATH . '/password_compat.php'); - } - $s = $conf->passwordHash; $ok = password_verify($password, $s); unset($password); @@ -203,12 +199,22 @@ class FreshRSS_auth_Controller extends Minz_ActionController { /** * This action gives possibility to a user to create an account. + * + * The user is redirected to the home if he's connected. + * + * A 403 is sent if max number of registrations is reached. */ public function registerAction() { + if (FreshRSS_Auth::hasAccess()) { + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); + } + if (max_registrations_reached()) { Minz_Error::error(403); } + $this->view->show_tos_checkbox = file_exists(join_path(DATA_PATH, 'tos.html')); + $this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation; Minz_View::prependTitle(_t('gen.auth.registration.title') . ' · '); } } diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 6d3c4dcce..b38d3289a 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -48,6 +48,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { FreshRSS_Context::$user_conf->topline_favorite = Minz_Request::param('topline_favorite', false); FreshRSS_Context::$user_conf->topline_date = Minz_Request::param('topline_date', false); FreshRSS_Context::$user_conf->topline_link = Minz_Request::param('topline_link', false); + FreshRSS_Context::$user_conf->topline_display_authors = Minz_Request::param('topline_display_authors', false); FreshRSS_Context::$user_conf->bottomline_read = Minz_Request::param('bottomline_read', false); FreshRSS_Context::$user_conf->bottomline_favorite = Minz_Request::param('bottomline_favorite', false); FreshRSS_Context::$user_conf->bottomline_sharing = Minz_Request::param('bottomline_sharing', false); @@ -166,8 +167,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { * tab and up. */ public function shortcutAction() { - global $SHORTCUT_KEYS; - $this->view->list_keys = $SHORTCUT_KEYS; + $this->view->list_keys = SHORTCUT_KEYS; if (Minz_Request::isPost()) { FreshRSS_Context::$user_conf->shortcuts = validateShortcutList(Minz_Request::param('shortcuts')); @@ -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<count>\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') . ' · '); } /** @@ -292,15 +329,24 @@ class FreshRSS_configure_Controller extends Minz_ActionController { * configuration values then sends a notification to the user. * * The options available on the page are: + * - instance name (default: FreshRSS) + * - auto update URL (default: false) + * - force emails validation (default: false) * - user limit (default: 1) * - user category limit (default: 16384) * - user feed limit (default: 16384) * - user login duration for form auth (default: 2592000) + * + * The `force-email-validation` is ignored with PHP < 5.5 */ public function systemAction() { if (!FreshRSS_Auth::hasAccess('admin')) { Minz_Error::error(403); } + + $can_enable_email_validation = version_compare(PHP_VERSION, '5.5') >= 0; + $this->view->can_enable_email_validation = $can_enable_email_validation; + if (Minz_Request::isPost()) { $limits = FreshRSS_Context::$system_conf->limits; $limits['max_registrations'] = Minz_Request::param('max-registrations', 1); @@ -310,6 +356,9 @@ class FreshRSS_configure_Controller extends Minz_ActionController { FreshRSS_Context::$system_conf->limits = $limits; FreshRSS_Context::$system_conf->title = Minz_Request::param('instance-name', 'FreshRSS'); FreshRSS_Context::$system_conf->auto_update_url = Minz_Request::param('auto-update-url', false); + if ($can_enable_email_validation) { + FreshRSS_Context::$system_conf->force_email_validation = Minz_Request::param('force-email-validation', false); + } FreshRSS_Context::$system_conf->save(); invalidateHttpCache(); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 9c5ee2616..7881cb3ec 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -17,7 +17,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { // If ajax request, we do not print layout $this->ajax = Minz_Request::param('ajax'); if ($this->ajax) { - $this->view->_useLayout(false); + $this->view->_layout(false); Minz_Request::_param('ajax'); } } @@ -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/extensionController.php b/app/Controllers/extensionController.php index 806e5a696..68bd90f4c 100644 --- a/app/Controllers/extensionController.php +++ b/app/Controllers/extensionController.php @@ -80,10 +80,10 @@ class FreshRSS_extension_Controller extends Minz_ActionController { */ public function configureAction() { if (Minz_Request::param('ajax')) { - $this->view->_useLayout(false); + $this->view->_layout(false); } else { $this->indexAction(); - $this->view->change_view('extension', 'index'); + $this->view->_path('extension/index.phtml'); } $ext_name = urldecode(Minz_Request::param('e')); diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 862bb10fb..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,23 +397,19 @@ 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) . ']'); } } $feedDAO->updateLastUpdate($feed->id(), false, $mtime); if ($needFeedCacheRefresh) { - $feedDAO->updateCachedValue($feed->id()); + $feedDAO->updateCachedValues($feed->id()); } if ($entryDAO->inTransaction()) { $entryDAO->commit(); @@ -530,7 +510,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { ); Minz_Session::_param('notification', $notif); // No layout in ajax request. - $this->view->_useLayout(false); + $this->view->_layout(false); } else { // Redirect to the main page with correct notification. if ($updated_feeds === 1) { diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 1d7176929..f2ae8238e 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -29,7 +29,25 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('sub.import_export.title') . ' · '); } + private static function megabytes($size_str) { + switch (substr($size_str, -1)) { + case 'M': case 'm': return (int)$size_str; + case 'K': case 'k': return (int)$size_str / 1024; + case 'G': case 'g': return (int)$size_str * 1024; + } + return $size_str; + } + + private static function minimumMemory($mb) { + $mb = (int)$mb; + $ini = self::megabytes(ini_get('memory_limit')); + if ($ini < $mb) { + ini_set('memory_limit', $mb . 'M'); + } + } + public function importFile($name, $path, $username = null) { + self::minimumMemory(256); require_once(LIB_PATH . '/lib_opml.php'); $this->catDAO = new FreshRSS_CategoryDAO($username); @@ -709,8 +727,6 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $this->entryDAO = FreshRSS_Factory::createEntryDao($username); $this->feedDAO = FreshRSS_Factory::createFeedDao($username); - $this->entryDAO->disableBuffering(); - if ($export_feeds === true) { //All feeds $export_feeds = $this->feedDAO->listFeedsIds(); @@ -773,7 +789,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { if (!Minz_Request::isPost()) { Minz_Request::forward(array('c' => 'importExport', 'a' => 'index'), true); } - $this->view->_useLayout(false); + $this->view->_layout(false); $nb_files = 0; try { diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index f536113dd..967029fd1 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -155,7 +155,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { // No layout for RSS output. $this->view->url = PUBLIC_TO_INDEX_PATH . '/' . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']); $this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title(); - $this->view->_useLayout(false); + $this->view->_layout(false); header('Content-Type: application/rss+xml; charset=utf-8'); } @@ -173,7 +173,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { private function updateContext() { if (empty(FreshRSS_Context::$categories)) { $catDAO = FreshRSS_Factory::createCategoryDao(); - FreshRSS_Context::$categories = $catDAO->listCategories(); + FreshRSS_Context::$categories = $catDAO->listSortedCategories(); } // Update number of read / unread variables. @@ -260,6 +260,23 @@ class FreshRSS_index_Controller extends Minz_ActionController { } /** + * This action displays the EULA page of FreshRSS. + * This page is enabled only if admin created a data/tos.html file. + * The content of the page is the content of data/tos.html. + * It returns 404 if there is no EULA. + */ + public function tosAction() { + $terms_of_service = file_get_contents(join_path(DATA_PATH, 'tos.html')); + if (!$terms_of_service) { + Minz_Error::error(404); + } + + $this->view->terms_of_service = $terms_of_service; + $this->view->can_register = !max_registrations_reached(); + Minz_View::prependTitle(_t('index.tos.title') . ' · '); + } + + /** * This action displays logs of FreshRSS for the current user. */ public function logsAction() { diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index d56da9cbb..c84e5483b 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -2,7 +2,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { public function firstAction() { - $this->view->_useLayout(false); + $this->view->_layout(false); } public function actualizeAction() { diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 79da39751..b4520c8e6 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -19,7 +19,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { $catDAO->checkDefault(); $feedDAO->updateTTL(); - $this->view->categories = $catDAO->listCategories(false); + $this->view->categories = $catDAO->listSortedCategories(false); $this->view->default_category = $catDAO->getDefault(); } @@ -74,7 +74,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { */ public function feedAction() { if (Minz_Request::param('ajax')) { - $this->view->_useLayout(false); + $this->view->_layout(false); } $feedDAO = FreshRSS_Factory::createFeedDao(); @@ -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(), ); @@ -152,7 +177,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController { } public function categoryAction() { - $this->view->_useLayout(false); + $this->view->_layout(false); $categoryDAO = FreshRSS_Factory::createCategoryDao(); @@ -165,9 +190,39 @@ 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'), + ]); + } + + $position = Minz_Request::param('position'); + $category->_attributes('position', '' === $position ? null : (int) $position); + + $values = [ 'name' => Minz_Request::param('name', ''), - ); + 'attributes' => $category->attributes(), + ]; invalidateHttpCache(); diff --git a/app/Controllers/tagController.php b/app/Controllers/tagController.php index 106e0afa8..ba9efe2fc 100644 --- a/app/Controllers/tagController.php +++ b/app/Controllers/tagController.php @@ -16,7 +16,7 @@ class FreshRSS_tag_Controller extends Minz_ActionController { // If ajax request, we do not print layout $this->ajax = Minz_Request::param('ajax'); if ($this->ajax) { - $this->view->_useLayout(false); + $this->view->_layout(false); Minz_Request::_param('ajax'); } } @@ -70,7 +70,7 @@ class FreshRSS_tag_Controller extends Minz_ActionController { } public function getTagsForEntryAction() { - $this->view->_useLayout(false); + $this->view->_layout(false); header('Content-Type: application/json; charset=UTF-8'); header('Cache-Control: private, no-cache, no-store, must-revalidate'); $id_entry = Minz_Request::param('id_entry', 0); diff --git a/app/Controllers/updateController.php b/app/Controllers/updateController.php index 2be644c85..ebe5e4cc8 100644 --- a/app/Controllers/updateController.php +++ b/app/Controllers/updateController.php @@ -89,7 +89,7 @@ class FreshRSS_update_Controller extends Minz_ActionController { } public function checkAction() { - $this->view->change_view('update', 'index'); + $this->view->_path('update/index.phtml'); if (file_exists(UPDATE_FILENAME)) { // There is already an update file to apply: we don't need to check diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index 6d0fced5b..6afc91b4e 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -8,26 +8,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { // so do not use a too high cost const BCRYPT_COST = 9; - /** - * This action is called before every other action in that class. It is - * the common boiler plate for every action. It is triggered by the - * underlying framework. - * - * @todo clean up the access condition. - */ - public function firstAction() { - if (!FreshRSS_Auth::hasAccess() && !( - Minz_Request::actionName() === 'create' && - !max_registrations_reached() - )) { - Minz_Error::error(403); - } - } - public static function hashPassword($passwordPlain) { - if (!function_exists('password_hash')) { - include_once(LIB_PATH . '/password_compat.php'); - } $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); $passwordPlain = ''; $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js @@ -52,12 +33,23 @@ class FreshRSS_user_Controller extends Minz_ActionController { return false; } - public static function updateUser($user, $passwordPlain, $apiPasswordPlain, $userConfigUpdated = array()) { + public static function updateUser($user, $email, $passwordPlain, $apiPasswordPlain, $userConfigUpdated = array()) { $userConfig = get_user_configuration($user); if ($userConfig === null) { return false; } + if ($email !== null && $userConfig->mail_login !== $email) { + $userConfig->mail_login = $email; + + if (FreshRSS_Context::$system_conf->force_email_validation) { + $salt = FreshRSS_Context::$system_conf->salt; + $userConfig->email_validation_token = sha1($salt . uniqid(mt_rand(), true)); + $mailer = new FreshRSS_User_Mailer(); + $mailer->send_email_need_validation($user, $userConfig); + } + } + if ($passwordPlain != '') { $passwordHash = self::hashPassword($passwordPlain); $userConfig->passwordHash = $passwordHash; @@ -103,7 +95,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $apiPasswordPlain = Minz_Request::param('apiPasswordPlain', '', true); $username = Minz_Request::param('username'); - $ok = self::updateUser($username, $passwordPlain, $apiPasswordPlain, array( + $ok = self::updateUser($username, null, $passwordPlain, $apiPasswordPlain, array( 'token' => Minz_Request::param('token', null), )); @@ -126,25 +118,63 @@ class FreshRSS_user_Controller extends Minz_ActionController { * This action displays the user profile page. */ public function profileAction() { + if (!FreshRSS_Auth::hasAccess()) { + Minz_Error::error(403); + } + + $email_not_verified = FreshRSS_Context::$user_conf->email_validation_token !== ''; + $this->view->disable_aside = false; + if ($email_not_verified) { + $this->view->_layout('simple'); + $this->view->disable_aside = true; + } + Minz_View::prependTitle(_t('conf.profile.title') . ' · '); Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); if (Minz_Request::isPost()) { + $system_conf = FreshRSS_Context::$system_conf; + $user_config = FreshRSS_Context::$user_conf; + $old_email = $user_config->mail_login; + + $email = trim(Minz_Request::param('email', '')); $passwordPlain = Minz_Request::param('newPasswordPlain', '', true); Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP $_POST['newPasswordPlain'] = ''; $apiPasswordPlain = Minz_Request::param('apiPasswordPlain', '', true); - $ok = self::updateUser(Minz_Session::param('currentUser'), $passwordPlain, $apiPasswordPlain, array( + if ($system_conf->force_email_validation && empty($email)) { + Minz_Request::bad( + _t('user.email.feedback.required'), + array('c' => 'user', 'a' => 'profile') + ); + } + + if (!empty($email) && !validateEmailAddress($email)) { + Minz_Request::bad( + _t('user.email.feedback.invalid'), + array('c' => 'user', 'a' => 'profile') + ); + } + + $ok = self::updateUser( + Minz_Session::param('currentUser'), + $email, + $passwordPlain, + $apiPasswordPlain, + array( 'token' => Minz_Request::param('token', null), - )); + ) + ); Minz_Session::_param('passwordHash', FreshRSS_Context::$user_conf->passwordHash); if ($ok) { - if ($passwordPlain == '') { + if ($system_conf->force_email_validation && $email !== $old_email) { + Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'user', 'a' => 'validateEmail')); + } elseif ($passwordPlain == '') { Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'user', 'a' => 'profile')); } else { Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'index', 'a' => 'index')); @@ -166,6 +196,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { Minz_View::prependTitle(_t('admin.user.title') . ' · '); + $this->view->show_email_field = FreshRSS_Context::$system_conf->force_email_validation; $this->view->current_user = Minz_Request::param('u'); $this->view->nb_articles = 0; @@ -180,9 +211,19 @@ class FreshRSS_user_Controller extends Minz_ActionController { } } - public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) { - if (!is_array($userConfig)) { - $userConfig = array(); + public static function createUser($new_user_name, $email, $passwordPlain, $apiPasswordPlain = '', $userConfigOverride = [], $insertDefaultFeeds = true) { + $userConfig = []; + + $customUserConfigPath = join_path(DATA_PATH, 'config-user.custom.php'); + if (file_exists($customUserConfigPath)) { + $customUserConfig = include($customUserConfigPath); + if (is_array($customUserConfig)) { + $userConfig = $customUserConfig; + } + } + + if (is_array($userConfigOverride)) { + $userConfig = array_merge($userConfig, $userConfigOverride); } $ok = self::checkUsername($new_user_name); @@ -206,9 +247,9 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok &= (file_put_contents($configPath, "<?php\n return " . var_export($userConfig, true) . ';') !== false); } if ($ok) { - $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->createUser($new_user_name, $userConfig['language'], $insertDefaultFeeds); - $ok &= self::updateUser($new_user_name, $passwordPlain, $apiPasswordPlain); + $newUserDAO = FreshRSS_Factory::createUserDao($new_user_name); + $ok &= $newUserDAO->createUser($insertDefaultFeeds); + $ok &= self::updateUser($new_user_name, $email, $passwordPlain, $apiPasswordPlain); } return $ok; } @@ -219,6 +260,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { * Request parameters are: * - new_user_language * - new_user_name + * - new_user_email * - new_user_passwordPlain * - r (i.e. a redirection url, optional) * @@ -226,15 +268,43 @@ class FreshRSS_user_Controller extends Minz_ActionController { * @todo handle r redirection in Minz_Request::forward directly? */ public function createAction() { - if (Minz_Request::isPost() && ( - FreshRSS_Auth::hasAccess('admin') || - !max_registrations_reached() - )) { + if (!FreshRSS_Auth::hasAccess('admin') && max_registrations_reached()) { + Minz_Error::error(403); + } + + if (Minz_Request::isPost()) { + $system_conf = FreshRSS_Context::$system_conf; + $new_user_name = Minz_Request::param('new_user_name'); + $email = Minz_Request::param('new_user_email', ''); $passwordPlain = Minz_Request::param('new_user_passwordPlain', '', true); $new_user_language = Minz_Request::param('new_user_language', FreshRSS_Context::$user_conf->language); - $ok = self::createUser($new_user_name, $passwordPlain, '', array('language' => $new_user_language)); + $tos_enabled = file_exists(join_path(DATA_PATH, 'tos.html')); + $accept_tos = Minz_Request::param('accept_tos', false); + + if ($system_conf->force_email_validation && empty($email)) { + Minz_Request::bad( + _t('user.email.feedback.required'), + array('c' => 'auth', 'a' => 'register') + ); + } + + if (!empty($email) && !validateEmailAddress($email)) { + Minz_Request::bad( + _t('user.email.feedback.invalid'), + array('c' => 'auth', 'a' => 'register') + ); + } + + if ($tos_enabled && !$accept_tos) { + Minz_Request::bad( + _t('user.tos.feedback.invalid'), + array('c' => 'auth', 'a' => 'register') + ); + } + + $ok = self::createUser($new_user_name, $email, $passwordPlain, '', array('language' => $new_user_language)); Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP $_POST['new_user_passwordPlain'] = ''; invalidateHttpCache(); @@ -266,9 +336,6 @@ class FreshRSS_user_Controller extends Minz_ActionController { } public static function deleteUser($username) { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - $ok = self::checkUsername($username); if ($ok) { $default_user = FreshRSS_Context::$system_conf->default_user; @@ -278,8 +345,8 @@ class FreshRSS_user_Controller extends Minz_ActionController { $ok &= is_dir($user_data); if ($ok) { self::deleteFeverKey($username); - $userDAO = new FreshRSS_UserDAO(); - $ok &= $userDAO->deleteUser($username); + $oldUserDAO = FreshRSS_Factory::createUserDao($username); + $ok &= $oldUserDAO->deleteUser(); $ok &= recursive_unlink($user_data); array_map('unlink', glob(PSHB_PATH . '/feeds/*/' . $username . '.txt')); } @@ -287,6 +354,122 @@ class FreshRSS_user_Controller extends Minz_ActionController { } /** + * This action validates an email address, based on the token sent by email. + * It also serves the main page when user is blocked. + * + * Request parameters are: + * - username + * - token + * + * This route works with GET requests since the URL is provided by email. + * The security risks (e.g. forged URL by an attacker) are not very high so + * it's ok. + * + * It returns 404 error if `force_email_validation` is disabled or if the + * user doesn't exist. + * + * It returns 403 if user isn't logged in and `username` param isn't passed. + */ + public function validateEmailAction() { + if (!FreshRSS_Context::$system_conf->force_email_validation) { + Minz_Error::error(404); + } + + Minz_View::prependTitle(_t('user.email.validation.title') . ' · '); + $this->view->_layout('simple'); + + $username = Minz_Request::param('username'); + $token = Minz_Request::param('token'); + + if ($username) { + $user_config = get_user_configuration($username); + } elseif (FreshRSS_Auth::hasAccess()) { + $user_config = FreshRSS_Context::$user_conf; + } else { + Minz_Error::error(403); + } + + if (!FreshRSS_UserDAO::exists($username) || $user_config === null) { + Minz_Error::error(404); + } + + if ($user_config->email_validation_token === '') { + Minz_Request::good( + _t('user.email.validation.feedback.unnecessary'), + array('c' => 'index', 'a' => 'index') + ); + } + + if ($token) { + if ($user_config->email_validation_token !== $token) { + Minz_Request::bad( + _t('user.email.validation.feedback.wrong_token'), + array('c' => 'user', 'a' => 'validateEmail') + ); + } + + $user_config->email_validation_token = ''; + if ($user_config->save()) { + Minz_Request::good( + _t('user.email.validation.feedback.ok'), + array('c' => 'index', 'a' => 'index') + ); + } else { + Minz_Request::bad( + _t('user.email.validation.feedback.error'), + array('c' => 'user', 'a' => 'validateEmail') + ); + } + } + } + + /** + * This action resends a validation email to the current user. + * + * It only acts on POST requests but doesn't require any param (except the + * CSRF token). + * + * It returns 403 error if the user is not logged in or 404 if request is + * not POST. Else it redirects silently to the index if user has already + * validated its email, or to the user#validateEmail route. + */ + public function sendValidationEmailAction() { + if (!FreshRSS_Auth::hasAccess()) { + Minz_Error::error(403); + } + + if (!Minz_Request::isPost()) { + Minz_Error::error(404); + } + + $username = Minz_Session::param('currentUser', '_'); + $user_config = FreshRSS_Context::$user_conf; + + if ($user_config->email_validation_token === '') { + Minz_Request::forward(array( + 'c' => 'index', + 'a' => 'index', + ), true); + } + + $mailer = new FreshRSS_User_Mailer(); + $ok = $mailer->send_email_need_validation($username, $user_config); + + $redirect_url = array('c' => 'user', 'a' => 'validateEmail'); + if ($ok) { + Minz_Request::good( + _t('user.email.validation.feedback.email_sent'), + $redirect_url + ); + } else { + Minz_Request::bad( + _t('user.email.validation.feedback.email_failed'), + $redirect_url + ); + } + } + + /** * This action delete an existing user. * * Request parameter is: @@ -296,17 +479,18 @@ class FreshRSS_user_Controller extends Minz_ActionController { */ public function deleteAction() { $username = Minz_Request::param('username'); + $self_deletion = Minz_Session::param('currentUser', '_') === $username; + + if (!FreshRSS_Auth::hasAccess('admin') && !$self_deletion) { + Minz_Error::error(403); + } + $redirect_url = urldecode(Minz_Request::param('r', false, true)); if (!$redirect_url) { $redirect_url = array('c' => 'user', 'a' => 'manage'); } - $self_deletion = Minz_Session::param('currentUser', '_') === $username; - - if (Minz_Request::isPost() && ( - FreshRSS_Auth::hasAccess('admin') || - $self_deletion - )) { + if (Minz_Request::isPost()) { $ok = true; if ($ok && $self_deletion) { // We check the password if it's a self-destruction diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 8f614c538..d472a2147 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -53,6 +53,10 @@ class FreshRSS extends Minz_FrontController { $ext_list = FreshRSS_Context::$user_conf->extensions_enabled; Minz_ExtensionManager::enableByList($ext_list); } + + self::checkEmailValidated(); + + Minz_ExtensionManager::callHook('freshrss_init'); } private static function initAuth() { @@ -142,4 +146,22 @@ class FreshRSS extends Minz_FrontController { FreshRSS_Share::load(join_path(APP_PATH, 'shares.php')); self::loadStylesAndScripts(); } + + private static function checkEmailValidated() { + $email_not_verified = FreshRSS_Auth::hasAccess() && FreshRSS_Context::$user_conf->email_validation_token !== ''; + $action_is_allowed = ( + Minz_Request::is('user', 'validateEmail') || + Minz_Request::is('user', 'sendValidationEmail') || + Minz_Request::is('user', 'profile') || + Minz_Request::is('user', 'delete') || + Minz_Request::is('auth', 'logout') || + Minz_Request::is('javascript', 'nonce') + ); + if ($email_not_verified && !$action_is_allowed) { + Minz_Request::forward(array( + 'c' => 'user', + 'a' => 'validateEmail', + ), true); + } + } } diff --git a/app/Mailers/UserMailer.php b/app/Mailers/UserMailer.php new file mode 100644 index 000000000..5a2d39f1a --- /dev/null +++ b/app/Mailers/UserMailer.php @@ -0,0 +1,31 @@ +<?php + +/** + * Manage the emails sent to the users. + */ +class FreshRSS_User_Mailer extends Minz_Mailer { + public function send_email_need_validation($username, $user_config) { + $this->view->_path('user_mailer/email_need_validation.txt'); + + $this->view->username = $username; + $this->view->site_title = FreshRSS_Context::$system_conf->title; + $this->view->validation_url = Minz_Url::display( + array( + 'c' => 'user', + 'a' => 'validateEmail', + 'params' => array( + 'username' => $username, + 'token' => $user_config->email_validation_token + ) + ), + 'txt', + true + ); + + $subject_prefix = '[' . FreshRSS_Context::$system_conf->title . ']'; + return $this->mail( + $user_config->mail_login, + $subject_prefix . ' ' ._t('user.mailer.email_need_validation.title') + ); + } +} diff --git a/app/Models/Auth.php b/app/Models/Auth.php index 6d079a01f..b7fb0e6d6 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -219,10 +219,6 @@ class FreshRSS_FormAuth { return false; } - if (!function_exists('password_verify')) { - include_once(LIB_PATH . '/password_compat.php'); - } - return password_verify($nonce . $hash, $challenge); } @@ -283,8 +279,7 @@ class FreshRSS_FormAuth { $cookie_duration = empty($limits['cookie_duration']) ? 2592000 : $limits['cookie_duration']; $oldest = time() - $cookie_duration; foreach (new DirectoryIterator(DATA_PATH . '/tokens/') as $file_info) { - // $extension = $file_info->getExtension(); doesn't work in PHP < 5.3.7 - $extension = pathinfo($file_info->getFilename(), PATHINFO_EXTENSION); + $extension = $file_info->getExtension(); if ($extension === 'txt' && $file_info->getMTime() < $oldest) { @unlink($file_info->getPathname()); } diff --git a/app/Models/Category.php b/app/Models/Category.php index fa711aa66..a195c88b3 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,8 +69,19 @@ class FreshRSS_Category extends Minz_Model { return $this->hasFeedsWithError; } - public function _id($value) { - $this->id = $value; + 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) { + $this->_name(_t('gen.short.default_category')); + } } public function _name($value) { $this->name = trim($value); @@ -84,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 (null === $value) { + unset($this->attributes[$key]); + } else { + $this->attributes[$key] = $value; + } + } } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 6535adae7..c1277751c 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -4,23 +4,92 @@ 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 `' . $this->prefix . 'category`(name) ' - . 'SELECT * FROM (SELECT TRIM(?)) c2 ' //TRIM() to provide a type hint as text for PostgreSQL - . 'WHERE NOT EXISTS (SELECT 1 FROM `' . $this->prefix . 'tag` WHERE name = TRIM(?))'; //No tag of the same name - $stm = $this->bd->prepare($sql); + $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'], ); if ($stm && $stm->execute($values)) { - return $this->bd->lastInsertId('"' . $this->prefix . 'category_id_seq"'); + return $this->pdo->lastInsertId('`_category_id_seq`'); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error addCategory: ' . $info[2]); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); + 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 `' . $this->prefix . 'category` SET name=? WHERE id=? ' - . 'AND NOT EXISTS (SELECT 1 FROM `' . $this->prefix . 'tag` WHERE name = ?)'; //No tag of the same name - $stm = $this->bd->prepare($sql); + $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'], ); @@ -53,8 +126,11 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error updateCategory: ' . $info[2]); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); + if ($this->autoUpdateDb($info)) { + return $this->updateCategory($valuesTmp); + } + Minz_Log::error('SQL error updateCategory: ' . json_encode($info)); return false; } } @@ -63,27 +139,42 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable if ($id <= self::DEFAULTCATEGORYID) { return false; } - $sql = 'DELETE FROM `' . $this->prefix . 'category` WHERE id=?'; - $stm = $this->bd->prepare($sql); - - $values = array($id); - - if ($stm && $stm->execute($values)) { + $sql = 'DELETE FROM `_category` WHERE id=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id, PDO::PARAM_INT); + if ($stm && $stm->execute()) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error deleteCategory: ' . $info[2]); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); + Minz_Log::error('SQL error deleteCategory: ' . json_encode($info)); return false; } } - public function searchById($id) { - $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=?'; - $stm = $this->bd->prepare($sql); - - $values = array($id); + public function selectAll() { + $sql = 'SELECT id, name, attributes FROM `_category`'; + $stm = $this->pdo->query($sql); + 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)); + yield false; + } + } - $stm->execute($values); + public function searchById($id) { + $sql = 'SELECT * FROM `_category` WHERE id=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id, PDO::PARAM_INT); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $cat = self::daoToCategory($res); @@ -94,15 +185,15 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } } public function searchByName($name) { - $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE name=?'; - $stm = $this->bd->prepare($sql); - - $values = array($name); - - $stm->execute($values); + $sql = 'SELECT * FROM `_category` WHERE name=:name'; + $stm = $this->pdo->prepare($sql); + if ($stm == false) { + return false; + } + $stm->bindParam(':name', $name); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $cat = self::daoToCategory($res); - if (isset($cat[0])) { return $cat[0]; } else { @@ -110,30 +201,61 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } } + public function listSortedCategories($prePopulateFeeds = true, $details = false) { + $categories = $this->listCategories($prePopulateFeeds, $details); + + if (!is_array($categories)) { + return $categories; + } + + uasort($categories, function ($a, $b) { + $aPosition = $a->attributes('position'); + $bPosition = $b->attributes('position'); + if ($aPosition === $bPosition) { + return ($a->name() < $b->name()) ? -1 : 1; + } elseif (null === $aPosition) { + return 1; + } elseif (null === $bPosition) { + return -1; + } + return ($aPosition < $bPosition) ? -1 : 1; + }); + + return $categories; + } + 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 `' . $this->prefix . 'category` c ' - . 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category=c.id ' + . 'FROM `_category` c ' + . 'LEFT OUTER JOIN `_feed` f ON f.category=c.id ' . 'WHERE f.priority >= :priority_normal ' . 'GROUP BY f.id, c_id ' . 'ORDER BY c.name, f.name'; - $stm = $this->bd->prepare($sql); - $stm->execute(array(':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL)); - return self::daoToCategoryPrepopulated($stm->fetchAll(PDO::FETCH_ASSOC)); + $stm = $this->pdo->prepare($sql); + $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 `' . $this->prefix . 'category` ORDER BY name'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $sql = 'SELECT * FROM `_category` ORDER BY name'; + $stm = $this->pdo->query($sql); return self::daoToCategory($stm->fetchAll(PDO::FETCH_ASSOC)); } } public function getDefault() { - $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=' . self::DEFAULTCATEGORYID; - $stm = $this->bd->prepare($sql); - + $sql = 'SELECT * FROM `_category` WHERE id=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindValue(':id', self::DEFAULTCATEGORYID, PDO::PARAM_INT); $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $cat = self::daoToCategory($res); @@ -155,12 +277,12 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable $cat = new FreshRSS_Category(_t('gen.short.default_category')); $cat->_id(self::DEFAULTCATEGORYID); - $sql = 'INSERT INTO `' . $this->prefix . 'category`(id, name) VALUES(?, ?)'; - if (parent::$sharedDbType === 'pgsql') { + $sql = 'INSERT INTO `_category`(id, name) VALUES(?, ?)'; + if ($this->pdo->dbType() === 'pgsql') { //Force call to nextval() - $sql .= ' RETURNING nextval(\'"' . $this->prefix . 'category_id_seq"\');'; + $sql .= " RETURNING nextval('`_category_id_seq`');"; } - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $values = array( $cat->id(), @@ -168,9 +290,9 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable ); if ($stm && $stm->execute($values)) { - return $this->bd->lastInsertId('"' . $this->prefix . 'category_id_seq"'); + return $this->pdo->lastInsertId('`_category_id_seq`'); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error check default category: ' . json_encode($info)); return false; } @@ -179,31 +301,27 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function count() { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'category`'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $sql = 'SELECT COUNT(*) AS count FROM `_category`'; + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - return $res[0]['count']; } public function countFeed($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'feed` WHERE category=?'; - $stm = $this->bd->prepare($sql); - $values = array($id); - $stm->execute($values); + $sql = 'SELECT COUNT(*) AS count FROM `_feed` WHERE category=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id, PDO::PARAM_INT); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - return $res[0]['count']; } public function countNotRead($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE category=? AND e.is_read=0'; - $stm = $this->bd->prepare($sql); - $values = array($id); - $stm->execute($values); + $sql = 'SELECT COUNT(*) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id WHERE category=:id AND e.is_read=0'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id, PDO::PARAM_INT); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - return $res[0]['count']; } @@ -248,6 +366,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable $feedDao->daoToFeed($feedsDao, $previousLine['c_id']) ); $cat->_id($previousLine['c_id']); + $cat->_attributes('', $previousLine['c_attributes']); $list[$previousLine['c_id']] = $cat; $feedsDao = array(); //Prepare for next category @@ -264,6 +383,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable $feedDao->daoToFeed($feedsDao, $previousLine['c_id']) ); $cat->_id($previousLine['c_id']); + $cat->_attributes('', $previousLine['c_attributes']); $list[$previousLine['c_id']] = $cat; } @@ -282,6 +402,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 @@ +<?php + +class FreshRSS_CategoryDAOSQLite extends FreshRSS_CategoryDAO { + + protected function autoUpdateDb($errorInfo) { + if ($tableInfo = $this->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 ec6380df4..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 : ''; } @@ -257,6 +247,9 @@ class FreshRSS_ConfigurationSetter { private function _topline_read(&$data, $value) { $data['topline_read'] = $this->handleBool($value); } + private function _topline_display_authors(&$data, $value) { + $data['topline_display_authors'] = $this->handleBool($value); + } /** * The (not so long) list of setters for system configuration. @@ -386,4 +379,8 @@ class FreshRSS_ConfigurationSetter { $data['auto_update_url'] = $value; } + + private function _force_email_validation(&$data, $value) { + $data['force_email_validation'] = $this->handleBool($value); + } } diff --git a/app/Models/Context.php b/app/Models/Context.php index 95dc47c8c..e27330665 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -51,6 +51,24 @@ 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); + $keepMin = (int)FreshRSS_Context::$user_conf->param('keep_history_default', -5); + if ($oldEntries > 0 || $keepMin > -5) { //Freshrss < 1.15 + $archiving = FreshRSS_Context::$user_conf->archiving; + $archiving['keep_max'] = false; + if ($oldEntries > 0) { + $archiving['keep_period'] = 'P' . $oldEntries . 'M'; + } + if ($keepMin > 0) { + $archiving['keep_min'] = $keepMin; + } elseif ($keepMin == -1) { //Infinite + $archiving['keep_period'] = false; + $archiving['keep_min'] = false; + } + FreshRSS_Context::$user_conf->archiving = $archiving; + } } /** diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php index b331eccc3..a36b469b1 100644 --- a/app/Models/DatabaseDAO.php +++ b/app/Models/DatabaseDAO.php @@ -8,25 +8,50 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { //MySQL error codes const ER_BAD_FIELD_ERROR = '42S22'; const ER_BAD_TABLE_ERROR = '42S02'; - const ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = '1366'; + const ER_DATA_TOO_LONG = '1406'; //MySQL InnoDB maximum index length for UTF8MB4 //https://dev.mysql.com/doc/refman/8.0/en/innodb-restrictions.html const LENGTH_INDEX_UNICODE = 191; + public function create() { + 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']); + return $this->pdo->exec($sql) !== false; + } catch (PDOException $e) { + $_SESSION['bd_error'] = $e->getMessage(); + syslog(LOG_DEBUG, __method__ . ' warning: ' . $e->getMessage()); + return false; + } + } + + public function testConnection() { + try { + $sql = 'SELECT 1'; + $stm = $this->pdo->query($sql); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); + return $res != false; + } catch (PDOException $e) { + $_SESSION['bd_error'] = $e->getMessage(); + syslog(LOG_DEBUG, __method__ . ' warning: ' . $e->getMessage()); + return false; + } + } + public function tablesAreCorrect() { - $sql = 'SHOW TABLES'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query('SHOW TABLES'); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $tables = array( - $this->prefix . 'category' => false, - $this->prefix . 'feed' => false, - $this->prefix . 'entry' => false, - $this->prefix . 'entrytmp' => false, - $this->prefix . 'tag' => false, - $this->prefix . 'entrytag' => false, + $this->pdo->prefix() . 'category' => false, + $this->pdo->prefix() . 'feed' => false, + $this->pdo->prefix() . 'entry' => false, + $this->pdo->prefix() . 'entrytmp' => false, + $this->pdo->prefix() . 'tag' => false, + $this->pdo->prefix() . 'entrytag' => false, ); foreach ($res as $value) { $tables[array_pop($value)] = true; @@ -36,10 +61,8 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { } public function getSchema($table) { - $sql = 'DESC ' . $this->prefix . $table; - $stm = $this->bd->prepare($sql); - $stm->execute(); - + $sql = 'DESC `_' . $table . '`'; + $stm = $this->pdo->query($sql); return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); } @@ -63,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', )); } @@ -119,9 +142,9 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { $values = array($db['base']); if (!$all) { $sql .= ' AND table_name LIKE ?'; - $values[] = $this->prefix . '%'; + $values[] = $this->pdo->prefix() . '%'; } - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; @@ -132,30 +155,23 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { $tables = array('category', 'feed', 'entry', 'entrytmp', 'tag', 'entrytag'); foreach ($tables as $table) { - $sql = 'OPTIMIZE TABLE `' . $this->prefix . $table . '`'; //MySQL - $stm = $this->bd->prepare($sql); - $ok &= $stm != false; - if ($stm) { - $ok &= $stm->execute(); - } + $sql = 'OPTIMIZE TABLE `_' . $table . '`'; //MySQL + $ok &= ($this->pdo->exec($sql) !== false); } return $ok; } public function ensureCaseInsensitiveGuids() { $ok = true; - $db = FreshRSS_Context::$system_conf->db; - if ($db['type'] === 'mysql') { - include_once(APP_PATH . '/SQL/install.sql.mysql.php'); - if (defined('SQL_UPDATE_GUID_LATIN1_BIN')) { //FreshRSS 1.12 - try { - $sql = sprintf(SQL_UPDATE_GUID_LATIN1_BIN, $this->prefix); - $stm = $this->bd->prepare($sql); - $ok = $stm->execute(); - } catch (Exception $e) { - $ok = false; - Minz_Log::error('FreshRSS_DatabaseDAO::ensureCaseInsensitiveGuids error: ' . $e->getMessage()); - } + if ($this->pdo->dbType() === 'mysql') { + include(APP_PATH . '/SQL/install.sql.mysql.php'); + + $ok = false; + try { + $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()); } } return $ok; @@ -164,4 +180,168 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { public function minorDbMaintenance() { $this->ensureCaseInsensitiveGuids(); } + + private static function stdError($error) { + if (defined('STDERR')) { + fwrite(STDERR, $error . "\n"); + } + Minz_Log::error($error); + return false; + } + + const SQLITE_EXPORT = 1; + const SQLITE_IMPORT = 2; + + public function dbCopy($filename, $mode, $clearFirst = false) { + $error = ''; + + $userDAO = FreshRSS_Factory::createUserDao(); + $catDAO = FreshRSS_Factory::createCategoryDao(); + $feedDAO = FreshRSS_Factory::createFeedDao(); + $entryDAO = FreshRSS_Factory::createEntryDao(); + $tagDAO = FreshRSS_Factory::createTagDao(); + + switch ($mode) { + case self::SQLITE_EXPORT: + if (@filesize($filename) > 0) { + $error = 'Error: SQLite export file already exists: ' . $filename; + } + break; + case self::SQLITE_IMPORT: + if (!is_readable($filename)) { + $error = 'Error: SQLite import file is not readable: ' . $filename; + } elseif ($clearFirst) { + $userDAO->deleteUser(); + if ($this->pdo->dbType() === 'sqlite') { + //We cannot just delete the .sqlite file otherwise PDO gets buggy. + //SQLite is the only one with database-level optimization, instead of at table level. + $this->optimize(); + } + } else { + $nbEntries = $entryDAO->countUnreadRead(); + if (!empty($nbEntries['all'])) { + $error = 'Error: Destination database already contains some entries!'; + } + } + break; + default: + $error = 'Invalid copy mode!'; + break; + } + if ($error != '') { + return self::stdError($error); + } + + $sqlite = null; + + try { + $sqlite = new MinzPDOSQLite('sqlite:' . $filename); + } catch (Exception $e) { + $error = 'Error while initialising SQLite copy: ' . $e->getMessage(); + return self::stdError($error); + } + + Minz_ModelPdo::clean(); + $userDAOSQLite = new FreshRSS_UserDAO('', $sqlite); + $categoryDAOSQLite = new FreshRSS_CategoryDAOSQLite('', $sqlite); + $feedDAOSQLite = new FreshRSS_FeedDAOSQLite('', $sqlite); + $entryDAOSQLite = new FreshRSS_EntryDAOSQLite('', $sqlite); + $tagDAOSQLite = new FreshRSS_TagDAOSQLite('', $sqlite); + + switch ($mode) { + case self::SQLITE_EXPORT: + $userFrom = $userDAO; $userTo = $userDAOSQLite; + $catFrom = $catDAO; $catTo = $categoryDAOSQLite; + $feedFrom = $feedDAO; $feedTo = $feedDAOSQLite; + $entryFrom = $entryDAO; $entryTo = $entryDAOSQLite; + $tagFrom = $tagDAO; $tagTo = $tagDAOSQLite; + break; + case self::SQLITE_IMPORT: + $userFrom = $userDAOSQLite; $userTo = $userDAO; + $catFrom = $categoryDAOSQLite; $catTo = $catDAO; + $feedFrom = $feedDAOSQLite; $feedTo = $feedDAO; + $entryFrom = $entryDAOSQLite; $entryTo = $entryDAO; + $tagFrom = $tagDAOSQLite; $tagTo = $tagDAO; + break; + } + + $idMaps = []; + + if (defined('STDERR')) { + fwrite(STDERR, "Start SQL copy…\n"); + } + + $userTo->createUser(); + + $catTo->beginTransaction(); + foreach ($catFrom->selectAll() as $category) { + $cat = $catTo->searchByName($category['name']); //Useful for the default category + if ($cat != null) { + $catId = $cat->id(); + } else { + $catId = $catTo->addCategory($category); + if ($catId == false) { + $error = 'Error during SQLite copy of categories!'; + return self::stdError($error); + } + } + $idMaps['c' . $category['id']] = $catId; + } + foreach ($feedFrom->selectAll() as $feed) { + $feed['category'] = empty($idMaps['c' . $feed['category']]) ? FreshRSS_CategoryDAO::DEFAULTCATEGORYID : $idMaps['c' . $feed['category']]; + $feedId = $feedTo->addFeed($feed); + if ($feedId == false) { + $error = 'Error during SQLite copy of feeds!'; + return self::stdError($error); + } + $idMaps['f' . $feed['id']] = $feedId; + } + $catTo->commit(); + + $nbEntries = $entryFrom->count(); + $n = 0; + $entryTo->beginTransaction(); + foreach ($entryFrom->selectAll() as $entry) { + $n++; + if (!empty($idMaps['f' . $entry['id_feed']])) { + $entry['id_feed'] = $idMaps['f' . $entry['id_feed']]; + if (!$entryTo->addEntry($entry, false)) { + $error = 'Error during SQLite copy of entries!'; + return self::stdError($error); + } + } + if ($n % 100 === 1 && defined('STDERR')) { //Display progression + fwrite(STDERR, "\033[0G" . $n . '/' . $nbEntries); + } + } + if (defined('STDERR')) { + fwrite(STDERR, "\033[0G" . $n . '/' . $nbEntries . "\n"); + } + $entryTo->commit(); + $feedTo->updateCachedValues(); + + $idMaps = []; + + $tagTo->beginTransaction(); + foreach ($tagFrom->selectAll() as $tag) { + $tagId = $tagTo->addTag($tag); + if ($tagId == false) { + $error = 'Error during SQLite copy of tags!'; + return self::stdError($error); + } + $idMaps['t' . $tag['id']] = $tagId; + } + foreach ($tagFrom->selectEntryTag() as $entryTag) { + if (!empty($idMaps['t' . $entryTag['id_tag']])) { + $entryTag['id_tag'] = $idMaps['t' . $entryTag['id_tag']]; + if (!$tagTo->tagEntry($entryTag['id_tag'], $entryTag['id_entry'])) { + $error = 'Error during SQLite copy of entry-tags!'; + return self::stdError($error); + } + } + } + $tagTo->commit(); + + return true; + } } diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php index 8582b5719..1a6b3599e 100644 --- a/app/Models/DatabaseDAOPGSQL.php +++ b/app/Models/DatabaseDAOPGSQL.php @@ -13,18 +13,18 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite { $db = FreshRSS_Context::$system_conf->db; $dbowner = $db['user']; $sql = 'SELECT * FROM pg_catalog.pg_tables where tableowner=?'; - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $values = array($dbowner); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $tables = array( - $this->prefix . 'category' => false, - $this->prefix . 'feed' => false, - $this->prefix . 'entry' => false, - $this->prefix . 'entrytmp' => false, - $this->prefix . 'tag' => false, - $this->prefix . 'entrytag' => false, + $this->pdo->prefix() . 'category' => false, + $this->pdo->prefix() . 'feed' => false, + $this->pdo->prefix() . 'entry' => false, + $this->pdo->prefix() . 'entrytmp' => false, + $this->pdo->prefix() . 'tag' => false, + $this->pdo->prefix() . 'entrytag' => false, ); foreach ($res as $value) { $tables[array_pop($value)] = true; @@ -35,8 +35,8 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite { public function getSchema($table) { $sql = 'select column_name as field, data_type as type, column_default as default, is_nullable as null from INFORMATION_SCHEMA.COLUMNS where table_name = ?'; - $stm = $this->bd->prepare($sql); - $stm->execute(array($this->prefix . $table)); + $stm = $this->pdo->prepare($sql); + $stm->execute(array($this->pdo->prefix() . $table)); return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); } @@ -49,12 +49,23 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite { ); } - public function size($all = true) { - $db = FreshRSS_Context::$system_conf->db; - $sql = 'SELECT pg_size_pretty(pg_database_size(?))'; - $values = array($db['base']); - $stm = $this->bd->prepare($sql); - $stm->execute($values); + public function size($all = false) { + if ($all) { + $db = FreshRSS_Context::$system_conf->db; + $sql = 'SELECT pg_database_size(:base)'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':base', $db['base']); + $stm->execute(); + } else { + $sql = "SELECT " + . "pg_total_relation_size('{$this->pdo->prefix()}category') + " + . "pg_total_relation_size('{$this->pdo->prefix()}feed') + " + . "pg_total_relation_size('{$this->pdo->prefix()}entry') + " + . "pg_total_relation_size('{$this->pdo->prefix()}entrytmp') + " + . "pg_total_relation_size('{$this->pdo->prefix()}tag') + " + . "pg_total_relation_size('{$this->pdo->prefix()}entrytag')"; + $stm = $this->pdo->query($sql); + } $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; } @@ -64,12 +75,8 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAOSQLite { $tables = array('category', 'feed', 'entry', 'entrytmp', 'tag', 'entrytag'); foreach ($tables as $table) { - $sql = 'VACUUM `' . $this->prefix . $table . '`'; - $stm = $this->bd->prepare($sql); - $ok &= $stm != false; - if ($stm) { - $ok &= $stm->execute(); - } + $sql = 'VACUUM `_' . $table . '`'; + $ok &= ($this->pdo->exec($sql) !== false); } return $ok; } diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php index a93a209b2..413e7ee09 100644 --- a/app/Models/DatabaseDAOSQLite.php +++ b/app/Models/DatabaseDAOSQLite.php @@ -6,17 +6,16 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { public function tablesAreCorrect() { $sql = 'SELECT name FROM sqlite_master WHERE type="table"'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $tables = array( - 'category' => false, - 'feed' => false, - 'entry' => false, - 'entrytmp' => false, - 'tag' => false, - 'entrytag' => false, + $this->pdo->prefix() . 'category' => false, + $this->pdo->prefix() . 'feed' => false, + $this->pdo->prefix() . 'entry' => false, + $this->pdo->prefix() . 'entrytmp' => false, + $this->pdo->prefix() . 'tag' => false, + $this->pdo->prefix() . 'entrytag' => false, ); foreach ($res as $value) { $tables[$value['name']] = true; @@ -27,9 +26,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { public function getSchema($table) { $sql = 'PRAGMA table_info(' . $table . ')'; - $stm = $this->bd->prepare($sql); - $stm->execute(); - + $stm = $this->pdo->query($sql); return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC)); } @@ -57,15 +54,18 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { } public function size($all = false) { - return @filesize(join_path(DATA_PATH, 'users', $this->current_user, 'db.sqlite')); + $sum = 0; + if ($all) { + foreach (glob(DATA_PATH . '/users/*/db.sqlite') as $filename) { + $sum += @filesize($filename); + } + } else { + $sum = @filesize(DATA_PATH . '/users/' . $this->current_user . '/db.sqlite'); + } + return $sum; } public function optimize() { - $sql = 'VACUUM'; - $stm = $this->bd->prepare($sql); - if ($stm) { - return $stm->execute(); - } - return false; + return $this->pdo->exec('VACUUM') !== false; } } diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 3bb977283..d90f828bc 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -327,7 +327,7 @@ class FreshRSS_Entry extends Minz_Model { } $ch = curl_init(); - curl_setopt_array($ch, array( + curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_REFERER => SimplePie_Misc::url_remove_credentials($url), CURLOPT_HTTPHEADER => array('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'), @@ -337,13 +337,9 @@ class FreshRSS_Entry extends Minz_Model { //CURLOPT_FAILONERROR => true; CURLOPT_MAXREDIRS => 4, CURLOPT_RETURNTRANSFER => true, - )); - if (version_compare(PHP_VERSION, '5.6.0') >= 0 || ini_get('open_basedir') == '') { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //Keep option separated for open_basedir PHP bug 65646 - } - if (defined('CURLOPT_ENCODING')) { - curl_setopt($ch, CURLOPT_ENCODING, ''); //Enable all encodings - } + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_ENCODING => '', //Enable all encodings + ]); curl_setopt_array($ch, $system_conf->curl_options); if (isset($attributes['ssl_verify'])) { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $attributes['ssl_verify'] ? 2 : 0); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index b47cd55ad..99e99f463 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -3,11 +3,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function isCompressed() { - return parent::$sharedDbType === 'mysql'; + return true; } public function hasNativeHex() { - return parent::$sharedDbType !== 'sqlite'; + return true; } public function sqlHexDecode($x) { @@ -19,106 +19,40 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } //TODO: Move the database auto-updates to DatabaseDAO - protected function addColumn($name) { - Minz_Log::warning('FreshRSS_EntryDAO::addColumn: ' . $name); - $hasTransaction = false; + protected function createEntryTempTable() { + $ok = false; + $hadTransaction = $this->pdo->inTransaction(); + if ($hadTransaction) { + $this->pdo->commit(); + } try { - $stm = null; - if ($name === 'lastSeen') { //v1.1.1 - if (!$this->bd->inTransaction()) { - $this->bd->beginTransaction(); - $hasTransaction = true; - } - $stm = $this->bd->prepare('ALTER TABLE `' . $this->prefix . 'entry` ADD COLUMN `lastSeen` INT(11) DEFAULT 0'); - if ($stm && $stm->execute()) { - $stm = $this->bd->prepare('CREATE INDEX entry_lastSeen_index ON `' . $this->prefix . 'entry`(`lastSeen`);'); //"IF NOT EXISTS" does not exist in MySQL 5.7 - if ($stm && $stm->execute()) { - if ($hasTransaction) { - $this->bd->commit(); - } - return true; - } - } - if ($hasTransaction) { - $this->bd->rollBack(); - } - } elseif ($name === 'hash') { //v1.1.1 - $stm = $this->bd->prepare('ALTER TABLE `' . $this->prefix . 'entry` ADD COLUMN hash BINARY(16)'); - return $stm && $stm->execute(); - } - } catch (Exception $e) { - Minz_Log::error('FreshRSS_EntryDAO::addColumn error: ' . $e->getMessage()); - if ($hasTransaction) { - $this->bd->rollBack(); - } + 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; + } catch (Exception $ex) { + Minz_Log::error(__method__ . ' error: ' . $ex->getMessage()); } - return false; + if ($hadTransaction) { + $this->pdo->beginTransaction(); + } + return $ok; } - private $triedUpdateToUtf8mb4 = false; - - //TODO: Move the database auto-updates to DatabaseDAO - protected function updateToUtf8mb4() { - if ($this->triedUpdateToUtf8mb4) { + private function updateToMediumBlob() { + if ($this->pdo->dbType() !== 'mysql') { return false; } - $this->triedUpdateToUtf8mb4 = true; - $db = FreshRSS_Context::$system_conf->db; - if ($db['type'] === 'mysql') { - include_once(APP_PATH . '/SQL/install.sql.mysql.php'); - if (defined('SQL_UPDATE_UTF8MB4')) { - Minz_Log::warning('Updating MySQL to UTF8MB4...'); //v1.5.0 - $hadTransaction = $this->bd->inTransaction(); - if ($hadTransaction) { - $this->bd->commit(); - } - $ok = false; - try { - $sql = sprintf(SQL_UPDATE_UTF8MB4, $this->prefix, $db['base']); - $stm = $this->bd->prepare($sql); - $ok = $stm->execute(); - } catch (Exception $e) { - Minz_Log::error('FreshRSS_EntryDAO::updateToUtf8mb4 error: ' . $e->getMessage()); - } - if ($hadTransaction) { - $this->bd->beginTransaction(); - //NB: Transaction not starting. Why? (tested on PHP 7.0.8-0ubuntu and MySQL 5.7.13-0ubuntu) - } - return $ok; - } - } - return false; - } + Minz_Log::warning('Update MySQL table to use MEDIUMBLOB...'); - //TODO: Move the database auto-updates to DatabaseDAO - protected function createEntryTempTable() { - $ok = false; - $hadTransaction = $this->bd->inTransaction(); - if ($hadTransaction) { - $this->bd->commit(); - } + $sql = <<<'SQL' +ALTER TABLE `_entry` MODIFY `content_bin` MEDIUMBLOB; +ALTER TABLE `_entrytmp` MODIFY `content_bin` MEDIUMBLOB; +SQL; try { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - Minz_Log::warning('SQL CREATE TABLE entrytmp...'); - if (defined('SQL_CREATE_TABLE_ENTRYTMP')) { - $sql = sprintf(SQL_CREATE_TABLE_ENTRYTMP, $this->prefix); - $stm = $this->bd->prepare($sql); - $ok = $stm && $stm->execute(); - } else { - global $SQL_CREATE_TABLE_ENTRYTMP; - $ok = !empty($SQL_CREATE_TABLE_ENTRYTMP); - foreach ($SQL_CREATE_TABLE_ENTRYTMP as $instruction) { - $sql = sprintf($instruction, $this->prefix); - $stm = $this->bd->prepare($sql); - $ok &= $stm && $stm->execute(); - } - } + $ok = $this->pdo->exec($sql) !== false; } catch (Exception $e) { - Minz_Log::error('FreshRSS_EntryDAO::createEntryTempTable error: ' . $e->getMessage()); - } - if ($hadTransaction) { - $this->bd->beginTransaction(); + $ok = false; + Minz_Log::error(__method__ . ' error: ' . $e->getMessage()); } return $ok; } @@ -126,14 +60,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { //TODO: Move the database auto-updates to DatabaseDAO protected function autoUpdateDb($errorInfo) { if (isset($errorInfo[0])) { - if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_FIELD_ERROR) { - //autoAddColumn - foreach (array('lastSeen', 'hash') as $column) { - if (stripos($errorInfo[2], $column) !== false) { - return $this->addColumn($column); - } - } - } elseif ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_TABLE_ERROR) { + if ($errorInfo[0] === FreshRSS_DatabaseDAO::ER_BAD_TABLE_ERROR) { if (stripos($errorInfo[2], 'tag') !== false) { $tagDAO = FreshRSS_Factory::createTagDao(); return $tagDAO->createTagTable(); //v1.12.0 @@ -143,8 +70,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } if (isset($errorInfo[1])) { - if ($errorInfo[1] == FreshRSS_DatabaseDAO::ER_TRUNCATED_WRONG_VALUE_FOR_FIELD) { - return $this->updateToUtf8mb4(); //v1.5.0 + if ($errorInfo[1] == FreshRSS_DatabaseDAO::ER_DATA_TOO_LONG) { + if (stripos($errorInfo[2], 'content_bin') !== false) { + return $this->updateToMediumBlob(); //v1.15.0 + } } } return false; @@ -152,9 +81,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { private $addEntryPrepared = null; - public function addEntry($valuesTmp) { + public function addEntry($valuesTmp, $useTmpTable = true) { if ($this->addEntryPrepared == null) { - $sql = 'INSERT INTO `' . $this->prefix . 'entrytmp` (id, guid, title, author, ' + $sql = 'INSERT INTO `_' . ($useTmpTable ? 'entrytmp' : 'entry') . '` (id, guid, title, author, ' . ($this->isCompressed() ? 'content_bin' : 'content') . ', link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . 'VALUES(:id, :guid, :title, :author, ' @@ -162,7 +91,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . ', :link, :date, :last_seen, ' . $this->sqlHexDecode(':hash') . ', :is_read, :is_favorite, :id_feed, :tags)'; - $this->addEntryPrepared = $this->bd->prepare($sql); + $this->addEntryPrepared = $this->pdo->prepare($sql); } if ($this->addEntryPrepared) { $this->addEntryPrepared->bindParam(':id', $valuesTmp['id']); @@ -178,7 +107,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $valuesTmp['link'] = safe_ascii($valuesTmp['link']); $this->addEntryPrepared->bindParam(':link', $valuesTmp['link']); $this->addEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); - $valuesTmp['lastSeen'] = time(); + if (empty($valuesTmp['lastSeen'])) { + $valuesTmp['lastSeen'] = time(); + } $this->addEntryPrepared->bindParam(':last_seen', $valuesTmp['lastSeen'], PDO::PARAM_INT); $valuesTmp['is_read'] = $valuesTmp['is_read'] ? 1 : 0; $this->addEntryPrepared->bindParam(':is_read', $valuesTmp['is_read'], PDO::PARAM_INT); @@ -191,14 +122,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($this->hasNativeHex()) { $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']); } else { - $valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); //hex2bin() is PHP5.4+ + $valuesTmp['hashBin'] = hex2bin($valuesTmp['hash']); $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']); } } if ($this->addEntryPrepared && $this->addEntryPrepared->execute()) { return true; } else { - $info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo(); + $info = $this->addEntryPrepared == null ? $this->pdo->errorInfo() : $this->addEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { $this->addEntryPrepared = null; return $this->addEntry($valuesTmp); @@ -211,22 +142,26 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function commitNewEntries() { - $sql = 'SET @rank=(SELECT MAX(id) - COUNT(*) FROM `' . $this->prefix . 'entrytmp`); ' . //MySQL-specific - 'INSERT IGNORE INTO `' . $this->prefix . 'entry` - ( - id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags - ) ' . - 'SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags - FROM `' . $this->prefix . 'entrytmp` - ORDER BY date; ' . - 'DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= @rank;'; - $hadTransaction = $this->bd->inTransaction(); + $sql = <<<'SQL' +SET @rank=(SELECT MAX(id) - COUNT(*) FROM `_entrytmp`); + +INSERT IGNORE INTO `_entry` ( + id, guid, title, author, content_bin, link, date, `lastSeen`, + hash, is_read, is_favorite, id_feed, tags +) +SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags +FROM `_entrytmp` +ORDER BY date; + +DELETE FROM `_entrytmp` WHERE id <= @rank;'; +SQL; + $hadTransaction = $this->pdo->inTransaction(); if (!$hadTransaction) { - $this->bd->beginTransaction(); + $this->pdo->beginTransaction(); } - $result = $this->bd->exec($sql) !== false; + $result = $this->pdo->exec($sql) !== false; if (!$hadTransaction) { - $this->bd->commit(); + $this->pdo->commit(); } return $result; } @@ -239,7 +174,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } if ($this->updateEntryPrepared === null) { - $sql = 'UPDATE `' . $this->prefix . 'entry` ' + $sql = 'UPDATE `_entry` ' . 'SET title=:title, author=:author, ' . ($this->isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content') . ', link=:link, date=:date, `lastSeen`=:last_seen, ' @@ -247,7 +182,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . ', ' . ($valuesTmp['is_read'] === null ? '' : 'is_read=:is_read, ') . 'tags=:tags ' . 'WHERE id_feed=:id_feed AND guid=:guid'; - $this->updateEntryPrepared = $this->bd->prepare($sql); + $this->updateEntryPrepared = $this->pdo->prepare($sql); } $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); @@ -273,14 +208,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($this->hasNativeHex()) { $this->updateEntryPrepared->bindParam(':hash', $valuesTmp['hash']); } else { - $valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); //hex2bin() is PHP5.4+ + $valuesTmp['hashBin'] = hex2bin($valuesTmp['hash']); $this->updateEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']); } if ($this->updateEntryPrepared && $this->updateEntryPrepared->execute()) { return true; } else { - $info = $this->updateEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->updateEntryPrepared->errorInfo(); + $info = $this->updateEntryPrepared == null ? $this->pdo->errorInfo() : $this->updateEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->updateEntry($valuesTmp); } @@ -308,16 +243,16 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return 0; } FreshRSS_UserDAO::touch(); - $sql = 'UPDATE `' . $this->prefix . 'entry` ' + $sql = 'UPDATE `_entry` ' . 'SET is_favorite=? ' . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)'; $values = array($is_favorite ? 1 : 0); $values = array_merge($values, $ids); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markFavorite: ' . $info[2]); return false; } @@ -335,11 +270,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { * @return boolean */ protected function updateCacheUnreads($catId = false, $feedId = false) { - $sql = 'UPDATE `' . $this->prefix . 'feed` f ' + $sql = 'UPDATE `_feed` f ' . 'LEFT OUTER JOIN (' . 'SELECT e.id_feed, ' . 'COUNT(*) AS nbUnreads ' - . 'FROM `' . $this->prefix . 'entry` e ' + . 'FROM `_entry` e ' . 'WHERE e.is_read=0 ' . 'GROUP BY e.id_feed' . ') x ON x.id_feed=f.id ' @@ -358,11 +293,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $sql .= ' f.category=?'; $values[] = $catId; } - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return true; } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error updateCacheUnreads: ' . $info[2]); return false; } @@ -392,14 +327,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $affected; } - $sql = 'UPDATE `' . $this->prefix . 'entry` ' + $sql = 'UPDATE `_entry` ' . 'SET is_read=? ' . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)'; $values = array($is_read ? 1 : 0); $values = array_merge($values, $ids); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if (!($stm && $stm->execute($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markRead: ' . $info[2]); return false; } @@ -409,16 +344,16 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } return $affected; } else { - $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' + $sql = 'UPDATE `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id ' . 'SET e.is_read=?,' . 'f.`cache_nbUnreads`=f.`cache_nbUnreads`' . ($is_read ? '-' : '+') . '1 ' . 'WHERE e.id=? AND e.is_read=?'; $values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markRead: ' . $info[2]); return false; } @@ -453,7 +388,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { Minz_Log::debug('Calling markReadEntries(0) is deprecated!'); } - $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' + $sql = 'UPDATE `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id ' . 'SET e.is_read=? ' . 'WHERE e.is_read <> ? AND e.id <= ?'; if ($onlyFavorites) { @@ -465,9 +400,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadEntries: ' . $info[2]); return false; } @@ -496,16 +431,16 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { Minz_Log::debug('Calling markReadCat(0) is deprecated!'); } - $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id ' + $sql = 'UPDATE `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id ' . 'SET e.is_read=? ' . 'WHERE f.category=? AND e.is_read <> ? AND e.id <= ?'; $values = array($is_read ? 1 : 0, $id, $is_read ? 1 : 0, $idMax); list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadCat: ' . $info[2]); return false; } @@ -533,39 +468,39 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $idMax = time() . '000000'; Minz_Log::debug('Calling markReadFeed(0) is deprecated!'); } - $this->bd->beginTransaction(); + $this->pdo->beginTransaction(); - $sql = 'UPDATE `' . $this->prefix . 'entry` ' + $sql = 'UPDATE `_entry` ' . 'SET is_read=? ' . 'WHERE id_feed=? AND is_read <> ? AND id <= ?'; $values = array($is_read ? 1 : 0, $id_feed, $is_read ? 1 : 0, $idMax); list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadFeed: ' . $info[2] . ' with SQL: ' . $sql . $search); - $this->bd->rollBack(); + $this->pdo->rollBack(); return false; } $affected = $stm->rowCount(); if ($affected > 0) { - $sql = 'UPDATE `' . $this->prefix . 'feed` ' + $sql = 'UPDATE `_feed` ' . 'SET `cache_nbUnreads`=`cache_nbUnreads`-' . $affected - . ' WHERE id=?'; - $values = array($id_feed); - $stm = $this->bd->prepare($sql); - if (!($stm && $stm->execute($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + . ' WHERE id=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id_feed, PDO::PARAM_INT); + if (!($stm && $stm->execute())) { + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadFeed cache: ' . $info[2]); - $this->bd->rollBack(); + $this->pdo->rollBack(); return false; } } - $this->bd->commit(); + $this->pdo->commit(); return $affected; } @@ -582,7 +517,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { Minz_Log::debug('Calling markReadTag(0) is deprecated!'); } - $sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'entrytag` et ON et.id_entry = e.id ' + $sql = 'UPDATE `_entry` e INNER JOIN `_entrytag` et ON et.id_entry = e.id ' . 'SET e.is_read = ? ' . 'WHERE ' . ($id == '' ? '' : 'et.id_tag = ? AND ') @@ -596,9 +531,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadTag: ' . $info[2]); return false; } @@ -609,48 +544,86 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $affected; } - public function cleanOldEntries($id_feed, $date_min, $keep = 15) { //Remember to call updateCachedValue($id_feed) or updateCachedValues() just after - $sql = 'DELETE FROM `' . $this->prefix . 'entry` ' - . 'WHERE id_feed=:id_feed 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 `' . $this->prefix . 'entry` e3 WHERE e3.id_feed=:id_feed) recent) ' //Do not remove the most newly seen articles, plus a few seconds of tolerance - . 'AND id NOT IN (SELECT id_entry FROM `' . $this->prefix . 'entrytag`) ' //Do not purge tagged entries - . 'AND id NOT IN (SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=:id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select: MySQL doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery' - $stm = $this->bd->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_feed', $id_feed, PDO::PARAM_INT); - $stm->bindParam(':id_max', $id_max, PDO::PARAM_STR); - $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) { + //Double SELECT for MySQL workaround ERROR 1093 (HY000) + $sql .= ' AND `lastSeen` < (SELECT `lastSeen`' + . ' FROM (SELECT e2.`lastSeen` FROM `_entry` e2 WHERE e2.id_feed = :id_feed2' + . ' ORDER BY e2.`lastSeen` DESC LIMIT 1 OFFSET :keep_min) last_seen2)'; + $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 maxlastseen' + . ' FROM (SELECT MAX(e3.`lastSeen`) AS maxlastseen FROM `_entry` e3 WHERE e3.id_feed = :id_feed3) last_seen3)'; + $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 `lastSeen`' + . ' FROM (SELECT e4.`lastSeen` FROM `_entry` e4 WHERE e4.id_feed = :id_feed4' + . ' ORDER BY e4.`lastSeen` DESC LIMIT 1 OFFSET :keep_max) last_seen4)'; + $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 ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $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; } } + public function selectAll() { + $sql = 'SELECT id, guid, title, author, ' + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, `lastSeen`, ' . $this->sqlHexEncode('hash') . ' AS hash, is_read, is_favorite, id_feed, tags ' + . 'FROM `_entry`'; + $stm = $this->pdo->query($sql); + while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + yield $row; + } + } + public function searchByGuid($id_feed, $guid) { // un guid est unique pour un flux donné $sql = 'SELECT id, guid, title, author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') . ', link, date, is_read, is_favorite, id_feed, tags ' - . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; - $stm = $this->bd->prepare($sql); - - $values = array( - $id_feed, - $guid, - ); - - $stm->execute($values); + . 'FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id_feed', $id_feed, PDO::PARAM_INT); + $stm->bindParam(':guid', $guid); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $entries = self::daoToEntries($res); return isset($entries[0]) ? $entries[0] : null; @@ -660,22 +633,21 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $sql = 'SELECT id, guid, title, author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') . ', link, date, is_read, is_favorite, id_feed, tags ' - . 'FROM `' . $this->prefix . 'entry` WHERE id=?'; - $stm = $this->bd->prepare($sql); - - $values = array($id); - - $stm->execute($values); + . 'FROM `_entry` WHERE id=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id, PDO::PARAM_INT); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $entries = self::daoToEntries($res); return isset($entries[0]) ? $entries[0] : null; } public function searchIdByGuid($id_feed, $guid) { - $sql = 'SELECT id FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; - $stm = $this->bd->prepare($sql); - $values = array($id_feed, $guid); - $stm->execute($values); + $sql = 'SELECT id FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id_feed', $id_feed, PDO::PARAM_INT); + $stm->bindParam(':guid', $guid); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return isset($res[0]) ? $res[0] : null; } @@ -859,7 +831,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $where .= '1=1 '; break; case 'ST': //Starred or tagged - $where .= 'e.is_favorite=1 OR EXISTS (SELECT et2.id_tag FROM `' . $this->prefix . 'entrytag` et2 WHERE et2.id_entry = e.id) '; + $where .= 'e.is_favorite=1 OR EXISTS (SELECT et2.id_tag FROM `_entrytag` et2 WHERE et2.id_entry = e.id) '; break; default: throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!'); @@ -870,9 +842,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return array(array_merge($values, $searchValues), 'SELECT ' . ($type === 'T' ? 'DISTINCT ' : '') - . 'e.id FROM `' . $this->prefix . 'entry` e ' - . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' - . ($type === 't' || $type === 'T' ? 'INNER JOIN `' . $this->prefix . 'entrytag` et ON et.id_entry = e.id ' : '') + . 'e.id FROM `_entry` e ' + . 'INNER JOIN `_feed` f ON e.id_feed = f.id ' + . ($type === 't' || $type === 'T' ? 'INNER JOIN `_entrytag` et ON et.id_entry = e.id ' : '') . 'WHERE ' . $where . $search . 'ORDER BY e.id ' . $order @@ -885,17 +857,17 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') . ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags ' - . 'FROM `' . $this->prefix . 'entry` e0 ' + . 'FROM `_entry` e0 ' . 'INNER JOIN (' . $sql . ') e2 ON e2.id=e0.id ' . 'ORDER BY e0.id ' . $order; - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return $stm; } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error listWhereRaw: ' . $info[2]); return false; } @@ -918,11 +890,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $sql = 'SELECT id, guid, title, author, ' . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') . ', link, date, is_read, is_favorite, id_feed, tags ' - . 'FROM `' . $this->prefix . 'entry` ' + . 'FROM `_entry` ' . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?) ' . 'ORDER BY id ' . $order; - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $stm->execute($ids); return self::daoToEntries($stm->fetchAll(PDO::FETCH_ASSOC)); } @@ -930,7 +902,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function listIdsWhere($type = 'a', $id = '', $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $limit = 1, $firstId = '', $filters = null) { //For API list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $stm->execute($values); return $stm->fetchAll(PDO::FETCH_COLUMN, 0); @@ -941,8 +913,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return array(); } $guids = array_unique($guids); - $sql = 'SELECT guid, ' . $this->sqlHexEncode('hash') . ' AS hex_hash FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT guid, ' . $this->sqlHexEncode('hash') . ' AS hex_hash FROM `_entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)'; + $stm = $this->pdo->prepare($sql); $values = array($id_feed); $values = array_merge($values, $guids); if ($stm && $stm->execute($values)) { @@ -953,7 +925,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } return $result; } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->listHashForFeedGuids($id_feed, $guids); } @@ -967,8 +939,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if (count($guids) < 1) { return 0; } - $sql = 'UPDATE `' . $this->prefix . 'entry` SET `lastSeen`=? WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)'; - $stm = $this->bd->prepare($sql); + $sql = 'UPDATE `_entry` SET `lastSeen`=? WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)'; + $stm = $this->pdo->prepare($sql); if ($mtime <= 0) { $mtime = time(); } @@ -977,7 +949,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->updateLastSeen($id_feed, $guids); } @@ -988,65 +960,70 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function countUnreadRead() { - $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE f.priority > 0' - . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE f.priority > 0 AND e.is_read=0'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $sql = 'SELECT COUNT(e.id) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id WHERE f.priority > 0' + . ' UNION SELECT COUNT(e.id) AS count FROM `_entry` e INNER JOIN `_feed` f ON e.id_feed=f.id WHERE f.priority > 0 AND e.is_read=0'; + $stm = $this->pdo->query($sql); + if ($stm === false) { + return false; + } $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); rsort($res); $all = empty($res[0]) ? 0 : $res[0]; $unread = empty($res[1]) ? 0 : $res[1]; return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread); } + public function count($minPriority = null) { - $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e'; + $sql = 'SELECT COUNT(e.id) AS count FROM `_entry` e'; if ($minPriority !== null) { - $sql .= ' INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id'; + $sql .= ' INNER JOIN `_feed` f ON e.id_feed=f.id'; $sql .= ' WHERE f.priority > ' . intval($minPriority); } - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); + if ($stm == false) { + return false; + } $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return isset($res[0]) ? $res[0] : 0; } + public function countNotRead($minPriority = null) { - $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e'; + $sql = 'SELECT COUNT(e.id) AS count FROM `_entry` e'; if ($minPriority !== null) { - $sql .= ' INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id'; + $sql .= ' INNER JOIN `_feed` f ON e.id_feed=f.id'; } $sql .= ' WHERE e.is_read=0'; if ($minPriority !== null) { $sql .= ' AND f.priority > ' . intval($minPriority); } - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; } public function countUnreadReadFavorites() { - $sql = <<<SQL - SELECT c - FROM ( - SELECT COUNT(e1.id) AS c - , 1 AS o - FROM `{$this->prefix}entry` AS e1 - JOIN `{$this->prefix}feed` AS f1 ON e1.id_feed = f1.id - WHERE e1.is_favorite = 1 - AND f1.priority >= :priority_normal - UNION - SELECT COUNT(e2.id) AS c - , 2 AS o - FROM `{$this->prefix}entry` AS e2 - JOIN `{$this->prefix}feed` AS f2 ON e2.id_feed = f2.id - WHERE e2.is_favorite = 1 - AND e2.is_read = 0 - AND f2.priority >= :priority_normal - ) u + $sql = <<<'SQL' +SELECT c FROM ( + SELECT COUNT(e1.id) AS c, 1 AS o + FROM `_entry` AS e1 + JOIN `_feed` AS f1 ON e1.id_feed = f1.id + WHERE e1.is_favorite = 1 + AND f1.priority >= :priority_normal1 + UNION + SELECT COUNT(e2.id) AS c, 2 AS o + FROM `_entry` AS e2 + JOIN `_feed` AS f2 ON e2.id_feed = f2.id + WHERE e2.is_favorite = 1 + AND e2.is_read = 0 + AND f2.priority >= :priority_normal2 + ) u ORDER BY o SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(array(':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL)); + $stm = $this->pdo->prepare($sql); + //Binding a value more than once is not standard and does not work with native prepared statements (e.g. MySQL) https://bugs.php.net/bug.php?id=40417 + $stm->bindValue(':priority_normal1', FreshRSS_Feed::PRIORITY_NORMAL, PDO::PARAM_INT); + $stm->bindValue(':priority_normal2', FreshRSS_Feed::PRIORITY_NORMAL, PDO::PARAM_INT); + $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); rsort($res); $all = empty($res[0]) ? 0 : $res[0]; diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php index e571e457f..9afea279f 100644 --- a/app/Models/EntryDAOPGSQL.php +++ b/app/Models/EntryDAOPGSQL.php @@ -2,6 +2,10 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { + public function hasNativeHex() { + return true; + } + public function sqlHexDecode($x) { return 'decode(' . $x . ", 'hex')"; } @@ -31,25 +35,27 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { public function commitNewEntries() { $sql = 'DO $$ DECLARE -maxrank bigint := (SELECT MAX(id) FROM `' . $this->prefix . 'entrytmp`); -rank bigint := (SELECT maxrank - COUNT(*) FROM `' . $this->prefix . 'entrytmp`); +maxrank bigint := (SELECT MAX(id) FROM `_entrytmp`); +rank bigint := (SELECT maxrank - COUNT(*) FROM `_entrytmp`); BEGIN - INSERT INTO `' . $this->prefix . 'entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) - (SELECT rank + row_number() OVER(ORDER BY date) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags - FROM `' . $this->prefix . 'entrytmp` AS etmp + INSERT INTO `_entry` + (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) + (SELECT rank + row_number() OVER(ORDER BY date) AS id, guid, title, author, content, + link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags + FROM `_entrytmp` AS etmp WHERE NOT EXISTS ( - SELECT 1 FROM `' . $this->prefix . 'entry` AS ereal + SELECT 1 FROM `_entry` AS ereal WHERE (etmp.id = ereal.id) OR (etmp.id_feed = ereal.id_feed AND etmp.guid = ereal.guid)) ORDER BY date); - DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= maxrank; + DELETE FROM `_entrytmp` WHERE id <= maxrank; END $$;'; - $hadTransaction = $this->bd->inTransaction(); + $hadTransaction = $this->pdo->inTransaction(); if (!$hadTransaction) { - $this->bd->beginTransaction(); + $this->pdo->beginTransaction(); } - $result = $this->bd->exec($sql) !== false; + $result = $this->pdo->exec($sql) !== false; if (!$hadTransaction) { - $this->bd->commit(); + $this->pdo->commit(); } return $result; } diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index f8cd14fe6..12e8f27e1 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -2,32 +2,32 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { + public function isCompressed() { + return false; + } + + public function hasNativeHex() { + return false; + } + public function sqlHexDecode($x) { return $x; } protected function autoUpdateDb($errorInfo) { - if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='tag'")) { + if ($tableInfo = $this->pdo->query("SELECT sql FROM sqlite_master where name='tag'")) { $showCreate = $tableInfo->fetchColumn(); if (stripos($showCreate, 'tag') === false) { $tagDAO = FreshRSS_Factory::createTagDao(); return $tagDAO->createTagTable(); //v1.12.0 } } - if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entrytmp'")) { + if ($tableInfo = $this->pdo->query("SELECT sql FROM sqlite_master where name='entrytmp'")) { $showCreate = $tableInfo->fetchColumn(); if (stripos($showCreate, 'entrytmp') === false) { return $this->createEntryTempTable(); //v1.7.0 } } - if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { - $showCreate = $tableInfo->fetchColumn(); - foreach (array('lastSeen', 'hash') as $column) { - if (stripos($showCreate, $column) === false) { - return $this->addColumn($column); - } - } - } return false; } @@ -36,27 +36,27 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { DROP TABLE IF EXISTS `tmp`; CREATE TEMP TABLE `tmp` AS SELECT id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags - FROM `' . $this->prefix . 'entrytmp` + FROM `_entrytmp` ORDER BY date; -INSERT OR IGNORE INTO `' . $this->prefix . 'entry` +INSERT OR IGNORE INTO `_entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `tmp` ORDER BY date; -DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`); +DELETE FROM `_entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`); DROP TABLE IF EXISTS `tmp`; '; - $hadTransaction = $this->bd->inTransaction(); + $hadTransaction = $this->pdo->inTransaction(); if (!$hadTransaction) { - $this->bd->beginTransaction(); + $this->pdo->beginTransaction(); } - $result = $this->bd->exec($sql) !== false; + $result = $this->pdo->exec($sql) !== false; if (!$result) { - Minz_Log::error('SQL error commitNewEntries: ' . json_encode($this->bd->errorInfo())); + Minz_Log::error('SQL error commitNewEntries: ' . json_encode($this->pdo->errorInfo())); } if (!$hadTransaction) { - $this->bd->commit(); + $this->pdo->commit(); } return $result; } @@ -66,10 +66,10 @@ DROP TABLE IF EXISTS `tmp`; } protected function updateCacheUnreads($catId = false, $feedId = false) { - $sql = 'UPDATE `' . $this->prefix . 'feed` ' + $sql = 'UPDATE `_feed` ' . 'SET `cache_nbUnreads`=(' - . 'SELECT COUNT(*) AS nbUnreads FROM `' . $this->prefix . 'entry` e ' - . 'WHERE e.id_feed=`' . $this->prefix . 'feed`.id AND e.is_read=0)'; + . 'SELECT COUNT(*) AS nbUnreads FROM `_entry` e ' + . 'WHERE e.id_feed=`_feed`.id AND e.is_read=0)'; $hasWhere = false; $values = array(); if ($feedId !== false) { @@ -84,11 +84,11 @@ DROP TABLE IF EXISTS `tmp`; $sql .= ' category=?'; $values[] = $catId; } - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return true; } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error updateCacheUnreads: ' . $info[2]); return false; } @@ -118,30 +118,30 @@ DROP TABLE IF EXISTS `tmp`; return $affected; } } else { - $this->bd->beginTransaction(); - $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read=? WHERE id=? AND is_read=?'; + $this->pdo->beginTransaction(); + $sql = 'UPDATE `_entry` SET is_read=? WHERE id=? AND is_read=?'; $values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if (!($stm && $stm->execute($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markRead 1: ' . $info[2]); - $this->bd->rollBack(); + $this->pdo->rollBack(); return false; } $affected = $stm->rowCount(); if ($affected > 0) { - $sql = 'UPDATE `' . $this->prefix . 'feed` SET `cache_nbUnreads`=`cache_nbUnreads`' . ($is_read ? '-' : '+') . '1 ' - . 'WHERE id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)'; + $sql = 'UPDATE `_feed` SET `cache_nbUnreads`=`cache_nbUnreads`' . ($is_read ? '-' : '+') . '1 ' + . 'WHERE id=(SELECT e.id_feed FROM `_entry` e WHERE e.id=?)'; $values = array($ids); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if (!($stm && $stm->execute($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markRead 2: ' . $info[2]); - $this->bd->rollBack(); + $this->pdo->rollBack(); return false; } } - $this->bd->commit(); + $this->pdo->commit(); return $affected; } } @@ -174,19 +174,19 @@ DROP TABLE IF EXISTS `tmp`; Minz_Log::debug('Calling markReadEntries(0) is deprecated!'); } - $sql = 'UPDATE `' . $this->prefix . 'entry` SET is_read = ? WHERE is_read <> ? AND id <= ?'; + $sql = 'UPDATE `_entry` SET is_read = ? WHERE is_read <> ? AND id <= ?'; if ($onlyFavorites) { $sql .= ' AND is_favorite=1'; } elseif ($priorityMin >= 0) { - $sql .= ' AND id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.priority > ' . intval($priorityMin) . ')'; + $sql .= ' AND id_feed IN (SELECT f.id FROM `_feed` f WHERE f.priority > ' . intval($priorityMin) . ')'; } $values = array($is_read ? 1 : 0, $is_read ? 1 : 0, $idMax); list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadEntries: ' . $info[2]); return false; } @@ -215,17 +215,17 @@ DROP TABLE IF EXISTS `tmp`; Minz_Log::debug('Calling markReadCat(0) is deprecated!'); } - $sql = 'UPDATE `' . $this->prefix . 'entry` ' + $sql = 'UPDATE `_entry` ' . 'SET is_read = ? ' . 'WHERE is_read <> ? AND id <= ? AND ' - . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)'; + . 'id_feed IN (SELECT f.id FROM `_feed` f WHERE f.category=?)'; $values = array($is_read ? 1 : 0, $is_read ? 1 : 0, $idMax, $id); list($searchValues, $search) = $this->sqlListEntriesWhere('', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadCat: ' . $info[2]); return false; } @@ -249,10 +249,10 @@ DROP TABLE IF EXISTS `tmp`; Minz_Log::debug('Calling markReadTag(0) is deprecated!'); } - $sql = 'UPDATE `' . $this->prefix . 'entry` e ' + $sql = 'UPDATE `_entry` e ' . 'SET e.is_read = ? ' . 'WHERE e.is_read <> ? AND e.id <= ? AND ' - . 'e.id IN (SELECT et.id_entry FROM `' . $this->prefix . 'entrytag` et ' + . 'e.id IN (SELECT et.id_entry FROM `_entrytag` et ' . ($id == '' ? '' : 'WHERE et.id = ?') . ')'; $values = array($is_read ? 1 : 0, $is_read ? 1 : 0, $idMax); @@ -262,9 +262,9 @@ DROP TABLE IF EXISTS `tmp`; list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filters, $state); - $stm = $this->bd->prepare($sql . $search); + $stm = $this->pdo->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error markReadTag: ' . $info[2]); return false; } diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 1accb491c..69885c205 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -2,8 +2,18 @@ class FreshRSS_Factory { + public static function createUserDao($username = null) { + return new FreshRSS_UserDAO($username); + } + 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 89989236c..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; } @@ -153,18 +149,17 @@ class FreshRSS_Feed extends Minz_Model { return $this->nbNotRead; } public function faviconPrepare() { - global $favicons_dir; require_once(LIB_PATH . '/favicons.php'); $url = $this->website; if ($url == '') { $url = $this->url; } - $txt = $favicons_dir . $this->hash() . '.txt'; + $txt = FAVICONS_DIR . $this->hash() . '.txt'; if (!file_exists($txt)) { file_put_contents($txt, $url); } if (FreshRSS_Context::$isCli) { - $ico = $favicons_dir . $this->hash() . '.ico'; + $ico = FAVICONS_DIR . $this->hash() . '.ico'; $ico_mtime = @filemtime($ico); $txt_mtime = @filemtime($txt); if ($txt_mtime != false && @@ -231,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); @@ -470,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'; } @@ -701,7 +712,7 @@ class FreshRSS_Feed extends Minz_Model { file_put_contents($hubFilename, json_encode($hubJson)); } $ch = curl_init(); - curl_setopt_array($ch, array( + curl_setopt_array($ch, [ CURLOPT_URL => $hubJson['hub'], CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => http_build_query(array( @@ -712,13 +723,9 @@ class FreshRSS_Feed extends Minz_Model { )), CURLOPT_USERAGENT => FRESHRSS_USERAGENT, CURLOPT_MAXREDIRS => 10, - )); - if (version_compare(PHP_VERSION, '5.6.0') >= 0 || ini_get('open_basedir') == '') { - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); //Keep option separated for open_basedir PHP bug 65646 - } - if (defined('CURLOPT_ENCODING')) { - curl_setopt($ch, CURLOPT_ENCODING, ''); //Enable all encodings - } + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_ENCODING => '', //Enable all encodings + ]); $response = curl_exec($ch); $info = curl_getinfo($ch); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index c9c9f6301..fa0001df7 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -3,14 +3,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { protected function addColumn($name) { - Minz_Log::warning('FreshRSS_FeedDAO::addColumn: ' . $name); + Minz_Log::warning(__method__ . ': ' . $name); try { if ($name === 'attributes') { //v1.11.0 - $stm = $this->bd->prepare('ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN attributes TEXT'); - return $stm && $stm->execute(); + return $this->pdo->exec('ALTER TABLE `_feed` ADD COLUMN attributes TEXT') !== false; } } catch (Exception $e) { - Minz_Log::error('FreshRSS_FeedDAO::addColumn error: ' . $e->getMessage()); + Minz_Log::error(__method__ . ' error: ' . $e->getMessage()); } return false; } @@ -18,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); } @@ -30,7 +29,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function addFeed($valuesTmp) { $sql = ' - INSERT INTO `' . $this->prefix . 'feed` + INSERT INTO `_feed` ( url, category, @@ -39,18 +38,24 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { description, `lastUpdate`, priority, + `pathEntries`, `httpAuth`, error, - keep_history, ttl, attributes ) VALUES - (?, ?, ?, ?, ?, ?, 10, ?, 0, ?, ?, ?)'; - $stm = $this->bd->prepare($sql); + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + $stm = $this->pdo->prepare($sql); $valuesTmp['url'] = safe_ascii($valuesTmp['url']); $valuesTmp['website'] = safe_ascii($valuesTmp['website']); + if (!isset($valuesTmp['pathEntries'])) { + $valuesTmp['pathEntries'] = ''; + } + if (!isset($valuesTmp['attributes'])) { + $valuesTmp['attributes'] = []; + } $values = array( substr($valuesTmp['url'], 0, 511), @@ -59,16 +64,18 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { substr($valuesTmp['website'], 0, 255), mb_strcut($valuesTmp['description'], 0, 1023, 'UTF-8'), $valuesTmp['lastUpdate'], + isset($valuesTmp['priority']) ? intval($valuesTmp['priority']) : FreshRSS_Feed::PRIORITY_MAIN_STREAM, + mb_strcut($valuesTmp['pathEntries'], 0, 511, 'UTF-8'), base64_encode($valuesTmp['httpAuth']), - FreshRSS_Feed::KEEP_HISTORY_DEFAULT, + isset($valuesTmp['error']) ? intval($valuesTmp['error']) : 0, 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)) { - return $this->bd->lastInsertId('"' . $this->prefix . 'feed_id_seq"'); + return $this->pdo->lastInsertId('`_feed_id_seq`'); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->addFeed($valuesTmp); } @@ -129,13 +136,13 @@ 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); - $sql = 'UPDATE `' . $this->prefix . 'feed` SET ' . $set . ' WHERE id=?'; - $stm = $this->bd->prepare($sql); + $sql = 'UPDATE `_feed` SET ' . $set . ' WHERE id=?'; + $stm = $this->pdo->prepare($sql); foreach ($valuesTmp as $v) { $values[] = $v; @@ -145,7 +152,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->updateFeed($id, $valuesTmp); } @@ -166,7 +173,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function updateLastUpdate($id, $inError = false, $mtime = 0) { //See also updateCachedValue() - $sql = 'UPDATE `' . $this->prefix . 'feed` ' + $sql = 'UPDATE `_feed` ' . 'SET `lastUpdate`=?, error=? ' . 'WHERE id=?'; $values = array( @@ -174,12 +181,12 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $inError ? 1 : 0, $id, ); - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error updateLastUpdate: ' . $info[2]); return false; } @@ -192,8 +199,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $newCat = $catDAO->getDefault(); } - $sql = 'UPDATE `' . $this->prefix . 'feed` SET category=? WHERE category=?'; - $stm = $this->bd->prepare($sql); + $sql = 'UPDATE `_feed` SET category=? WHERE category=?'; + $stm = $this->pdo->prepare($sql); $values = array( $newCat->id(), @@ -203,44 +210,54 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error changeCategory: ' . $info[2]); return false; } } public function deleteFeed($id) { - $sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE id=?'; - $stm = $this->bd->prepare($sql); + $sql = 'DELETE FROM `_feed` WHERE id=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error deleteFeed: ' . $info[2]); return false; } } public function deleteFeedByCategory($id) { - $sql = 'DELETE FROM `' . $this->prefix . 'feed` WHERE category=?'; - $stm = $this->bd->prepare($sql); + $sql = 'DELETE FROM `_feed` WHERE category=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error deleteFeedByCategory: ' . $info[2]); return false; } } + public function selectAll() { + $sql = 'SELECT id, url, category, name, website, description, `lastUpdate`, priority, ' + . '`pathEntries`, `httpAuth`, error, ttl, attributes ' + . 'FROM `_feed`'; + $stm = $this->pdo->query($sql); + while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + yield $row; + } + } + public function searchById($id) { - $sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE id=?'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT * FROM `_feed` WHERE id=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); @@ -255,8 +272,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } public function searchByUrl($url) { - $sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE url=?'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT * FROM `_feed` WHERE url=?'; + $stm = $this->pdo->prepare($sql); $values = array($url); @@ -272,25 +289,21 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function listFeedsIds() { - $sql = 'SELECT id FROM `' . $this->prefix . 'feed`'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $sql = 'SELECT id FROM `_feed`'; + $stm = $this->pdo->query($sql); return $stm->fetchAll(PDO::FETCH_COLUMN, 0); } public function listFeeds() { - $sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY name'; - $stm = $this->bd->prepare($sql); - $stm->execute(); - + $sql = 'SELECT * FROM `_feed` ORDER BY name'; + $stm = $this->pdo->query($sql); return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); } public function arrayFeedCategoryNames() { //For API - $sql = 'SELECT f.id, f.name, c.name as c_name FROM `' . $this->prefix . 'feed` f ' - . 'INNER JOIN `' . $this->prefix . 'category` c ON c.id = f.category'; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $sql = 'SELECT f.id, f.name, c.name as c_name FROM `_feed` f ' + . 'INNER JOIN `_category` c ON c.id = f.category'; + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); $feedCategoryNames = array(); foreach ($res as $line) { @@ -307,17 +320,18 @@ 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 ' - . 'FROM `' . $this->prefix . 'feed` ' + $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) . '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ') + . ' AND `lastUpdate` < (' . (time() + 60) + . '-(CASE WHEN ttl=' . FreshRSS_Feed::TTL_DEFAULT . ' THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) ') . 'ORDER BY `lastUpdate` ' . ($limit < 1 ? '' : 'LIMIT ' . intval($limit)); - $stm = $this->bd->prepare($sql); - if ($stm && $stm->execute()) { + $stm = $this->pdo->query($sql); + if ($stm !== false) { return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC)); } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->listFeedsOrderUpdate($defaultCacheDuration); } @@ -327,8 +341,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function listByCategory($cat) { - $sql = 'SELECT * FROM `' . $this->prefix . 'feed` WHERE category=? ORDER BY name'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT * FROM `_feed` WHERE category=? ORDER BY name'; + $stm = $this->pdo->prepare($sql); $values = array($cat); @@ -338,8 +352,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function countEntries($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=?'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); @@ -348,8 +362,8 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function countNotRead($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND is_read=0'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT COUNT(*) AS count FROM `_entry` WHERE id_feed=? AND is_read=0'; + $stm = $this->pdo->prepare($sql); $values = array($id); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); @@ -357,62 +371,51 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $res[0]['count']; } - public function updateCachedValue($id) { //For multiple feeds, call updateCachedValues() - $sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE - . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' - . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0) ' - . 'WHERE id=?'; - $values = array($id); - $stm = $this->bd->prepare($sql); - - if ($stm && $stm->execute($values)) { - return $stm->rowCount(); - } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error updateCachedValue: ' . $info[2]); - return false; + public function updateCachedValues($id = null) { + //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE + $sql = 'UPDATE `_feed` ' + . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `_entry` e1 WHERE e1.id_feed=`_feed`.id),' + . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `_entry` e2 WHERE e2.id_feed=`_feed`.id AND e2.is_read=0)' + . ($id != null ? ' WHERE id=:id' : ''); + $stm = $this->pdo->prepare($sql); + if ($id != null) { + $stm->bindParam(':id', $id, PDO::PARAM_INT); } - } - public function updateCachedValues() { //For one single feed, call updateCachedValue($id) - $sql = 'UPDATE `' . $this->prefix . 'feed` ' - . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' - . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)'; - $stm = $this->bd->prepare($sql); if ($stm && $stm->execute()) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error updateCachedValues: ' . $info[2]); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); + Minz_Log::error('SQL error updateCachedValue: ' . $info[2]); return false; } } public function truncate($id) { - $sql = 'DELETE FROM `' . $this->prefix . 'entry` WHERE id_feed=?'; - $stm = $this->bd->prepare($sql); - $values = array($id); - $this->bd->beginTransaction(); - if (!($stm && $stm->execute($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $sql = 'DELETE FROM `_entry` WHERE id_feed=:id'; + $stm = $this->pdo->prepare($sql); + $stm->bindParam(':id', $id, PDO::PARAM_INT); + $this->pdo->beginTransaction(); + if (!($stm && $stm->execute())) { + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error truncate: ' . $info[2]); - $this->bd->rollBack(); + $this->pdo->rollBack(); return false; } $affected = $stm->rowCount(); - $sql = 'UPDATE `' . $this->prefix . 'feed` ' - . 'SET `cache_nbEntries`=0, `cache_nbUnreads`=0 WHERE id=?'; - $values = array($id); - $stm = $this->bd->prepare($sql); - if (!($stm && $stm->execute($values))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $sql = 'UPDATE `_feed` ' + . '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())) { + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error truncate: ' . $info[2]); - $this->bd->rollBack(); + $this->pdo->rollBack(); return false; } - $this->bd->commit(); + $this->pdo->commit(); return $affected; } @@ -446,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); @@ -461,20 +463,16 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function updateTTL() { - $sql = <<<SQL -UPDATE `{$this->prefix}feed` - SET ttl = :new_value - WHERE ttl = :old_value -SQL; - $stm = $this->bd->prepare($sql); + $sql = 'UPDATE `_feed` SET ttl=:new_value WHERE ttl=:old_value'; + $stm = $this->pdo->prepare($sql); if (!($stm && $stm->execute(array(':new_value' => FreshRSS_Feed::TTL_DEFAULT, ':old_value' => -2)))) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL warning updateTTL 1: ' . $info[2] . ' ' . $sql); - $sql2 = 'ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN ttl INT NOT NULL DEFAULT ' . FreshRSS_Feed::TTL_DEFAULT; //v0.7.3 - $stm = $this->bd->prepare($sql2); - if (!($stm && $stm->execute())) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $sql2 = 'ALTER TABLE `_feed` ADD COLUMN ttl INT NOT NULL DEFAULT ' . FreshRSS_Feed::TTL_DEFAULT; //v0.7.3 + $stm = $this->pdo->query($sql2); + if ($stm === false) { + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error updateTTL 2: ' . $info[2] . ' ' . $sql2); } } else { diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php index 3c203b378..0f685867a 100644 --- a/app/Models/FeedDAOSQLite.php +++ b/app/Models/FeedDAOSQLite.php @@ -3,9 +3,9 @@ class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO { protected function autoUpdateDb($errorInfo) { - if ($tableInfo = $this->bd->query("PRAGMA table_info('feed')")) { + 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/StatsDAO.php b/app/Models/StatsDAO.php index 67ada73f7..cbfa79c61 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -45,13 +45,11 @@ SELECT COUNT(1) AS total, COUNT(1) - SUM(e.is_read) AS count_unreads, SUM(e.is_read) AS count_reads, SUM(e.is_favorite) AS count_favorites -FROM `{$this->prefix}entry` AS e -, `{$this->prefix}feed` AS f +FROM `_entry` AS e, `_feed` AS f WHERE e.id_feed = f.id {$filter} SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); return $res[0]; @@ -73,13 +71,12 @@ SQL; $sql = <<<SQL SELECT {$sqlDay} AS day, COUNT(*) as count -FROM `{$this->prefix}entry` +FROM `_entry` WHERE date >= {$oldest} AND date < {$midnight} GROUP BY day ORDER BY day ASC SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); foreach ($res as $value) { @@ -143,14 +140,13 @@ SQL; $sql = <<<SQL SELECT DATE_FORMAT(FROM_UNIXTIME(e.date), '{$period}') AS period , COUNT(1) AS count -FROM `{$this->prefix}entry` AS e +FROM `_entry` AS e {$restrict} GROUP BY period ORDER BY period ASC SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_NAMED); $repartition = array(); @@ -207,11 +203,10 @@ SQL; SELECT COUNT(1) AS count , MIN(date) AS date_min , MAX(date) AS date_max -FROM `{$this->prefix}entry` AS e +FROM `_entry` AS e {$restrict} SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetch(PDO::FETCH_NAMED); $date_min = new \DateTime(); $date_min->setTimestamp($res['date_min']); @@ -251,14 +246,12 @@ SQL; $sql = <<<SQL SELECT c.name AS label , COUNT(f.id) AS data -FROM `{$this->prefix}category` AS c, -`{$this->prefix}feed` AS f +FROM `_category` AS c, `_feed` AS f WHERE c.id = f.category GROUP BY label ORDER BY data DESC SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); return $res; @@ -274,16 +267,13 @@ SQL; $sql = <<<SQL SELECT c.name AS label , COUNT(e.id) AS data -FROM `{$this->prefix}category` AS c, -`{$this->prefix}feed` AS f, -`{$this->prefix}entry` AS e +FROM `_category` AS c, `_feed` AS f, `_entry` AS e WHERE c.id = f.category AND f.id = e.id_feed GROUP BY label ORDER BY data DESC SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_ASSOC); return $res; @@ -300,17 +290,14 @@ SELECT f.id AS id , MAX(f.name) AS name , MAX(c.name) AS category , COUNT(e.id) AS count -FROM `{$this->prefix}category` AS c, -`{$this->prefix}feed` AS f, -`{$this->prefix}entry` AS e +FROM `_category` AS c, `_feed` AS f, `_entry` AS e WHERE c.id = f.category AND f.id = e.id_feed GROUP BY f.id ORDER BY count DESC LIMIT 10 SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); return $stm->fetchAll(PDO::FETCH_ASSOC); } @@ -325,14 +312,12 @@ SELECT MAX(f.id) as id , MAX(f.name) AS name , MAX(date) AS last_date , COUNT(*) AS nb_articles -FROM `{$this->prefix}feed` AS f, -`{$this->prefix}entry` AS e +FROM `_feed` AS f, `_entry` AS e WHERE f.id = e.id_feed GROUP BY f.id ORDER BY name SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); return $stm->fetchAll(PDO::FETCH_ASSOC); } diff --git a/app/Models/StatsDAOPGSQL.php b/app/Models/StatsDAOPGSQL.php index 1effbb64b..4a66068cb 100644 --- a/app/Models/StatsDAOPGSQL.php +++ b/app/Models/StatsDAOPGSQL.php @@ -47,14 +47,13 @@ class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO { $sql = <<<SQL SELECT extract( {$period} from to_timestamp(e.date)) AS period , COUNT(1) AS count -FROM "{$this->prefix}entry" AS e +FROM `_entry` AS e {$restrict} GROUP BY period ORDER BY period ASC SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_NAMED); foreach ($res as $value) { diff --git a/app/Models/StatsDAOSQLite.php b/app/Models/StatsDAOSQLite.php index 6cfc20463..f96f8f479 100644 --- a/app/Models/StatsDAOSQLite.php +++ b/app/Models/StatsDAOSQLite.php @@ -15,14 +15,13 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO { $sql = <<<SQL SELECT strftime('{$period}', e.date, 'unixepoch') AS period , COUNT(1) AS count -FROM `{$this->prefix}entry` AS e +FROM `_entry` AS e {$restrict} GROUP BY period ORDER BY period ASC SQL; - $stm = $this->bd->prepare($sql); - $stm->execute(); + $stm = $this->pdo->query($sql); $res = $stm->fetchAll(PDO::FETCH_NAMED); $repartition = array(); 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 297d24c96..5882eee76 100644 --- a/app/Models/TagDAO.php +++ b/app/Models/TagDAO.php @@ -8,37 +8,24 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function createTagTable() { $ok = false; - $hadTransaction = $this->bd->inTransaction(); + $hadTransaction = $this->pdo->inTransaction(); if ($hadTransaction) { - $this->bd->commit(); + $this->pdo->commit(); } try { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.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...'); - if (defined('SQL_CREATE_TABLE_TAGS')) { - $sql = sprintf(SQL_CREATE_TABLE_TAGS, $this->prefix); - $stm = $this->bd->prepare($sql); - $ok = $stm && $stm->execute(); - } else { - global $SQL_CREATE_TABLE_TAGS; - $ok = !empty($SQL_CREATE_TABLE_TAGS); - foreach ($SQL_CREATE_TABLE_TAGS as $instruction) { - $sql = sprintf($instruction, $this->prefix); - $stm = $this->bd->prepare($sql); - $ok &= $stm && $stm->execute(); - } - } + $ok = $this->pdo->exec($SQL_CREATE_TABLE_TAGS) !== false; } catch (Exception $e) { Minz_Log::error('FreshRSS_EntryDAO::createTagTable error: ' . $e->getMessage()); } if ($hadTransaction) { - $this->bd->beginTransaction(); + $this->pdo->beginTransaction(); } return $ok; } @@ -55,22 +42,25 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function addTag($valuesTmp) { - $sql = 'INSERT INTO `' . $this->prefix . 'tag`(name, attributes) ' - . 'SELECT * FROM (SELECT TRIM(?), TRIM(?)) t2 ' //TRIM() to provide a type hint as text for PostgreSQL - . 'WHERE NOT EXISTS (SELECT 1 FROM `' . $this->prefix . 'category` WHERE name = TRIM(?))'; //No category of the same name - $stm = $this->bd->prepare($sql); + $sql = 'INSERT INTO `_tag`(name, attributes) ' + . 'SELECT * FROM (SELECT TRIM(?) as name, TRIM(?) as attributes) t2 ' //TRIM() gives a text type hint to PostgreSQL + . 'WHERE NOT EXISTS (SELECT 1 FROM `_category` WHERE name = TRIM(?))'; //No category of the same name + $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'], ); if ($stm && $stm->execute($values)) { - return $this->bd->lastInsertId('"' . $this->prefix . 'tag_id_seq"'); + return $this->pdo->lastInsertId('`_tag_id_seq`'); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error addTag: ' . $info[2]); return false; } @@ -89,14 +79,17 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function updateTag($id, $valuesTmp) { - $sql = 'UPDATE `' . $this->prefix . 'tag` SET name=?, attributes=? WHERE id=? ' - . 'AND NOT EXISTS (SELECT 1 FROM `' . $this->prefix . 'category` WHERE name = ?)'; //No category of the same name - $stm = $this->bd->prepare($sql); + $sql = 'UPDATE `_tag` SET name=?, attributes=? WHERE id=? ' + . 'AND NOT EXISTS (SELECT 1 FROM `_category` WHERE name = ?)'; //No category of the same name + $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'], ); @@ -104,7 +97,7 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error updateTag: ' . $info[2]); return false; } @@ -125,23 +118,39 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($id <= 0) { return false; } - $sql = 'DELETE FROM `' . $this->prefix . 'tag` WHERE id=?'; - $stm = $this->bd->prepare($sql); + $sql = 'DELETE FROM `_tag` WHERE id=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); if ($stm && $stm->execute($values)) { return $stm->rowCount(); } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error deleteTag: ' . $info[2]); return false; } } + public function selectAll() { + $sql = 'SELECT id, name, attributes FROM `_tag`'; + $stm = $this->pdo->query($sql); + while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + yield $row; + } + } + + public function selectEntryTag() { + $sql = 'SELECT id_tag, id_entry FROM `_entrytag`'; + $stm = $this->pdo->query($sql); + while ($row = $stm->fetch(PDO::FETCH_ASSOC)) { + yield $row; + } + } + public function searchById($id) { - $sql = 'SELECT * FROM `' . $this->prefix . 'tag` WHERE id=?'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT * FROM `_tag` WHERE id=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); @@ -150,8 +159,8 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function searchByName($name) { - $sql = 'SELECT * FROM `' . $this->prefix . 'tag` WHERE name=?'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT * FROM `_tag` WHERE name=?'; + $stm = $this->pdo->prepare($sql); $values = array($name); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); @@ -162,20 +171,20 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function listTags($precounts = false) { if ($precounts) { $sql = 'SELECT t.id, t.name, count(e.id) AS unreads ' - . 'FROM `' . $this->prefix . 'tag` t ' - . 'LEFT OUTER JOIN `' . $this->prefix . 'entrytag` et ON et.id_tag = t.id ' - . 'LEFT OUTER JOIN `' . $this->prefix . 'entry` e ON et.id_entry = e.id AND e.is_read = 0 ' + . 'FROM `_tag` t ' + . 'LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id ' + . 'LEFT OUTER JOIN `_entry` e ON et.id_entry = e.id AND e.is_read = 0 ' . 'GROUP BY t.id ' . 'ORDER BY t.name'; } else { - $sql = 'SELECT * FROM `' . $this->prefix . 'tag` ORDER BY name'; + $sql = 'SELECT * FROM `_tag` ORDER BY name'; } - $stm = $this->bd->prepare($sql); - if ($stm && $stm->execute()) { + $stm = $this->pdo->query($sql); + if ($stm !== false) { return self::daoToTag($stm->fetchAll(PDO::FETCH_ASSOC)); } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->listTags($precounts); } @@ -185,13 +194,13 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function count() { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'tag`'; - $stm = $this->bd->prepare($sql); - if ($stm && $stm->execute()) { + $sql = 'SELECT COUNT(*) AS count FROM `_tag`'; + $stm = $this->pdo->query($sql); + if ($stm !== false) { $res = $stm->fetchAll(PDO::FETCH_ASSOC); return $res[0]['count']; } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->count(); } @@ -201,8 +210,8 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function countEntries($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entrytag` WHERE id_tag=?'; - $stm = $this->bd->prepare($sql); + $sql = 'SELECT COUNT(*) AS count FROM `_entrytag` WHERE id_tag=?'; + $stm = $this->pdo->prepare($sql); $values = array($id); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); @@ -210,10 +219,10 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function countNotRead($id) { - $sql = 'SELECT COUNT(*) AS count FROM `' . $this->prefix . 'entrytag` et ' - . 'INNER JOIN `' . $this->prefix . 'entry` e ON et.id_entry=e.id ' + $sql = 'SELECT COUNT(*) AS count FROM `_entrytag` et ' + . 'INNER JOIN `_entry` e ON et.id_entry=e.id ' . 'WHERE et.id_tag=? AND e.is_read=0'; - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $values = array($id); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); @@ -222,17 +231,17 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function tagEntry($id_tag, $id_entry, $checked = true) { if ($checked) { - $sql = 'INSERT ' . $this->sqlIgnore() . ' INTO `' . $this->prefix . 'entrytag`(id_tag, id_entry) VALUES(?, ?)'; + $sql = 'INSERT ' . $this->sqlIgnore() . ' INTO `_entrytag`(id_tag, id_entry) VALUES(?, ?)'; } else { - $sql = 'DELETE FROM `' . $this->prefix . 'entrytag` WHERE id_tag=? AND id_entry=?'; + $sql = 'DELETE FROM `_entrytag` WHERE id_tag=? AND id_entry=?'; } - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $values = array($id_tag, $id_entry); if ($stm && $stm->execute($values)) { return true; } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); Minz_Log::error('SQL error tagEntry: ' . $info[2]); return false; } @@ -240,11 +249,11 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function getTagsForEntry($id_entry) { $sql = 'SELECT t.id, t.name, et.id_entry IS NOT NULL as checked ' - . 'FROM `' . $this->prefix . 'tag` t ' - . 'LEFT OUTER JOIN `' . $this->prefix . 'entrytag` et ON et.id_tag = t.id AND et.id_entry=? ' + . 'FROM `_tag` t ' + . 'LEFT OUTER JOIN `_entrytag` et ON et.id_tag = t.id AND et.id_entry=? ' . 'ORDER BY t.name'; - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); $values = array($id_entry); if ($stm && $stm->execute($values)) { @@ -255,7 +264,7 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } return $lines; } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->getTagsForEntry($id_entry); } @@ -266,8 +275,8 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function getTagsForEntries($entries) { $sql = 'SELECT et.id_entry, et.id_tag, t.name ' - . 'FROM `' . $this->prefix . 'tag` t ' - . 'INNER JOIN `' . $this->prefix . 'entrytag` et ON et.id_tag = t.id'; + . 'FROM `_tag` t ' + . 'INNER JOIN `_entrytag` et ON et.id_tag = t.id'; $values = array(); if (is_array($entries) && count($entries) > 0) { @@ -286,12 +295,12 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } } - $stm = $this->bd->prepare($sql); + $stm = $this->pdo->prepare($sql); if ($stm && $stm->execute($values)) { return $stm->fetchAll(PDO::FETCH_ASSOC); } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->getTagsForEntries($entries); } diff --git a/app/Models/TagDAOSQLite.php b/app/Models/TagDAOSQLite.php index b1deb6c65..ca0fce7ca 100644 --- a/app/Models/TagDAOSQLite.php +++ b/app/Models/TagDAOSQLite.php @@ -7,7 +7,7 @@ class FreshRSS_TagDAOSQLite extends FreshRSS_TagDAO { } protected function autoUpdateDb($errorInfo) { - if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='tag'")) { + if ($tableInfo = $this->pdo->query("SELECT sql FROM sqlite_master where name='tag'")) { $showCreate = $tableInfo->fetchColumn(); if (stripos($showCreate, 'tag') === false) { return $this->createTagTable(); //v1.12.0 diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index e9d3a7329..4e824cf01 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -1,83 +1,52 @@ <?php class FreshRSS_UserDAO extends Minz_ModelPdo { - public function createUser($username, $new_user_language, $insertDefaultFeeds = true) { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); - - $userPDO = new Minz_ModelPdo($username); - - $currentLanguage = Minz_Translate::language(); + public function createUser($insertDefaultFeeds = false) { + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); try { - Minz_Translate::reset($new_user_language); - $ok = false; - $bd_prefix_user = $db['prefix'] . $username . '_'; - if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL - $sql = sprintf(SQL_CREATE_TABLES . SQL_CREATE_TABLE_ENTRYTMP . SQL_CREATE_TABLE_TAGS, $bd_prefix_user, _t('gen.short.default_category')); - $stm = $userPDO->bd->prepare($sql); - $ok = $stm && $stm->execute(); - } else { //E.g. SQLite - global $SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP, $SQL_CREATE_TABLE_TAGS; - if (is_array($SQL_CREATE_TABLES)) { - $instructions = array_merge($SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP, $SQL_CREATE_TABLE_TAGS); - $ok = !empty($instructions); - foreach ($instructions as $instruction) { - $sql = sprintf($instruction, $bd_prefix_user, _t('gen.short.default_category')); - $stm = $userPDO->bd->prepare($sql); - $ok &= ($stm && $stm->execute()); - } - } - } + $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) { - if (defined('SQL_INSERT_FEEDS')) { //E.g. MySQL - $sql = sprintf(SQL_INSERT_FEEDS, $bd_prefix_user); - $stm = $userPDO->bd->prepare($sql); - $ok &= $stm && $stm->execute(); - } else { //E.g. SQLite - global $SQL_INSERT_FEEDS; - if (is_array($SQL_INSERT_FEEDS)) { - foreach ($SQL_INSERT_FEEDS as $instruction) { - $sql = sprintf($instruction, $bd_prefix_user); - $stm = $userPDO->bd->prepare($sql); - $ok &= ($stm && $stm->execute()); - } - } + $default_feeds = FreshRSS_Context::$system_conf->default_feeds; + $stm = $this->pdo->prepare($SQL_INSERT_FEED); + foreach ($default_feeds as $feed) { + $parameters = [ + ':url' => $feed['url'], + ':name' => $feed['name'], + ':website' => $feed['website'], + ':description' => $feed['description'], + ]; + $ok &= ($stm && $stm->execute($parameters)); } } } catch (Exception $e) { - Minz_Log::error('Error while creating user: ' . $e->getMessage()); + Minz_Log::error('Error while creating database for user: ' . $e->getMessage()); } - Minz_Translate::reset($currentLanguage); - if ($ok) { return true; } else { - $info = empty($stm) ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error: ' . $info[2]); + $info = empty($stm) ? $this->pdo->errorInfo() : $stm->errorInfo(); + Minz_Log::error(__METHOD__ . ' error: ' . $info[2]); return false; } } - public function deleteUser($username) { - $db = FreshRSS_Context::$system_conf->db; - require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); + public function deleteUser() { + if (defined('STDERR')) { + fwrite(STDERR, 'Deleting SQL data for user “' . $this->current_user . "”…\n"); + } + + require(APP_PATH . '/SQL/install.sql.' . $this->pdo->dbType() . '.php'); + $ok = $this->pdo->exec($SQL_DROP_TABLES) !== false; - if ($db['type'] === 'sqlite') { - return unlink(USERS_PATH . '/' . $username . '/db.sqlite'); + if ($ok) { + return true; } else { - $userPDO = new Minz_ModelPdo($username); - - $sql = sprintf(SQL_DROP_TABLES, $db['prefix'] . $username . '_'); - $stm = $userPDO->bd->prepare($sql); - if ($stm && $stm->execute()) { - return true; - } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error : ' . $info[2]); - return false; - } + $info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo(); + Minz_Log::error(__METHOD__ . ' error: ' . $info[2]); + return false; } } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index b3353ac95..1eabfae8b 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -1,20 +1,23 @@ <?php -define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS `%1$s` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); +$SQL_CREATE_DB = <<<'SQL' +CREATE DATABASE IF NOT EXISTS `%1$s` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +SQL; -define('SQL_CREATE_TABLES', ' -CREATE TABLE IF NOT EXISTS `%1$scategory` ( +$SQL_CREATE_TABLES = <<<'SQL' +CREATE TABLE IF NOT EXISTS `_category` ( `id` SMALLINT NOT NULL AUTO_INCREMENT, -- v0.7 - `name` VARCHAR(' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . ') NOT NULL, -- Max index length for Unicode is 191 characters (767 bytes) + `name` VARCHAR(191) NOT NULL, -- Max index length for Unicode is 191 characters (767 bytes) FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE + `attributes` TEXT, -- v1.15.0 PRIMARY KEY (`id`), UNIQUE KEY (`name`) -- v0.7 ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; -CREATE TABLE IF NOT EXISTS `%1$sfeed` ( +CREATE TABLE IF NOT EXISTS `_feed` ( `id` SMALLINT NOT NULL AUTO_INCREMENT, -- v0.7 `url` VARCHAR(511) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, `category` SMALLINT DEFAULT 0, -- v0.7 - `name` VARCHAR(' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . ') NOT NULL, + `name` VARCHAR(191) NOT NULL, `website` VARCHAR(255) CHARACTER SET latin1 COLLATE latin1_bin, `description` TEXT, `lastUpdate` INT(11) DEFAULT 0, -- Until year 2038 @@ -22,26 +25,24 @@ CREATE TABLE IF NOT EXISTS `%1$sfeed` ( `pathEntries` VARCHAR(511) DEFAULT NULL, `httpAuth` VARCHAR(511) DEFAULT NULL, `error` BOOLEAN DEFAULT 0, - `keep_history` MEDIUMINT NOT NULL DEFAULT -2, -- v0.7 `ttl` INT NOT NULL DEFAULT 0, -- v0.7.3 `attributes` TEXT, -- v1.11.0 `cache_nbEntries` INT DEFAULT 0, -- v0.7 `cache_nbUnreads` INT DEFAULT 0, -- v0.7 PRIMARY KEY (`id`), - FOREIGN KEY (`category`) REFERENCES `%1$scategory`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, + FOREIGN KEY (`category`) REFERENCES `_category`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, UNIQUE KEY (`url`), -- v0.7 INDEX (`name`), -- v0.7 - INDEX (`priority`), -- v0.7 - INDEX (`keep_history`) -- v0.7 + INDEX (`priority`) -- v0.7 ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; -CREATE TABLE IF NOT EXISTS `%1$sentry` ( +CREATE TABLE IF NOT EXISTS `_entry` ( `id` BIGINT NOT NULL, -- v0.7 `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, -- Maximum for UNIQUE is 767B `title` VARCHAR(255) NOT NULL, `author` VARCHAR(255), - `content_bin` BLOB, -- v0.7 + `content_bin` MEDIUMBLOB, -- v0.7 `link` VARCHAR(1023) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, `date` INT(11), -- Until year 2038 `lastSeen` INT(11) DEFAULT 0, -- v1.1.1, Until year 2038 @@ -51,25 +52,29 @@ CREATE TABLE IF NOT EXISTS `%1$sentry` ( `id_feed` SMALLINT, -- v0.7 `tags` VARCHAR(1023), PRIMARY KEY (`id`), - FOREIGN KEY (`id_feed`) REFERENCES `%1$sfeed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`id_feed`) REFERENCES `_feed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE KEY (`id_feed`,`guid`), -- v0.7 INDEX (`is_favorite`), -- v0.7 INDEX (`is_read`), -- v0.7 - INDEX `entry_lastSeen_index` (`lastSeen`) -- v1.1.1 - -- INDEX `entry_feed_read_index` (`id_feed`,`is_read`) -- v1.7 Located futher down + INDEX `entry_lastSeen_index` (`lastSeen`), -- v1.1.1 + INDEX `entry_feed_read_index` (`id_feed`,`is_read`) -- v1.7 ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; -INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); -'); +INSERT IGNORE INTO `_category` (id, name) VALUES(1, "Uncategorized"); +SQL; -define('SQL_CREATE_TABLE_ENTRYTMP', ' -CREATE TABLE IF NOT EXISTS `%1$sentrytmp` ( -- v1.7 +$SQL_CREATE_INDEX_ENTRY_1 = <<<'SQL' +CREATE INDEX `entry_feed_read_index` ON `_entry` (`id_feed`,`is_read`); -- v1.7 +SQL; + +$SQL_CREATE_TABLE_ENTRYTMP = <<<'SQL' +CREATE TABLE IF NOT EXISTS `_entrytmp` ( -- v1.7 `id` BIGINT NOT NULL, `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, `title` VARCHAR(255) NOT NULL, `author` VARCHAR(255), - `content_bin` BLOB, + `content_bin` MEDIUMBLOB, `link` VARCHAR(1023) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, `date` INT(11), `lastSeen` INT(11) DEFAULT 0, @@ -79,17 +84,15 @@ CREATE TABLE IF NOT EXISTS `%1$sentrytmp` ( -- v1.7 `id_feed` SMALLINT, `tags` VARCHAR(1023), PRIMARY KEY (`id`), - FOREIGN KEY (`id_feed`) REFERENCES `%1$sfeed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`id_feed`) REFERENCES `_feed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE KEY (`id_feed`,`guid`), INDEX (`date`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; +SQL; -CREATE INDEX `entry_feed_read_index` ON `%1$sentry`(`id_feed`,`is_read`); -- v1.7 Located here to be auto-added -'); - -define('SQL_CREATE_TABLE_TAGS', ' -CREATE TABLE IF NOT EXISTS `%1$stag` ( -- v1.12 +$SQL_CREATE_TABLE_TAGS = <<<'SQL' +CREATE TABLE IF NOT EXISTS `_tag` ( -- v1.12 `id` SMALLINT NOT NULL AUTO_INCREMENT, `name` VARCHAR(63) NOT NULL, `attributes` TEXT, @@ -98,46 +101,27 @@ CREATE TABLE IF NOT EXISTS `%1$stag` ( -- v1.12 ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; -CREATE TABLE IF NOT EXISTS `%1$sentrytag` ( -- v1.12 +CREATE TABLE IF NOT EXISTS `_entrytag` ( -- v1.12 `id_tag` SMALLINT, `id_entry` BIGINT, PRIMARY KEY (`id_tag`,`id_entry`), - FOREIGN KEY (`id_tag`) REFERENCES `%1$stag`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY (`id_entry`) REFERENCES `%1$sentry`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`id_tag`) REFERENCES `_tag`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (`id_entry`) REFERENCES `_entry`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, INDEX (`id_entry`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; -'); - -define('SQL_INSERT_FEEDS', ' -INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("https://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "https://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400); -INSERT IGNORE INTO `%1$sfeed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS @ GitHub", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400); -'); - -define('SQL_DROP_TABLES', 'DROP TABLE IF EXISTS `%1$sentrytag`, `%1$stag`, `%1$sentrytmp`, `%1$sentry`, `%1$sfeed`, `%1$scategory`'); - -define('SQL_UPDATE_UTF8MB4', ' -ALTER DATABASE `%2$s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- v1.5.0 - -ALTER TABLE `%1$scategory` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -UPDATE `%1$scategory` SET name=SUBSTRING(name,1,' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . ') WHERE LENGTH(name) > ' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . '; -ALTER TABLE `%1$scategory` MODIFY `name` VARCHAR(' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . ') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL; -OPTIMIZE TABLE `%1$scategory`; +SQL; -ALTER TABLE `%1$sfeed` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -UPDATE `%1$sfeed` SET name=SUBSTRING(name,1,' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . ') WHERE LENGTH(name) > ' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . '; -ALTER TABLE `%1$sfeed` MODIFY `name` VARCHAR(' . FreshRSS_DatabaseDAO::LENGTH_INDEX_UNICODE . ') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL; -ALTER TABLE `%1$sfeed` MODIFY `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -OPTIMIZE TABLE `%1$sfeed`; +$SQL_INSERT_FEED = <<<'SQL' +INSERT IGNORE INTO `_feed` (url, category, name, website, description, ttl) + VALUES(:url, 1, :name, :website, :description, 86400); +SQL; -ALTER TABLE `%1$sentry` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `%1$sentry` MODIFY `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL; -ALTER TABLE `%1$sentry` MODIFY `author` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `%1$sentry` MODIFY `tags` VARCHAR(1023) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -OPTIMIZE TABLE `%1$sentry`; -'); +$SQL_DROP_TABLES = <<<'SQL' +DROP TABLE IF EXISTS `_entrytag`, `_tag`, `_entrytmp`, `_entry`, `_feed`, `_category`; +SQL; -define('SQL_UPDATE_GUID_LATIN1_BIN', ' -- v1.12 -ALTER TABLE `%1$sentrytmp` MODIFY `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL; -ALTER TABLE `%1$sentry` MODIFY `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL; -'); +$SQL_UPDATE_GUID_LATIN1_BIN = <<<'SQL' +ALTER TABLE `_entrytmp` MODIFY `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL; -- v1.12 +ALTER TABLE `_entry` MODIFY `guid` VARCHAR(760) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL; +SQL; diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index e68e6f3be..53afc8a17 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -1,14 +1,16 @@ <?php -define('SQL_CREATE_DB', 'CREATE DATABASE "%1$s" ENCODING \'UTF8\';'); +$SQL_CREATE_DB = <<<'SQL' +CREATE DATABASE "%1$s" ENCODING 'UTF8'; +SQL; -global $SQL_CREATE_TABLES; -$SQL_CREATE_TABLES = array( -'CREATE TABLE IF NOT EXISTS "%1$scategory" ( +$SQL_CREATE_TABLES = <<<'SQL' +CREATE TABLE IF NOT EXISTS `_category` ( "id" SERIAL PRIMARY KEY, - "name" VARCHAR(255) UNIQUE NOT NULL -);', + "name" VARCHAR(255) UNIQUE NOT NULL, + "attributes" TEXT -- v1.15.0 +); -'CREATE TABLE IF NOT EXISTS "%1$sfeed" ( +CREATE TABLE IF NOT EXISTS `_feed` ( "id" SERIAL PRIMARY KEY, "url" VARCHAR(511) UNIQUE NOT NULL, "category" SMALLINT DEFAULT 0, @@ -20,18 +22,16 @@ $SQL_CREATE_TABLES = array( "pathEntries" VARCHAR(511) DEFAULT NULL, "httpAuth" VARCHAR(511) DEFAULT NULL, "error" SMALLINT DEFAULT 0, - "keep_history" INT NOT NULL DEFAULT -2, "ttl" INT NOT NULL DEFAULT 0, "attributes" TEXT, -- v1.11.0 "cache_nbEntries" INT DEFAULT 0, "cache_nbUnreads" INT DEFAULT 0, - FOREIGN KEY ("category") REFERENCES "%1$scategory" ("id") ON DELETE SET NULL ON UPDATE CASCADE -);', -'CREATE INDEX "%1$sname_index" ON "%1$sfeed" ("name");', -'CREATE INDEX "%1$spriority_index" ON "%1$sfeed" ("priority");', -'CREATE INDEX "%1$skeep_history_index" ON "%1$sfeed" ("keep_history");', + FOREIGN KEY ("category") REFERENCES `_category` ("id") ON DELETE SET NULL ON UPDATE CASCADE +); +CREATE INDEX IF NOT EXISTS `_name_index` ON `_feed` ("name"); +CREATE INDEX IF NOT EXISTS `_priority_index` ON `_feed` ("priority"); -'CREATE TABLE IF NOT EXISTS "%1$sentry" ( +CREATE TABLE IF NOT EXISTS `_entry` ( "id" BIGINT NOT NULL PRIMARY KEY, "guid" VARCHAR(760) NOT NULL, "title" VARCHAR(255) NOT NULL, @@ -45,22 +45,26 @@ $SQL_CREATE_TABLES = array( "is_favorite" SMALLINT NOT NULL DEFAULT 0, "id_feed" SMALLINT, "tags" VARCHAR(1023), - FOREIGN KEY ("id_feed") REFERENCES "%1$sfeed" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("id_feed") REFERENCES `_feed` ("id") ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE ("id_feed","guid") -);', -'CREATE INDEX "%1$sis_favorite_index" ON "%1$sentry" ("is_favorite");', -'CREATE INDEX "%1$sis_read_index" ON "%1$sentry" ("is_read");', -'CREATE INDEX "%1$sentry_lastSeen_index" ON "%1$sentry" ("lastSeen");', - -'INSERT INTO "%1$scategory" (id, name) - SELECT 1, \'%2$s\' - WHERE NOT EXISTS (SELECT id FROM "%1$scategory" WHERE id = 1) - RETURNING nextval(\'"%1$scategory_id_seq"\');', ); +CREATE INDEX IF NOT EXISTS `_is_favorite_index` ON `_entry` ("is_favorite"); +CREATE INDEX IF NOT EXISTS `_is_read_index` ON `_entry` ("is_read"); +CREATE INDEX IF NOT EXISTS `_entry_lastSeen_index` ON `_entry` ("lastSeen"); +CREATE INDEX IF NOT EXISTS `_entry_feed_read_index` ON `_entry` ("id_feed","is_read"); -- v1.7 + +INSERT INTO `_category` (id, name) + SELECT 1, 'Uncategorized' + WHERE NOT EXISTS (SELECT id FROM `_category` WHERE id = 1) + RETURNING nextval('`_category_id_seq`'); +SQL; -global $SQL_CREATE_TABLE_ENTRYTMP; -$SQL_CREATE_TABLE_ENTRYTMP = array( -'CREATE TABLE IF NOT EXISTS "%1$sentrytmp" ( -- v1.7 +$SQL_CREATE_INDEX_ENTRY_1 = <<<'SQL' +CREATE INDEX IF NOT EXISTS `_entry_feed_read_index` ON `_entry` ("id_feed","is_read"); -- v1.7 +SQL; + +$SQL_CREATE_TABLE_ENTRYTMP = <<<'SQL' +CREATE TABLE IF NOT EXISTS `_entrytmp` ( -- v1.7 "id" BIGINT NOT NULL PRIMARY KEY, "guid" VARCHAR(760) NOT NULL, "title" VARCHAR(255) NOT NULL, @@ -74,39 +78,34 @@ $SQL_CREATE_TABLE_ENTRYTMP = array( "is_favorite" SMALLINT NOT NULL DEFAULT 0, "id_feed" SMALLINT, "tags" VARCHAR(1023), - FOREIGN KEY ("id_feed") REFERENCES "%1$sfeed" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("id_feed") REFERENCES `_feed` ("id") ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE ("id_feed","guid") -);', -'CREATE INDEX "%1$sentrytmp_date_index" ON "%1$sentrytmp" ("date");', - -'CREATE INDEX "%1$sentry_feed_read_index" ON "%1$sentry" ("id_feed","is_read");', //v1.7 ); +CREATE INDEX IF NOT EXISTS `_entrytmp_date_index` ON `_entrytmp` ("date"); +SQL; -global $SQL_CREATE_TABLE_TAGS; -$SQL_CREATE_TABLE_TAGS = array( -'CREATE TABLE IF NOT EXISTS "%1$stag" ( -- v1.12 +$SQL_CREATE_TABLE_TAGS = <<<'SQL' +CREATE TABLE IF NOT EXISTS `_tag` ( -- v1.12 "id" SERIAL PRIMARY KEY, "name" VARCHAR(63) UNIQUE NOT NULL, "attributes" TEXT -);', -'CREATE TABLE IF NOT EXISTS "%1$sentrytag" ( +); +CREATE TABLE IF NOT EXISTS `_entrytag` ( "id_tag" SMALLINT, "id_entry" BIGINT, PRIMARY KEY ("id_tag","id_entry"), - FOREIGN KEY ("id_tag") REFERENCES "%1$stag" ("id") ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY ("id_entry") REFERENCES "%1$sentry" ("id") ON DELETE CASCADE ON UPDATE CASCADE -);', -'CREATE INDEX "%1$sentrytag_id_entry_index" ON "%1$sentrytag" ("id_entry");', + FOREIGN KEY ("id_tag") REFERENCES `_tag` ("id") ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY ("id_entry") REFERENCES `_entry` ("id") ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX IF NOT EXISTS `_entrytag_id_entry_index` ON `_entrytag` ("id_entry"); +SQL; -global $SQL_INSERT_FEEDS; -$SQL_INSERT_FEEDS = array( -'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) - SELECT \'https://freshrss.org/feeds/all.atom.xml\', 1, \'FreshRSS.org\', \'https://freshrss.org/\', \'FreshRSS, a free, self-hostable aggregator…\', 86400 - WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'https://freshrss.org/feeds/all.atom.xml\');', -'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) - SELECT \'https://github.com/FreshRSS/FreshRSS/releases.atom\', 1, \'FreshRSS @ GitHub\', \'https://github.com/FreshRSS/FreshRSS/\', \'FreshRSS releases @ GitHub\', 86400 - WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'https://github.com/FreshRSS/FreshRSS/releases.atom\');', -); +$SQL_INSERT_FEED = <<<'SQL' +INSERT INTO `_feed` (url, category, name, website, description, ttl) + SELECT :url::VARCHAR, 1, :name, :website, :description, 86400 + WHERE NOT EXISTS (SELECT id FROM `_feed` WHERE url = :url); +SQL; -define('SQL_DROP_TABLES', 'DROP TABLE IF EXISTS "%1$sentrytag", "%1$stag", "%1$sentrytmp", "%1$sentry", "%1$sfeed", "%1$scategory"'); +$SQL_DROP_TABLES = <<<'SQL' +DROP TABLE IF EXISTS `_entrytag`, `_tag`, `_entrytmp`, `_entry`, `_feed`, `_category`; +SQL; diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 1dd5f2647..2a4763637 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -1,13 +1,17 @@ <?php -global $SQL_CREATE_TABLES; -$SQL_CREATE_TABLES = array( -'CREATE TABLE IF NOT EXISTS `category` ( +$SQL_CREATE_DB = <<<'SQL' +SELECT 1; -- Do nothing for SQLite +SQL; + +$SQL_CREATE_TABLES = <<<'SQL' +CREATE TABLE IF NOT EXISTS `category` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` VARCHAR(255) NOT NULL, + `attributes` TEXT, -- v1.15.0 UNIQUE (`name`) -);', +); -'CREATE TABLE IF NOT EXISTS `feed` ( +CREATE TABLE IF NOT EXISTS `feed` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` VARCHAR(511) NOT NULL, `category` SMALLINT DEFAULT 0, @@ -19,19 +23,17 @@ $SQL_CREATE_TABLES = array( `pathEntries` VARCHAR(511) DEFAULT NULL, `httpAuth` VARCHAR(511) DEFAULT NULL, `error` BOOLEAN DEFAULT 0, - `keep_history` MEDIUMINT NOT NULL DEFAULT -2, `ttl` INT NOT NULL DEFAULT 0, `attributes` TEXT, -- v1.11.0 `cache_nbEntries` INT DEFAULT 0, `cache_nbUnreads` INT DEFAULT 0, FOREIGN KEY (`category`) REFERENCES `category`(`id`) ON DELETE SET NULL ON UPDATE CASCADE, UNIQUE (`url`) -);', -'CREATE INDEX IF NOT EXISTS feed_name_index ON `feed`(`name`);', -'CREATE INDEX IF NOT EXISTS feed_priority_index ON `feed`(`priority`);', -'CREATE INDEX IF NOT EXISTS feed_keep_history_index ON `feed`(`keep_history`);', +); +CREATE INDEX IF NOT EXISTS feed_name_index ON `feed`(`name`); +CREATE INDEX IF NOT EXISTS feed_priority_index ON `feed`(`priority`); -'CREATE TABLE IF NOT EXISTS `entry` ( +CREATE TABLE IF NOT EXISTS `entry` ( `id` BIGINT NOT NULL, `guid` VARCHAR(760) NOT NULL, `title` VARCHAR(255) NOT NULL, @@ -48,17 +50,21 @@ $SQL_CREATE_TABLES = array( PRIMARY KEY (`id`), FOREIGN KEY (`id_feed`) REFERENCES `feed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE (`id_feed`,`guid`) -);', -'CREATE INDEX IF NOT EXISTS entry_is_favorite_index ON `entry`(`is_favorite`);', -'CREATE INDEX IF NOT EXISTS entry_is_read_index ON `entry`(`is_read`);', -'CREATE INDEX IF NOT EXISTS entry_lastSeen_index ON `entry`(`lastSeen`);', //v1.1.1 - -'INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "%2$s");', ); +CREATE INDEX IF NOT EXISTS entry_is_favorite_index ON `entry`(`is_favorite`); +CREATE INDEX IF NOT EXISTS entry_is_read_index ON `entry`(`is_read`); +CREATE INDEX IF NOT EXISTS entry_lastSeen_index ON `entry`(`lastSeen`); -- //v1.1.1 +CREATE INDEX IF NOT EXISTS entry_feed_read_index ON `entry`(`id_feed`,`is_read`); -- v1.7 + +INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "Uncategorized"); +SQL; -global $SQL_CREATE_TABLE_ENTRYTMP; -$SQL_CREATE_TABLE_ENTRYTMP = array( -'CREATE TABLE IF NOT EXISTS `entrytmp` ( -- v1.7 +$SQL_CREATE_INDEX_ENTRY_1 = <<<'SQL' +CREATE INDEX IF NOT EXISTS entry_feed_read_index ON `entry`(`id_feed`,`is_read`); -- v1.7 +SQL; + +$SQL_CREATE_TABLE_ENTRYTMP = <<<'SQL' +CREATE TABLE IF NOT EXISTS `entrytmp` ( -- v1.7 `id` BIGINT NOT NULL, `guid` VARCHAR(760) NOT NULL, `title` VARCHAR(255) NOT NULL, @@ -75,36 +81,37 @@ $SQL_CREATE_TABLE_ENTRYTMP = array( PRIMARY KEY (`id`), FOREIGN KEY (`id_feed`) REFERENCES `feed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE (`id_feed`,`guid`) -);', -'CREATE INDEX IF NOT EXISTS entrytmp_date_index ON `entrytmp`(`date`);', - -'CREATE INDEX IF NOT EXISTS `entry_feed_read_index` ON `entry`(`id_feed`,`is_read`);', //v1.7 ); +CREATE INDEX IF NOT EXISTS entrytmp_date_index ON `entrytmp`(`date`); +SQL; -global $SQL_CREATE_TABLE_TAGS; -$SQL_CREATE_TABLE_TAGS = array( -'CREATE TABLE IF NOT EXISTS `tag` ( -- v1.12 +$SQL_CREATE_TABLE_TAGS = <<<'SQL' +CREATE TABLE IF NOT EXISTS `tag` ( -- v1.12 `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` VARCHAR(63) NOT NULL, `attributes` TEXT, UNIQUE (`name`) -);', -'CREATE TABLE IF NOT EXISTS `entrytag` ( +); +CREATE TABLE IF NOT EXISTS `entrytag` ( `id_tag` SMALLINT, - `id_entry` SMALLINT, + `id_entry` BIGINT, PRIMARY KEY (`id_tag`,`id_entry`), FOREIGN KEY (`id_tag`) REFERENCES `tag` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (`id_entry`) REFERENCES `entry` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -);', -'CREATE INDEX entrytag_id_entry_index ON `entrytag` (`id_entry`);', ); +CREATE INDEX IF NOT EXISTS entrytag_id_entry_index ON `entrytag` (`id_entry`); +SQL; -global $SQL_INSERT_FEEDS; -$SQL_INSERT_FEEDS = array( -'INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) - VALUES ("https://freshrss.org/feeds/all.atom.xml", 1, "FreshRSS.org", "https://freshrss.org/", "FreshRSS, a free, self-hostable aggregator…", 86400);', -'INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) - VALUES ("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS releases", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400);', -); +$SQL_INSERT_FEED = <<<'SQL' +INSERT OR IGNORE INTO `feed` (url, category, name, website, description, ttl) + VALUES(:url, 1, :name, :website, :description, 86400); +SQL; -define('SQL_DROP_TABLES', 'DROP TABLE IF EXISTS `entrytag`, `tag`, `entrytmp`, `entry`, `feed`, `category`'); +$SQL_DROP_TABLES = <<<'SQL' +DROP TABLE IF EXISTS `entrytag`; +DROP TABLE IF EXISTS `tag`; +DROP TABLE IF EXISTS `entrytmp`; +DROP TABLE IF EXISTS `entry`; +DROP TABLE IF EXISTS `feed`; +DROP TABLE IF EXISTS `category`; +SQL; diff --git a/app/i18n/cz/admin.php b/app/i18n/cz/admin.php index 68127f571..a2a509560 100644 --- a/app/i18n/cz/admin.php +++ b/app/i18n/cz/admin.php @@ -163,6 +163,7 @@ return array( 'help' => 'in seconds', //TODO - Translation 'number' => 'Duration to keep logged in', //TODO - Translation ), + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Instance name', //TODO - Translation 'max-categories' => 'Categories per user limit', //TODO - Translation 'max-feeds' => 'Feeds per user limit', //TODO - Translation diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php index a2618e310..056e895a7 100644 --- a/app/i18n/cz/conf.php +++ b/app/i18n/cz/conf.php @@ -3,13 +3,21 @@ return array( 'archiving' => 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ž', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Datum vydání', 'related_tags' => 'Související tagy', //TODO - Translation 'sharing' => 'Sdílení', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Horní řádek', ), 'language' => 'Jazyk', @@ -45,6 +54,7 @@ return array( '_' => 'Smazání účtu', 'warn' => 'Váš účet bude smazán spolu se všemi souvisejícími daty', ), + 'email' => 'Email', 'password_api' => 'Password API<br /><small>(tzn. pro mobilní aplikace)</small>', 'password_form' => 'Heslo<br /><small>(pro přihlášení webovým formulářem)</small>', 'password_format' => 'Alespoň 7 znaků', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Více informací', 'print' => 'Tisk', 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php index 08fce0280..de1456187 100644 --- a/app/i18n/cz/gen.php +++ b/app/i18n/cz/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Aktualizovat', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← Zpět na seznam RSS kanálů', 'cancel' => 'Zrušit', 'create' => 'Vytvořit', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Email', 'keep_logged_in' => 'Zapamatovat přihlášení <small>(%s dny)</small>', 'login' => 'Login', @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/cz/index.php b/app/i18n/cz/index.php index 00f424fe8..078ca5ca1 100644 --- a/app/i18n/cz/index.php +++ b/app/i18n/cz/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Hlášení chyb', 'credits' => 'Poděkování', 'credits_content' => 'Některé designové prvky pocházejí z <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>, FreshRSS ale tuto platformu nevyužívá. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Ikony</a> pocházejí z <a href="https://www.gnome.org/">GNOME projektu</a>. Font <em>Open Sans</em> vytvořil <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS je založen na PHP framework <a href="https://github.com/marienfressinaud/MINZ">Minz</a>.', - 'freshrss_description' => 'FreshRSS je čtečka RSS kanálů určená k provozu na vlastním serveru, podobná <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> nebo <a href="http://leed.idleman.fr/">Leed</a>. Je to nenáročný a jednoduchý, zároveň ale mocný a konfigurovatelný nástroj.', + 'freshrss_description' => 'FreshRSS je čtečka RSS kanálů určená k provozu na vlastním serveru, podobná <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> nebo <a href="https://github.com/LeedRSS/Leed">Leed</a>. Je to nenáročný a jednoduchý, zároveň ale mocný a konfigurovatelný nástroj.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">na Github</a>', 'license' => 'Licence', 'project_website' => 'Stránka projektu', @@ -15,6 +15,9 @@ return array( 'version' => 'Verze', 'website' => 'Webové stránka', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'Můžete přidat kanály.', 'empty' => 'Žádné články k zobrazení.', diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index b2bdf416b..f2c259d15 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Kategorie', 'add' => 'Přidat kategorii', + 'archiving' => 'Archivace', 'empty' => 'Vyprázdit kategorii', 'information' => 'Informace', 'new' => 'Nová kategorie', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Název', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Nejsou označeny žádné kanály.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.',// TODO + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox feed reader', //TODO - Translation ), 'import_export' => array( diff --git a/app/i18n/cz/user.php b/app/i18n/cz/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/cz/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/de/admin.php b/app/i18n/de/admin.php index f0307dcab..d075bf28f 100644 --- a/app/i18n/de/admin.php +++ b/app/i18n/de/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Systemeinstellungen', 'auto-update-url' => 'Auto-update URL', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Dein Reader Name', 'max-categories' => 'Anzahl erlaubter Kategorien pro Benutzer', 'max-feeds' => 'Anzahl erlaubter Feeds pro Benutzer', diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index 40209576e..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Datum der Veröffentlichung', 'related_tags' => 'Verwandte Tags', 'sharing' => 'Teilen', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Kopfzeile', ), 'language' => 'Sprache', @@ -45,6 +54,7 @@ return array( '_' => 'Accountlöschung', 'warn' => 'Dein Account und alle damit bezogenen Daten werden gelöscht.', ), + 'email' => 'E-Mail-Adresse', 'password_api' => 'Passwort-API<br /><small>(z. B. für mobile Anwendungen)</small>', 'password_form' => 'Passwort<br /><small>(für die Anmeldemethode per Webformular)</small>', 'password_format' => 'mindestens 7 Zeichen', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'E-Mail', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Weitere Informationen', 'print' => 'Drucken', 'remove' => 'Entferne Teilen-Dienst', diff --git a/app/i18n/de/gen.php b/app/i18n/de/gen.php index c02a55b2c..e2dd2a251 100644 --- a/app/i18n/de/gen.php +++ b/app/i18n/de/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Aktualisieren', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← Zurück zu Ihren RSS-Feeds gehen', 'cancel' => 'Abbrechen', 'create' => 'Erstellen', @@ -22,6 +23,7 @@ return array( 'update' => 'Aktualisieren', ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'E-Mail-Adresse', 'keep_logged_in' => 'Eingeloggt bleiben <small>(%s Tage)</small>', 'login' => 'Anmelden', @@ -160,15 +162,22 @@ 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*', 'email' => 'E-Mail', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known-Seite (https://withknown.com)', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/de/index.php b/app/i18n/de/index.php index 10172e6f5..85ab3bb26 100644 --- a/app/i18n/de/index.php +++ b/app/i18n/de/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Fehlerberichte', 'credits' => 'Credits', 'credits_content' => 'Einige Designelemente stammen von <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>, obwohl FreshRSS dieses Framework nicht nutzt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> stammen vom <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> Font wurde von <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a> erstellt. FreshRSS basiert auf <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, einem PHP-Framework.', - 'freshrss_description' => 'FreshRSS ist ein RSS-Feedsaggregator zum selbst hosten wie zum Beispiel <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> oder <a href="http://leed.idleman.fr/">Leed</a>. Er ist leicht und einfach zu handhaben und gleichzeitig ein leistungsstarkes und konfigurierbares Werkzeug.', + 'freshrss_description' => 'FreshRSS ist ein RSS-Feedsaggregator zum selbst hosten wie zum Beispiel <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> oder <a href="https://github.com/LeedRSS/Leed">Leed</a>. Er ist leicht und einfach zu handhaben und gleichzeitig ein leistungsstarkes und konfigurierbares Werkzeug.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>', 'license' => 'Lizenz', 'project_website' => 'Projekt-Webseite', @@ -15,6 +15,9 @@ return array( 'version' => 'Version', 'website' => 'Webseite', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'Sie können Feeds hinzufügen.', 'empty' => 'Es gibt keinen Artikel zum Anzeigen.', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index abc01b954..754ac0866 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Kategorie', 'add' => 'Eine Kategorie hinzufügen', + 'archiving' => 'Archivierung', 'empty' => 'Leere Kategorie', 'information' => 'Information', 'new' => 'Neue Kategorie', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Titel', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em> eingefügt.', 'mute' => 'Stumm schalten', 'no_selected' => 'Kein Feed ausgewählt.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Folge den <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">hier</a> beschriebenen Schritten um FreshRSS zu Deiner Firefox RSS-Reader Liste hinzuzufügen.', + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox RSS-Reader', ), 'import_export' => array( diff --git a/app/i18n/de/user.php b/app/i18n/de/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/de/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/en/admin.php b/app/i18n/en/admin.php index 004089ffc..c5ab183e8 100644 --- a/app/i18n/en/admin.php +++ b/app/i18n/en/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'System configuration', 'auto-update-url' => 'Auto-update server URL', + 'force_email_validation' => 'Force email addresses validation', 'instance-name' => 'Instance name', 'max-categories' => 'Categories per user limit', 'max-feeds' => 'Feeds per user limit', diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index fde78f5b5..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Date of publication', 'related_tags' => 'Article tags', 'sharing' => 'Sharing', + 'display_authors' => 'Authors', 'top_line' => 'Top line', ), 'language' => 'Language', @@ -45,6 +54,7 @@ return array( '_' => 'Account deletion', 'warn' => 'Your account and all related data will be deleted.', ), + 'email' => 'Email address', 'password_api' => 'API password<br /><small>(e.g., for mobile apps)</small>', 'password_form' => 'Password<br /><small>(for the Web-form login method)</small>', 'password_format' => 'At least 7 characters', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'More information', 'print' => 'Print', 'remove' => 'Remove sharing method', diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php index 32f5ee02e..fc1bd587a 100644 --- a/app/i18n/en/gen.php +++ b/app/i18n/en/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Actualize', + 'back' => '← Go back', 'back_to_rss_feeds' => '← Go back to your RSS feeds', 'cancel' => 'Cancel', 'create' => 'Create', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', 'email' => 'Email address', 'keep_logged_in' => 'Keep me logged in <small>(%s days)</small>', 'login' => 'Login', @@ -127,6 +129,7 @@ return array( 'oc' => 'Occitan', 'pt-br' => 'Português (Brasil)', 'ru' => 'Русский', + 'sk' => 'Slovenčina', 'tr' => 'Türkçe', 'zh-cn' => '简体中文', ), @@ -160,15 +163,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/en/index.php b/app/i18n/en/index.php index 46c415816..71bf8b53e 100644 --- a/app/i18n/en/index.php +++ b/app/i18n/en/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Bugs reports', 'credits' => 'Credits', 'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.', - 'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="http://leed.idleman.fr/">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.', + 'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="https://github.com/LeedRSS/Leed">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>', 'license' => 'License', 'project_website' => 'Project website', @@ -15,6 +15,9 @@ return array( 'version' => 'Version', 'website' => 'Website', ), + 'tos' => array( + 'title' => 'Terms of Service', + ), 'feed' => array( 'add' => 'You may add some feeds.', 'empty' => 'There is no article to show.', diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index fde01f9df..252177940 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Category', 'add' => 'Add a category', + 'archiving' => 'Archiving', 'empty' => 'Empty category', 'information' => 'Information', 'new' => 'New category', + 'position' => 'Display position', + 'position_help' => 'To control category sort order', 'title' => 'Title', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', 'mute' => 'mute', 'no_selected' => 'No feed selected.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', 'title' => 'Firefox feed reader', ), 'import_export' => array( diff --git a/app/i18n/en/user.php b/app/i18n/en/user.php new file mode 100644 index 000000000..54d8dfa4d --- /dev/null +++ b/app/i18n/en/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', + 'required' => 'The email address is required.', + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', + 'email_sent' => 'An email has been sent to your address.', + 'error' => 'The email address failed to be validated.', + 'ok' => 'The email address has been validated.', + 'unneccessary' => 'The email address was already validated.', + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', + 'resend_email' => 'Resend the email', + 'title' => 'Email address validation', + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', + 'welcome' => 'Welcome %s,', + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', + ), + ), +); diff --git a/app/i18n/es/admin.php b/app/i18n/es/admin.php index 0ec8549bd..1af3bdcb2 100755 --- a/app/i18n/es/admin.php +++ b/app/i18n/es/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Configuración del sistema', 'auto-update-url' => 'URL de auto-actualización', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Nombre de la fuente', 'max-categories' => 'Límite de categorías por usuario', 'max-feeds' => 'Límite de fuentes por usuario', diff --git a/app/i18n/es/conf.php b/app/i18n/es/conf.php index b7d87f375..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Fecha de publicación', 'related_tags' => 'Etiquetas relacionadas', 'sharing' => 'Compartir', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Línea superior', ), 'language' => 'Idioma', @@ -45,6 +54,7 @@ return array( '_' => 'Borrar cuenta', 'warn' => 'Tu cuenta y todos los datos asociados serán eliminados.', ), + 'email' => 'Correo electrónico', 'password_api' => 'Contraseña API <br /><small>(para apps móviles, por ej.)</small>', 'password_form' => 'Contraseña<br /><small>(para el método de identificación por formulario web)</small>', 'password_format' => 'Mínimo de 7 caracteres', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Más información', 'print' => 'Print', 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/es/gen.php b/app/i18n/es/gen.php index db36e5f5b..538ddc8fe 100755 --- a/app/i18n/es/gen.php +++ b/app/i18n/es/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Actualizar', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← regresar a tus fuentes RSS', 'cancel' => 'Cancelar', 'create' => 'Crear', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Correo electrónico', 'keep_logged_in' => 'Mantenerme identificado <small>(%s días)</small>', 'login' => 'Conectar', @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/es/index.php b/app/i18n/es/index.php index d7a42537b..8977ee70b 100755 --- a/app/i18n/es/index.php +++ b/app/i18n/es/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Informe de fallos', 'credits' => 'Créditos', 'credits_content' => 'Aunque FreshRSS no usa ese entorno, algunos elementos del diseño están obtenidos de <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>. Los <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Iconos</a> han sido obtenidos del <a href="https://www.gnome.org/">proyecto GNOME</a>. La fuente <em>Open Sans</em> es una creación de <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS usa el entorno PHP <a href="https://github.com/marienfressinaud/MINZ">Minz</a>.', - 'freshrss_description' => 'FreshRSS es un agregador de fuentes RSS de alojamiento privado al estilo de <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="http://leed.idleman.fr/">Leed</a>. Es una herramienta potente, pero ligera y fácil de usar y configurar.', + 'freshrss_description' => 'FreshRSS es un agregador de fuentes RSS de alojamiento privado al estilo de <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="https://github.com/LeedRSS/Leed">Leed</a>. Es una herramienta potente, pero ligera y fácil de usar y configurar.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">en Github</a>', 'license' => 'Licencia', 'project_website' => 'Web del proyecto', @@ -15,6 +15,9 @@ return array( 'version' => 'Versión', 'website' => 'Web', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'Puedes añadir fuentes.', 'empty' => 'No hay artículos a mostrar.', diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php index 7d33c59fa..f1640b76b 100755 --- a/app/i18n/es/sub.php +++ b/app/i18n/es/sub.php @@ -13,9 +13,12 @@ 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', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Título', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'No hay funentes seleccionadas.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', //TODO - Translation + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox feed reader', //TODO - Translation ), 'import_export' => array( diff --git a/app/i18n/es/user.php b/app/i18n/es/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/es/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/fr/admin.php b/app/i18n/fr/admin.php index 74605b5ee..6002617fc 100644 --- a/app/i18n/fr/admin.php +++ b/app/i18n/fr/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Configuration du système', 'auto-update-url' => 'URL du service de mise à jour', + 'force_email_validation' => 'Forcer la validation des adresses email', 'instance-name' => 'Nom de l’instance', 'max-categories' => 'Limite de catégories par utilisateur', 'max-feeds' => 'Limite de flux par utilisateur', diff --git a/app/i18n/fr/conf.php b/app/i18n/fr/conf.php index ef29a360e..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Date de publication', 'related_tags' => 'Tags de l’article', 'sharing' => 'Partage', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Ligne du haut', ), 'language' => 'Langue', @@ -45,6 +54,7 @@ return array( '_' => 'Suppression du compte', 'warn' => 'Le compte et toutes les données associées vont être supprimées.', ), + 'email' => 'Adresse email', 'password_api' => 'Mot de passe API<br /><small>(ex. : pour applis mobiles)</small>', 'password_form' => 'Mot de passe<br /><small>(pour connexion par formulaire)</small>', 'password_format' => '7 caractères minimum', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Courriel', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Plus d’informations', 'print' => 'Print', 'remove' => 'Supprimer la méthode de partage', diff --git a/app/i18n/fr/gen.php b/app/i18n/fr/gen.php index 86d8461e6..a6875dd05 100644 --- a/app/i18n/fr/gen.php +++ b/app/i18n/fr/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Actualiser', + 'back' => '← Retour', 'back_to_rss_feeds' => '← Retour à vos flux RSS', 'cancel' => 'Annuler', 'create' => 'Créer', @@ -22,6 +23,7 @@ return array( 'update' => 'Mettre à jour', ), 'auth' => array( + 'accept_tos' => 'Accepter les <a href="%s">Conditions Générales d’Utilisation</a>.', 'email' => 'Adresse courriel', 'keep_logged_in' => 'Rester connecté <small>(%s jours)</small>', 'login' => 'Connexion', @@ -160,15 +162,22 @@ 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*', 'email' => 'Courriel', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Sites basés sur Known', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/fr/index.php b/app/i18n/fr/index.php index c9595e449..489de3849 100644 --- a/app/i18n/fr/index.php +++ b/app/i18n/fr/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Rapports de bugs', 'credits' => 'Crédits', 'credits_content' => 'Des éléments de design sont issus du <a href="http://twitter.github.io/bootstrap/">projet Bootstrap</a> bien que FreshRSS n’utilise pas ce framework. Les <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icônes</a> sont issues du <a href="https://www.gnome.org/">projet GNOME</a>. La police <em>Open Sans</em> utilisée a été créée par <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS repose sur <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.', - 'freshrss_description' => 'FreshRSS est un agrégateur de flux RSS à auto-héberger à l’image de <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> ou <a href="http://leed.idleman.fr/">Leed</a>. Il se veut léger et facile à prendre en main tout en étant un outil puissant et paramétrable.', + 'freshrss_description' => 'FreshRSS est un agrégateur de flux RSS à auto-héberger à l’image de <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> ou <a href="https://github.com/LeedRSS/Leed">Leed</a>. Il se veut léger et facile à prendre en main tout en étant un outil puissant et paramétrable.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">sur Github</a>', 'license' => 'Licence', 'project_website' => 'Site du projet', @@ -15,6 +15,9 @@ return array( 'version' => 'Version', 'website' => 'Site Internet', ), + 'tos' => array( + 'title' => 'Conditions Générales d’Utilisation', + ), 'feed' => array( 'add' => 'Vous pouvez ajouter des flux.', 'empty' => 'Il n’y a aucun article à afficher.', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index df44150c2..e12444315 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Catégorie', 'add' => 'Ajouter une catégorie', + 'archiving' => 'Archivage', 'empty' => 'Catégorie vide', 'information' => 'Informations', 'new' => 'Nouvelle catégorie', + 'position' => 'Position d’affichage', + 'position_help' => 'Pour contrôler l’ordre de tri des catégories', 'title' => 'Titre', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', 'mute' => 'muet', 'no_selected' => 'Aucun flux sélectionné.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Suivre les étapes décrites <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">ici</a> pour ajouter FreshRSS à la liste des lecteurs de flux dans Firefox.', + 'obsolete_63' => 'À partir de la version 63, Firefox ne supporte plus l’ajout de services d’abonnements.', 'title' => 'Lecteur de flux dans Firefox', ), 'import_export' => array( diff --git a/app/i18n/fr/user.php b/app/i18n/fr/user.php new file mode 100644 index 000000000..7b531c749 --- /dev/null +++ b/app/i18n/fr/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'L’adresse email est invalide.', + 'required' => 'L’adresse email est requise.', + ), + 'validation' => array( + 'change_email' => 'Vous pouvez changer votre adresse email <a href="%s">dans votre profil</a>.', + 'email_sent_to' => 'Nous venons d’envoyer un email à <strong>%s</strong>, veuillez suivre ses indications pour valider votre adresse.', + 'feedback' => array( + 'email_failed' => 'Nous n’avons pas pu vous envoyer d’email à cause d’une mauvaise configuration du serveur.', + 'email_sent' => 'Un email a été envoyé à votre adresse.', + 'error' => 'L’adresse email n’a pas pu être validée.', + 'ok' => 'L’adresse email a été validée.', + 'unnecessary' => 'L’adresse email a déjà été validée.', + 'wrong_token' => 'L’adresse email n’a pas pu être validée à cause d’un mauvais token.', + ), + 'need_to' => 'Vous devez valider votre adresse email avant de pouvoir utiliser %s.', + 'resend_email' => 'Renvoyer l’email', + 'title' => 'Validation de l’adresse email', + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'Vous devez accepter les conditions générales d’utilisation pour pouvoir vous inscrire.', + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'Vous devez valider votre compte', + 'welcome' => 'Bienvenue %s,', + 'body' => 'Vous venez de vous inscrire sur %s mais vous devez encore valider votre adresse email. Pour cela, veuillez cliquer sur ce lien :', + ), + ), +); diff --git a/app/i18n/he/admin.php b/app/i18n/he/admin.php index e0dfc405d..759b74e2a 100644 --- a/app/i18n/he/admin.php +++ b/app/i18n/he/admin.php @@ -163,6 +163,7 @@ return array( 'help' => 'in seconds', //TODO - Translation 'number' => 'Duration to keep logged in', //TODO - Translation ), + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Instance name', //TODO - Translation 'max-categories' => 'Categories per user limit', //TODO - Translation 'max-feeds' => 'Feeds per user limit', //TODO - Translation diff --git a/app/i18n/he/conf.php b/app/i18n/he/conf.php index 1eb447911..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' => 'אין לרענן אוטומטית יותר מ', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'תאריך הפרסום', 'related_tags' => 'תגיות קשורות', //TODO - Translation 'sharing' => 'שיתוף', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'שורה עליונה', ), 'language' => 'שפה', @@ -45,6 +54,7 @@ return array( '_' => 'Account deletion', //TODO - Translation 'warn' => 'Your account and all related data will be deleted.', //TODO - Translation ), + 'email' => 'Email address', //TODO - Translation 'password_api' => 'סיסמת API<br /><small>(לדוגמה ליישומים סלולריים)</small>', 'password_form' => 'סיסמה<br /><small>(לשימוש בטפוס ההרשמה)</small>', 'password_format' => 'At least 7 characters', //TODO - Translation @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'דואר אלקטרוני', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'מידע נוסף', 'print' => 'הדפסה', 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/he/gen.php b/app/i18n/he/gen.php index cf4a1fcda..34e6d77de 100644 --- a/app/i18n/he/gen.php +++ b/app/i18n/he/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'מימוש', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← חזרה להזנות הRSS שלך', 'cancel' => 'ביטול', 'create' => 'יצירה', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Email address', //TODO - Translation 'keep_logged_in' => 'השאר מחובר <small>חודש</small>', 'login' => 'כניסה לחשבון', @@ -160,15 +162,22 @@ 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*', 'email' => 'דואר אלקטרוני', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/he/index.php b/app/i18n/he/index.php index e01a02773..7cd1945e7 100644 --- a/app/i18n/he/index.php +++ b/app/i18n/he/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'דיווח באגים', 'credits' => 'קרדיטים', 'credits_content' => 'מאפייני עיצוב מסויימים הגיעו מ <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> אף על פי ש FreshRSS אינו משתמש בתשתית הזו. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">סמלילים</a> הגיעו מ <a href="https://www.gnome.org/"> פרוייקט GNOME </a>. <em>Open Sans</em> הגופן police נוצר על ידי <a href="https://www.google.com/webfonts/specimen/Open+Sans">Steve Matteson</a>. Favicons נאספים בעזרת <a href="https://getfavicon.appspot.com/">getFavicon API</a>. FreshRSS מבוסס על <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, תשתית PHP.', - 'freshrss_description' => 'FreshRSS הוא קורא RSS לאחסון עצמי בדומה ל <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> או <a href="http://leed.idleman.fr/">Leed</a>. אינו צורך משאבים רבים, וקל לתפעול אך בו בזמן חזק וניתן להתאמה.', + 'freshrss_description' => 'FreshRSS הוא קורא RSS לאחסון עצמי בדומה ל <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> או <a href="https://github.com/LeedRSS/Leed">Leed</a>. אינו צורך משאבים רבים, וקל לתפעול אך בו בזמן חזק וניתן להתאמה.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">בגיטהאב</a>', 'license' => 'רישיון', 'project_website' => 'אתר', @@ -15,6 +15,9 @@ return array( 'version' => 'גרסה', 'website' => 'אתר', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'ניתן להוסיף הזנות חדשות.', 'empty' => 'אין מאמר להצגה.', diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php index 8a629defb..3fd0f267a 100644 --- a/app/i18n/he/sub.php +++ b/app/i18n/he/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'קטגוריה', 'add' => 'הוספת קטגוריה', + 'archiving' => 'ארכוב', 'empty' => 'Empty category', //TODO - Translation 'information' => 'מידע', 'new' => 'קטגוריה חדשה', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'כותרת', ), 'feed' => array( @@ -40,7 +43,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => 'מידע', - 'keep_history' => 'מסםר מינימלי של מאמרים לשמור', + 'keep_min' => 'מסםר מינימלי של מאמרים לשמור', 'moved_category_deleted' => 'כאשר הקטגוריה נמחקת ההזנות שבתוכה אוטומטית מקוטלגות תחת <em>%s</em>.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'אף הזנה לא נבחרה.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', //TODO - Translation + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox feed reader', //TODO - Translation ), 'import_export' => array( diff --git a/app/i18n/he/user.php b/app/i18n/he/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/he/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/it/admin.php b/app/i18n/it/admin.php index d4253e9ba..8bb6c7bfe 100644 --- a/app/i18n/it/admin.php +++ b/app/i18n/it/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Configurazione di sistema', 'auto-update-url' => 'Auto-update server URL', //TODO - Translation + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Nome istanza', 'max-categories' => 'Limite categorie per utente', 'max-feeds' => 'Limite feeds per utente', diff --git a/app/i18n/it/conf.php b/app/i18n/it/conf.php index df4a5ebeb..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Data di pubblicazione', 'related_tags' => 'Tags correlati', //TODO - Translation 'sharing' => 'Condivisione', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Barra in alto', ), 'language' => 'Lingua', @@ -45,6 +54,7 @@ return array( '_' => 'Cancellazione account', 'warn' => 'Il tuo account e tutti i dati associati saranno cancellati.', ), + 'email' => 'Indirizzo email', 'password_api' => 'Password API<br /><small>(e.g., per applicazioni mobili)</small>', 'password_form' => 'Password<br /><small>(per il login classico)</small>', 'password_format' => 'Almeno 7 caratteri', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Ulteriori informazioni', 'print' => 'Stampa', 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/it/gen.php b/app/i18n/it/gen.php index 9cc40ffe3..50d4b4e6c 100644 --- a/app/i18n/it/gen.php +++ b/app/i18n/it/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Aggiorna', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← Indietro', 'cancel' => 'Annulla', 'create' => 'Crea', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', // TODO ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Indirizzo email', 'keep_logged_in' => 'Ricorda i dati <small>(%s giorni)</small>', 'login' => 'Accedi', @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Siti basati su Known', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/it/index.php b/app/i18n/it/index.php index 8162b1639..16c695a12 100644 --- a/app/i18n/it/index.php +++ b/app/i18n/it/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Bugs', 'credits' => 'Crediti', 'credits_content' => 'Alcuni elementi di design provengono da <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> sebbene FreshRSS non usi questo framework. Le <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icone</a> provengono dal progetto <a href="https://www.gnome.org/">GNOME</a>. Il carattere <em>Open Sans</em> è stato creato da <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS è basato su <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.', - 'freshrss_description' => 'FreshRSS è un aggregatore di feeds RSS da installare sul proprio host come <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="http://leed.idleman.fr/">Leed</a>. Leggero e facile da mantenere pur essendo molto configurabile e potente.', + 'freshrss_description' => 'FreshRSS è un aggregatore di feeds RSS da installare sul proprio host come <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="https://github.com/LeedRSS/Leed">Leed</a>. Leggero e facile da mantenere pur essendo molto configurabile e potente.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">su Github</a>', 'license' => 'Licenza', 'project_website' => 'Sito del progetto', @@ -15,6 +15,9 @@ return array( 'version' => 'Versione', 'website' => 'Sito', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'Aggiungi un Feed RSS', 'empty' => 'Non ci sono articoli da mostrare.', diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index 50738d9e3..78db7b0a6 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Categoria', 'add' => 'Aggiungi una categoria', + 'archiving' => 'Archiviazione', 'empty' => 'Categoria vuota', 'information' => 'Informazioni', 'new' => 'Nuova categoria', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Titolo', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Nessun feed selezionato.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', //TODO - Translation + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox feed reader', //TODO - Translation ), 'import_export' => array( diff --git a/app/i18n/it/user.php b/app/i18n/it/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/it/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/kr/admin.php b/app/i18n/kr/admin.php index 6312bd3fe..4a8e425d5 100644 --- a/app/i18n/kr/admin.php +++ b/app/i18n/kr/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => '시스템 설정', 'auto-update-url' => '자동 업데이트 서버 URL', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => '인스턴스 이름', 'max-categories' => '사용자별 카테고리 개수 제한', 'max-feeds' => '사용자별 피드 개수 제한', diff --git a/app/i18n/kr/conf.php b/app/i18n/kr/conf.php index acd4c40c1..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' => '다음 시간이 지나기 전에 새로고침 금지', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => '발행일', 'related_tags' => '관련 태그', 'sharing' => '공유', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => '상단', ), 'language' => '언어', @@ -45,6 +54,7 @@ return array( '_' => '계정 삭제', 'warn' => '당신의 계정과 관련된 모든 데이터가 삭제됩니다.', ), + 'email' => '메일 주소', 'password_api' => 'API 암호<br /><small>(예: 모바일 애플리케이션)</small>', 'password_form' => '암호<br /><small>(웹폼 로그인 방식 사용시)</small>', 'password_format' => '7 글자 이상이어야 합니다', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => '메일', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => '자세한 정보', 'print' => '인쇄', 'remove' => '공유 방법 삭제', diff --git a/app/i18n/kr/gen.php b/app/i18n/kr/gen.php index f7855c499..fdc95d431 100644 --- a/app/i18n/kr/gen.php +++ b/app/i18n/kr/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => '새 글 가져오기', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← RSS 피드로 돌아가기', 'cancel' => '취소', 'create' => '생성', @@ -22,6 +23,7 @@ return array( 'update' => '변경', ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => '메일 주소', 'keep_logged_in' => '로그인 유지 <small>(%s 일)</small>', 'login' => '로그인', @@ -160,15 +162,22 @@ 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*', 'email' => '메일', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/kr/index.php b/app/i18n/kr/index.php index bebc8bdec..6e582d906 100644 --- a/app/i18n/kr/index.php +++ b/app/i18n/kr/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => '버그 제보하기', 'credits' => '크레딧', 'credits_content' => 'FreshRSS는 <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> 프레임워크를 사용하진 않지만, 일부 디자인 요소를 가져왔습니다. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">아이콘들</a>은 <a href="https://www.gnome.org/">GNOME 프로젝트</a>에서 가져왔습니다. <em>Open Sans</em> 글꼴은 <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>가 제작하였습니다. FreshRSS는 PHP 프레임워크인 <a href="https://github.com/marienfressinaud/MINZ">Minz</a>에 기반하고 있습니다.', - 'freshrss_description' => 'FreshRSS는 <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> 또는 <a href="http://leed.idleman.fr/">Leed</a>와 같은 셀프 호스팅 기반의 RSS 피드 수집기입니다. FreshRSS는 강력하고 다양한 설정을 할 수 있으면서 도 가볍고 사용하기 쉽습니다.', + 'freshrss_description' => 'FreshRSS는 <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> 또는 <a href="https://github.com/LeedRSS/Leed">Leed</a>와 같은 셀프 호스팅 기반의 RSS 피드 수집기입니다. FreshRSS는 강력하고 다양한 설정을 할 수 있으면서 도 가볍고 사용하기 쉽습니다.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">Github 저장소에 제보</a>', 'license' => '라이센스', 'project_website' => '프로젝트 웹사이트', @@ -15,6 +15,9 @@ return array( 'version' => '버전', 'website' => '웹사이트', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => '피드를 추가하세요.', 'empty' => '글이 없습니다.', diff --git a/app/i18n/kr/sub.php b/app/i18n/kr/sub.php index f8eccfa27..ac45e4e73 100644 --- a/app/i18n/kr/sub.php +++ b/app/i18n/kr/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => '카테고리', 'add' => '카테고리 추가', + 'archiving' => '보관', 'empty' => '빈 카테고리', 'information' => '정보', 'new' => '새 카테고리', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => '제목', ), 'feed' => array( @@ -40,7 +43,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => '정보', - 'keep_history' => '최소 유지 글 개수', + 'keep_min' => '최소 유지 글 개수', 'moved_category_deleted' => '카테고리를 삭제하면, 해당 카테고리 아래에 있던 피드들은 자동적으로 <em>%s</em> 아래로 분류됩니다.', 'mute' => '무기한 새로고침 금지', 'no_selected' => '선택된 피드가 없습니다.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'FreshRSS를 Firefox 피드 리더에 추가하기 위해서는 <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">여기</a>의 설명을 따르세요.', + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox 피드 리더', ), 'import_export' => array( diff --git a/app/i18n/kr/user.php b/app/i18n/kr/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/kr/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/nl/admin.php b/app/i18n/nl/admin.php index e5d126eb8..1083c630b 100644 --- a/app/i18n/nl/admin.php +++ b/app/i18n/nl/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Systeem configuratie', 'auto-update-url' => 'Automatische update server URL', + 'force_email_validation' => 'Emailadresvalidatie forceren', 'instance-name' => 'Voorbeeld naam', 'max-categories' => 'Categorielimiet per gebruiker', 'max-feeds' => 'Feedlimiet per gebruiker', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index fa84ae184..ca6627cbb 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -1,15 +1,23 @@ <?php -/* Dutch translation by Wanabo. http://www.nieuwskop.be */ + return array( 'archiving' => array( '_' => 'Archivering', - 'advanced' => 'Geavanceerd', 'delete_after' => 'Verwijder artikelen na', + 'exception' => 'Zuiveringsuitzondering', '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' => 'Favorieten nooit verwijderen', + 'keep_min_by_feed' => 'Minimum aantal te behouden artikelen in de feed', + 'keep_labels' => 'Labels nooit verwijderen', + 'keep_unreads' => 'Ongelezen artikels nooit verwijderen', + 'maintenance' => 'Onderhoud', + 'optimize' => 'Database optimaliseren', 'optimize_help' => 'Doe dit zo af en toe om de omvang van de database te verkleinen', + 'policy' => 'Zuiveringsbeleid', + 'policy_warning' => 'Zonder zuiveringsbeleid wordt elk artikel bewaard.', 'purge_now' => 'Schoon nu op', + 'keep_max' => 'Maximaal aantal artikelen om te behouden', + 'keep_period' => 'Maximumleeftijd artikelen om te behouden', 'title' => 'Archivering', 'ttl' => 'Vernieuw niet automatisch meer dan', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Publicatie datum', 'related_tags' => 'Gerelateerde labels', 'sharing' => 'Delen', + 'display_authors' => 'Auteurs', 'top_line' => 'Bovenaan', ), 'language' => 'Taal', @@ -45,6 +54,7 @@ return array( '_' => 'Account verwijderen', 'warn' => 'Uw account en alle gerelateerde gegvens worden verwijderd.', ), + 'email' => 'Email adres', 'password_api' => 'Wachtwoord API<br /><small>(e.g., voor mobiele apps)</small>', 'password_form' => 'Wachtwoord<br /><small>(voor de Web-formulier log in methode)</small>', 'password_format' => 'Ten minste 7 tekens', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Meer informatie', 'print' => 'Afdrukken', 'remove' => 'Deelmethode verwijderen', diff --git a/app/i18n/nl/feedback.php b/app/i18n/nl/feedback.php index 25378360b..97e1a71b8 100644 --- a/app/i18n/nl/feedback.php +++ b/app/i18n/nl/feedback.php @@ -75,7 +75,7 @@ return array( ), 'feed' => array( 'actualized' => '<em>%s</em> vernieuwd', - 'actualizeds' => 'RSS feeds vernieuwd', + 'actualizeds' => 'RSS-feeds vernieuwd', 'added' => 'RSS feed <em>%s</em> toegevoegd', 'already_subscribed' => 'Al geabonneerd op <em>%s</em>', 'deleted' => 'Feed verwijderd', diff --git a/app/i18n/nl/gen.php b/app/i18n/nl/gen.php index bdf2e0abd..fdbb866fc 100644 --- a/app/i18n/nl/gen.php +++ b/app/i18n/nl/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Actualiseren', + 'back' => '← Terug', 'back_to_rss_feeds' => '← Ga terug naar je RSS feeds', 'cancel' => 'Annuleren', 'create' => 'Opslaan', @@ -22,6 +23,7 @@ return array( 'update' => 'Updaten', ), 'auth' => array( + 'accept_tos' => 'Ik accepteer de <a href="%s">gebruiksvoorwaarden</a>.', 'email' => 'Email adres', 'keep_logged_in' => 'Ingelogd blijven voor <small>(%s dagen)</small>', 'login' => 'Log in', @@ -160,25 +162,16 @@ return array( 'nothing_to_load' => 'Er zijn geen artikelen meer', 'previous' => 'Vorige', ), + 'period' => array( + 'days' => 'dagen', + 'hours' => 'uren', + 'months' => 'maanden', + 'weeks' => 'weken', + 'years' => 'jaren', + ), 'share' => array( - 'blogotext' => 'Blogotext', - 'diaspora' => 'Diaspora*', 'email' => 'Email', - 'facebook' => 'Facebook', - 'g+' => 'Google+', - 'gnusocial' => 'GNU social', - 'jdh' => 'Journal du hacker', - 'Known' => 'Known based sites', - 'linkedin' => 'LinkedIn', - 'mastodon' => 'Mastodon', - 'movim' => 'Movim', - 'pinboard' => 'Pinboard', - 'pocket' => 'Pocket', - 'print' => 'Print', - 'shaarli' => 'Shaarli', - 'twitter' => 'Twitter', - 'wallabag' => 'wallabag v1', - 'wallabagv2' => 'wallabag v2', + 'Known' => 'Known-gebaseerde sites', ), 'short' => array( 'attention' => 'Attentie!', diff --git a/app/i18n/nl/index.php b/app/i18n/nl/index.php index d202b812a..22720f927 100644 --- a/app/i18n/nl/index.php +++ b/app/i18n/nl/index.php @@ -7,19 +7,22 @@ return array( 'bugs_reports' => 'Rapporteer fouten', 'credits' => 'Waarderingen', 'credits_content' => 'Sommige ontwerp elementen komen van <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> alhoewel FreshRSS dit raamwerk niet gebruikt. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Pictogrammen</a> komen van het <a href="https://www.gnome.org/">GNOME project</a>. <em>De Open Sans</em> font police is gemaakt door <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS is gebaseerd op <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, een PHP raamwerk. Nederlandse vertaling door Wanabo, <a href="http://www.nieuwskop.be" title="NieuwsKop">NieuwsKop.be</a>. Link naar de Nederlandse vertaling, <a href="https://github.com/Wanabo/FreshRSS-Dutch-translation/tree/master">FreshRSS-Dutch-translation</a>.', - 'freshrss_description' => 'FreshRSS is een RSS feed aggregator om zelf te hosten zoals <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> of <a href="http://leed.idleman.fr/">Leed</a>. Het gebruikt weinig systeembronnen en is makkelijk te administreren terwijl het een krachtig en makkelijk te configureren programma is.', + 'freshrss_description' => 'FreshRSS is een RSS-feed aggregator om zelf te hosten, net als <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> of <a href="https://github.com/LeedRSS/Leed">Leed</a>. Het gebruikt weinig systeembronnen en is makkelijk te beheren terwijl het een krachtig en makkelijk te configureren programma is.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">op Github</a>', - 'license' => 'License', - 'project_website' => 'Project website', + 'license' => 'Licentie', + 'project_website' => 'Projectwebsite', 'title' => 'Over', 'version' => 'Versie', 'website' => 'Website', ), + 'tos' => array( + 'title' => 'Gebruiksvoorwaarden', + ), 'feed' => array( 'add' => 'U kunt wat feeds toevoegen.', 'empty' => 'Er is geen artikel om te laten zien.', - 'rss_of' => 'RSS feed van %s', - 'title' => 'Overzicht RSS feeds', + 'rss_of' => 'RSS-feed van %s', + 'title' => 'Overzicht RSS-feeds', 'title_global' => 'Globale weergave', 'title_fav' => 'Uw favorieten', ), @@ -48,7 +51,7 @@ return array( 'queries' => 'Gebruikers queries', 'read' => 'Laat alleen gelezen zien', 'reader_view' => 'Lees modus', - 'rss_view' => 'RSS feed', + 'rss_view' => 'RSS-feed', 'search_short' => 'Zoeken', 'starred' => 'Laat alleen favorieten zien', 'stats' => 'Statistieken', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index b59515f42..8a3af6064 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -13,34 +13,37 @@ return array( 'category' => array( '_' => 'Categorie', 'add' => 'Voeg categorie toe', + 'archiving' => 'Archiveren', 'empty' => 'Lege categorie', 'information' => 'Informatie', 'new' => 'Nieuwe categorie', + 'position' => 'Weergavepositie', + 'position_help' => 'Om de categorieweergave-sorteervolgorde te controleren', 'title' => 'Titel', ), 'feed' => array( - 'add' => 'Voeg een RSS feed toe', + 'add' => 'Voeg een RSS-feed toe', 'advanced' => 'Geavanceerd', 'archiving' => 'Archiveren', 'auth' => array( 'configuration' => 'Log in', - 'help' => 'Verbinding toestaan toegang te krijgen tot HTTP beveiligde RSS feeds', + 'help' => 'Verbinding toestaan toegang te krijgen tot HTTP beveiligde RSS-feeds', 'http' => 'HTTP Authenticatie', 'password' => 'HTTP wachtwoord', 'username' => 'HTTP gebruikers naam', ), 'clear_cache' => 'Cache altijd leegmaken', - 'css_help' => 'Haalt verstoorde RSS feeds op (attentie, heeft meer tijd nodig!)', - 'css_path' => 'Artikelen CSS pad op originele website', + 'css_help' => 'Haalt onvolledige RSS-feeds op (attentie, heeft meer tijd nodig!)', + 'css_path' => 'CSS-pad van artikelen op originele website', 'description' => 'Omschrijving', 'empty' => 'Deze feed is leeg. Controleer of deze nog actueel is.', 'error' => 'Deze feed heeft problemen. Verifieer a.u.b het doeladres en actualiseer het.', 'filteractions' => array( - '_' => 'Filter actions', //TODO - Translation - 'help' => 'Write one search filter per line.', //TODO - Translation + '_' => 'Filteracties', + '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 <em>%s</em>.', 'mute' => 'demp', 'no_selected' => 'Geen feed geselecteerd.', @@ -64,14 +67,15 @@ return array( 'think_to_add' => 'Voeg wat feeds toe.', 'timeout' => 'Time-out in seconden', 'title' => 'Titel', - 'title_add' => 'Voeg een RSS feed toe', + 'title_add' => 'Voeg een RSS-feed toe', 'ttl' => 'Vernieuw automatisch niet vaker dan', - 'url' => 'Feed URL', + 'url' => 'Feed-url', 'validator' => 'Controleer de geldigheid van de feed', - 'website' => 'Website URL', + 'website' => 'Website-url', ), 'firefox' => array( 'documentation' => 'Volg de stappen die <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">hier</a> beschreven worden om FreshRSS aan de Firefox-nieuwslezerlijst toe te voegen.', + 'obsolete_63' => 'Vanaf versie 63 en nieuwer, heeft Firefox de mogelijkheid om zelf niewslezers toe te voegen verwijderd voor online diensten.', 'title' => 'Firefox-nieuwslezer', ), 'import_export' => array( diff --git a/app/i18n/nl/user.php b/app/i18n/nl/user.php new file mode 100644 index 000000000..f98a6b2fd --- /dev/null +++ b/app/i18n/nl/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'Het emailadres is niet geldig.', + 'required' => 'Het emailadres is vereist.', + ), + 'validation' => array( + 'change_email' => 'Het emailadres kan worden gewijzigd <a href="%s">op de profielpagina</a>.', + 'email_sent_to' => 'Er is een email verzonden naar <strong>%s</strong>. Volg de instructies om het emailadres te valideren.', + 'feedback' => array( + 'email_failed' => 'Er kon geen email worden verzonden vanwege een incorrecte configuratie van de server.', + 'email_sent' => 'Er is een email naar het adres verzonden.', + 'error' => 'Het emailadres kon niet worden gevalideerd.', + 'ok' => 'Het emailadres is gevalideerd.', + 'unneccessary' => 'Het emailadres is al eerder gevalideerd.', + 'wrong_token' => 'Het emailadres kon niet worden gevalideerd vanwege een fout token.', + ), + 'need_to' => 'Het emailadres %1 moet worden gevalideerd voordat het kan worden gebruikt.', + 'resend_email' => 'Email opnieuw sturen', + 'title' => 'Emailadresvalidatie', + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'De gebruiksvoorwaarden moeten worden geaccepteerd om te kunnen registeren.', + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'Je account moet worden gevalideerd', + 'welcome' => 'Welkom %s,', + 'body' => 'Je hebt je net geregistreerd op %s, maar je moet je email nog valideren. Volg daarvoor de link:', + ), + ), +); diff --git a/app/i18n/oc/admin.php b/app/i18n/oc/admin.php index 2f8ede873..1fb8d5c3a 100644 --- a/app/i18n/oc/admin.php +++ b/app/i18n/oc/admin.php @@ -163,6 +163,7 @@ return array( 'help' => 'en segondas', 'number' => 'Durada de téner d’ésser connectat', ), + 'force_email_validation' => 'Forçar la validacion de las adreças electronicas', 'instance-name' => 'Nom de l’instància', 'max-categories' => 'Limita de categoria per utilizaire', 'max-feeds' => 'Limita de fluxes per utilizaire', diff --git a/app/i18n/oc/conf.php b/app/i18n/oc/conf.php index 1596950ea..e123c03c5 100644 --- a/app/i18n/oc/conf.php +++ b/app/i18n/oc/conf.php @@ -2,15 +2,24 @@ return array( 'archiving' => array( - '_' => 'Archivar', + '_' => 'Archius', 'advanced' => 'Avançat', 'delete_after' => 'Levar los articles aprèp', + 'exception' => 'Excepcion de purga', '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' => 'Jamai suprimir los favorits', + 'keep_min_by_feed' => 'Nombre minimum d’articles de servar per flux', + 'keep_labels' => 'Jamai suprimir las etiquetas', + 'keep_unreads' => 'Jamai suprimir los pas legits', + 'maintenance' => 'Entreten', 'optimize' => 'Optimizar la basa de donada', 'optimize_help' => 'De far de temps en temps per redusir la talha de la basa de donadas', + 'policy' => 'Politica de purga', + 'policy_warning' => 'Se cap de politica de purga es pas seleccionada, totes los articles seràn gardats', 'purge_now' => 'Purgar ara', - 'title' => 'Archivar', + 'keep_max' => 'Nombre maximum d’articles de gardar', + 'keep_period' => 'Atge maximum dels articles de gardar', + 'title' => 'Archius', 'ttl' => 'Actualizar pas automaticament mai sovent que', ), 'display' => array( @@ -21,6 +30,7 @@ return array( 'publication_date' => 'Data de publicacion', 'related_tags' => 'Etiquetas ligadas', 'sharing' => 'Partatge', + 'display_authors' => 'Autors', 'top_line' => 'Linha amont', ), 'language' => 'Lenga', @@ -45,6 +55,7 @@ return array( '_' => 'Supression del compte', 'warn' => 'Lo compte e totas las donadas ligadas seràn suprimits.', ), + 'email' => 'Adreça de corrièl', 'password_api' => 'Senhal API<br /><small>(ex. : per las aplicacions mobil)</small>', 'password_form' => 'Senhal API<br /><small>(ex. : per la connexion via formulari)</small>', 'password_format' => 'Almens 7 caractèrs', @@ -133,7 +144,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Corrièl', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Mai d’informacions', 'print' => 'Imprimir', 'remove' => 'Suprimir lo metòde de partatge', diff --git a/app/i18n/oc/gen.php b/app/i18n/oc/gen.php index 7f9793283..a5bd003c2 100644 --- a/app/i18n/oc/gen.php +++ b/app/i18n/oc/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Actualizar', + 'back' => '← Tornar', 'back_to_rss_feeds' => '← Tornar a vòstres fluxes RSS', 'cancel' => 'Anullar', 'create' => 'Crear', @@ -22,6 +23,7 @@ return array( 'update' => 'Actualizar', ), 'auth' => array( + 'accept_tos' => 'Accepti las <a href="%s">condicions d’utilizacion</a>.', 'email' => 'Adreça de corrièl', 'keep_logged_in' => 'Demorar connectat <small>(%s jorns) </small>', 'login' => 'Connexion', @@ -49,7 +51,7 @@ return array( 'Aug' => '\\a\\g\\o\\s\\t', 'aug' => 'agost', 'august' => 'agost', - 'before_yesterday' => 'Abans ièr', + 'before_yesterday' => 'Anterior a ièr', 'Dec' => '\\d\\e\\c\\e\\m\\b\\r\\e', 'dec' => 'dec.', 'december' => 'decembre', @@ -160,15 +162,22 @@ return array( 'nothing_to_load' => 'I a pas mai d’articles', 'previous' => 'Precedent', ), + 'period' => array( + 'days' => 'jorns', + 'hours' => 'oras', + 'months' => 'meses', + 'weeks' => 'setmanas', + 'years' => 'ans', + ), 'share' => array( 'blogotext' => 'Blogotext', 'diaspora' => 'Diaspora*', 'email' => 'Corrièl', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Sites basats sus Known', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/oc/index.php b/app/i18n/oc/index.php index 5cc71c9a9..763d24139 100644 --- a/app/i18n/oc/index.php +++ b/app/i18n/oc/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Senhalament de problèmas', 'credits' => 'Crèdits', 'credits_content' => 'Unes elements de l’estil venon del <a href="http://twitter.github.io/bootstrap/">projècte Bootstrap</a> encara que FreshRSS utilize pas aqueste framework. Las <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">icònas</a> venon del <a href="https://www.gnome.org/">projècte GNOME</a>. La polissa <em>Open Sans</em> utilizada foguèt creada per en <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS es basat sus <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, un framework PHP.', - 'freshrss_description' => 'FreshRSS es un agregador de fluxes RSS per l’auto-albergar tal coma <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="http://projet.idleman.fr/leed/">Leed</a>. Sa tòca es d’èsser leugièr e de bon utilizar de prima abòrd mas tanben d’èsser potent e parametrable.', + 'freshrss_description' => 'FreshRSS es un agregador de fluxes RSS per l’auto-albergar tal coma <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> o <a href="https://github.com/LeedRSS/Leed">Leed</a>. Sa tòca es d’èsser leugièr e de bon utilizar de prima abòrd mas tanben d’èsser potent e parametrable.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>', 'license' => 'Licéncia', 'project_website' => 'Site del projècte', @@ -15,6 +15,9 @@ return array( 'website' => 'Site internet', 'version' => 'Version', ), + 'tos' => array( + 'title' => 'Condicions d’utilizacion', + ), 'feed' => array( 'add' => 'Podètz ajustar de fluxes.', 'empty' => 'I a pas cap de flux de mostrar.', diff --git a/app/i18n/oc/sub.php b/app/i18n/oc/sub.php index eae9dff29..98a7521eb 100644 --- a/app/i18n/oc/sub.php +++ b/app/i18n/oc/sub.php @@ -12,9 +12,12 @@ return array( 'category' => array( '_' => 'Categoria', 'add' => 'Ajustar una categoria', + 'archiving' => 'Archivar', 'empty' => 'Categoria voida', 'information' => 'Informacions', 'new' => 'Nòva categoria', + 'position' => 'Mostrar la posicion', + 'position_help' => 'Per contrarotlar l’òrdre de tria de la categoria', 'title' => 'Títol', ), 'feed' => array( @@ -39,7 +42,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 <em>%s</em>.', 'mute' => 'mut', 'no_selected' => 'Cap de flux pas seleccionat.', @@ -71,6 +74,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Seguissètz las etapas descrichas <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">aquí</a> per ajustar FreshRSS a la lista dels lectors de flux de Firefox.', + 'obsolete_63' => 'A partir de la version 63 e las seguentas, Firefox permet pas mai d’ajustar vòstres pròpris servicis d’abonament.', 'title' => 'Lector de flux de Firefox', ), 'import_export' => array( diff --git a/app/i18n/oc/user.php b/app/i18n/oc/user.php new file mode 100644 index 000000000..655aa052c --- /dev/null +++ b/app/i18n/oc/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'L’adreça electronica es invalida.', + 'required' => 'L’adreça electronica es requesida.', + ), + 'validation' => array( + 'change_email' => 'Podètz cambiar l’adreça electronica <a href="%s">sus la pagina de perfil</a>.', + 'email_sent_to' => 'Vos avèm enviat un corrièl a <strong>%s</strong>, mercés de seguir las consignas per validar l’adreça electronica.', + 'feedback' => array( + 'email_failed' => 'Avèm pas pogut vos enviar un corrièl a causa d’una marrida configuracion del servidor.', + 'email_sent' => 'Avèm enviat un corrièl a vòstra adreça.', + 'error' => 'Fracàs de la validacion de l’adreça electronica.', + 'ok' => 'L’adreça electronica es estada validada.', + 'unneccessary' => 'L’adreça es ja estada validada.', + 'wrong_token' => 'Fracàs de la validacion de l’adreça a causa d’un marrit geton.', + ), + 'need_to' => 'Devèètz validar vòstra adreça electronica abans de poder utilizar %s.', + 'resend_email' => 'Tornar enviar lo corrièl', + 'title' => 'Validacion de l’adreça electronica', + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'Vos cal acceptar las condicions d’utilizacion per poder vos inscriure.', + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'Vos cal validar vòstra adreça electronica', + 'welcome' => 'La benvenguda %s,', + 'body' => 'Venètz de vos marcar sus %s mas vos cal encara validar l’adreça electronica. Per aquò far, seguissètz lo ligam :', + ), + ), +); diff --git a/app/i18n/pt-br/admin.php b/app/i18n/pt-br/admin.php index 82559c67b..cef6694c2 100644 --- a/app/i18n/pt-br/admin.php +++ b/app/i18n/pt-br/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Configuração do sistema', 'auto-update-url' => 'URL do servidor para atualização automática', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Nome da instância', 'max-categories' => 'Limite de categorias por usuário', 'max-feeds' => 'Limite de Feeds por usuário', diff --git a/app/i18n/pt-br/conf.php b/app/i18n/pt-br/conf.php index 8f5eb7746..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Data da publicação', 'related_tags' => 'Tags relacionadas', //TODO - Translation 'sharing' => 'Compartilhar', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Linha superior', ), 'language' => 'Ídioma', @@ -45,6 +54,7 @@ return array( '_' => 'Remover conta', 'warn' => 'Sua conta e todos os dados relacionados serão removidos.', ), + 'email' => 'Endereço de e-mail', 'password_api' => 'Senha da API<br /><small>(p.s., para aplicativos móveis)</small>', 'password_form' => 'Senha<br /><small>(para o método de formulário web)</small>', 'password_format' => 'Ao menos 7 caracteres', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Mais informação', 'print' => 'Imprimir', 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/pt-br/gen.php b/app/i18n/pt-br/gen.php index 46ae53eb4..0e7f367ee 100644 --- a/app/i18n/pt-br/gen.php +++ b/app/i18n/pt-br/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Atualizar', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← Volte para o seu feeds RSS', 'cancel' => 'Cancelar', 'create' => 'Criar', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Endereço de e-mail', 'keep_logged_in' => 'Mantenha logado por <small>(%s days)</small>', 'login' => 'Login', @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/pt-br/index.php b/app/i18n/pt-br/index.php index e5807ed95..fac17e171 100644 --- a/app/i18n/pt-br/index.php +++ b/app/i18n/pt-br/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Reportar Bugs', 'credits' => 'Créditos', 'credits_content' => 'Alguns elementos de design vieram do <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> Embora FreshRRS não utiliza este framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Ícones</a> vieram do <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police foi criada por <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS é baseado no <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, um framework PHP.', - 'freshrss_description' => 'FreshRSS é um RSS feeds aggregator para um host próprio como o <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> ou <a href="http://leed.idleman.fr/">Leed</a>. É leve e fácil de utilizar enquanto é uma ferramenta poderosa e configurável. ', + 'freshrss_description' => 'FreshRSS é um RSS feeds aggregator para um host próprio como o <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> ou <a href="https://github.com/LeedRSS/Leed">Leed</a>. É leve e fácil de utilizar enquanto é uma ferramenta poderosa e configurável. ', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">no Github</a>', 'license' => 'licença', 'project_website' => 'Site do projeto', @@ -15,6 +15,9 @@ return array( 'version' => 'Versão', 'website' => 'Site', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'Você pode adicionar alguns feeds.', 'empty' => 'Não há nenhum artigo para mostrar.', diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php index d4bea33c4..04e0c85ab 100644 --- a/app/i18n/pt-br/sub.php +++ b/app/i18n/pt-br/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Categoria', 'add' => 'Adicionar uma categoria', + 'archiving' => 'Arquivar', 'empty' => 'Categoria vazia', 'information' => 'Informações', 'new' => 'Nova categoria', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Título', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Nenhum feed selecionado.', @@ -70,6 +73,11 @@ return array( 'validator' => 'Verifique a validade do feed', 'website' => 'URL do site', ), + 'firefox' => array( + 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.',// TODO + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation + 'title' => 'Firefox feed reader', //TODO - Translation + ), 'import_export' => array( 'export' => 'Exportar', 'export_opml' => 'Exporta a lista dos feeds (OPML)', diff --git a/app/i18n/pt-br/user.php b/app/i18n/pt-br/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/pt-br/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/ru/admin.php b/app/i18n/ru/admin.php index c9a7d6683..adf091df9 100644 --- a/app/i18n/ru/admin.php +++ b/app/i18n/ru/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Системные настройки', 'auto-update-url' => 'Адрес сервера для автоматического обновления', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Название этого сервера', 'max-categories' => 'Количество категорий на пользователя', 'max-feeds' => 'Количество статей на пользователя', diff --git a/app/i18n/ru/conf.php b/app/i18n/ru/conf.php index 841477964..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' => 'Не обновлять чаще чем', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Date of publication', //TODO - Translation 'related_tags' => 'Related tags', //TODO - Translation 'sharing' => 'Sharing', //TODO - Translation + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Top line', //TODO - Translation ), 'language' => 'Язык', @@ -45,6 +54,7 @@ return array( '_' => 'Account deletion', //TODO - Translation 'warn' => 'Your account and all the related data will be deleted.', //TODO - Translation ), + 'email' => 'Email address', //TODO - Translation 'password_api' => 'Password API<br /><small>(e.g., for mobile apps)</small>', //TODO - Translation 'password_form' => 'Password<br /><small>(for the Web-form login method)</small>', //TODO - Translation 'password_format' => 'At least 7 characters', //TODO - Translation @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', //TODO - Translation 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'More information', //TODO - Translation 'print' => 'Print', //TODO - Translation 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/ru/gen.php b/app/i18n/ru/gen.php index b55c6b667..5200a7005 100644 --- a/app/i18n/ru/gen.php +++ b/app/i18n/ru/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Actualize', //TODO - Translation + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← Go back to your RSS feeds', //TODO - Translation 'cancel' => 'Cancel', //TODO - Translation 'create' => 'Create', //TODO - Translation @@ -22,6 +23,7 @@ return array( 'update' => 'Update', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Email address', //TODO - Translation 'keep_logged_in' => 'Keep me logged in <small>(%s дней)</small>', //TODO - Translation 'login' => 'Login', //TODO - Translation @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/ru/index.php b/app/i18n/ru/index.php index 977777178..b5f022cd2 100644 --- a/app/i18n/ru/index.php +++ b/app/i18n/ru/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Bugs reports', //TODO - Translation 'credits' => 'Credits', //TODO - Translation 'credits_content' => 'Some design elements come from <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> although FreshRSS doesn’t use this framework. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Icons</a> come from <a href="https://www.gnome.org/">GNOME project</a>. <em>Open Sans</em> font police has been created by <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS is based on <a href="https://github.com/marienfressinaud/MINZ">Minz</a>, a PHP framework.', //TODO - Translation - 'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="http://leed.idleman.fr/">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.', //TODO - Translation + 'freshrss_description' => 'FreshRSS is a RSS feeds aggregator to self-host like <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> or <a href="https://github.com/LeedRSS/Leed">Leed</a>. It is light and easy to take in hand while being powerful and configurable tool.', //TODO - Translation 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">on Github</a>', //TODO - Translation 'license' => 'License', //TODO - Translation 'project_website' => 'Project website', //TODO - Translation @@ -15,6 +15,9 @@ return array( 'version' => 'Version', //TODO - Translation 'website' => 'Website', //TODO - Translation ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'You may add some feeds.', //TODO - Translation 'empty' => 'There is no article to show.', //TODO - Translation diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index a2c4e4690..e8cdeb89d 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -13,9 +13,12 @@ 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 + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Title', //TODO - Translation ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em>.', //TODO - Translation 'mute' => 'mute', //TODO - Translation 'no_selected' => 'No feed selected.', //TODO - Translation @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', //TODO - Translation + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox feed reader', //TODO - Translation ), 'import_export' => array( diff --git a/app/i18n/ru/user.php b/app/i18n/ru/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/ru/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/sk/admin.php b/app/i18n/sk/admin.php new file mode 100644 index 000000000..347204f37 --- /dev/null +++ b/app/i18n/sk/admin.php @@ -0,0 +1,199 @@ +<?php + +return array( + 'auth' => array( + 'allow_anonymous' => 'Povoliť čítanie článkov prednastaveného používateľa (%s) bez prihlásenia.', + 'allow_anonymous_refresh' => 'Povoliť obnovenie článkov bez prihlásenia', + 'api_enabled' => 'Povoliť prístup cez <abbr>API</abbr> <small>(vyžadujú mobilné aplikácie)</small>', + 'form' => 'Webový formulár (traditičný, vyžaduje JavaScript)', + 'http' => 'HTTP (pre pokročilých používateľov s HTTPS)', + 'none' => 'Žiadny (nebezpečné)', + 'title' => 'Prihlásenie', + 'title_reset' => 'Reset prihlásenia', + 'token' => 'Token prihlásenia', + 'token_help' => 'Povoliť prístup k výstupu RSS prednastaveného používateľa bez prihlásenia:', + 'type' => 'Spôsob prihlásenia', + 'unsafe_autologin' => 'Povoliť nebezpečné automatické prihlásenie pomocou webového formulára: ', + ), + 'check_install' => array( + 'cache' => array( + 'nok' => 'Overte prístupové práva priečinka <em>./data/cache</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Prístupové práva priečinka pre vyrovnávaciu pamäť sú OK.', + ), + 'categories' => array( + 'nok' => 'Tabuľka kategórií je nesprávne nastavená.', + 'ok' => 'Tabuľka kategórií je OK.', + ), + 'connection' => array( + 'nok' => 'Nepodarilo sa vytvoriť pripojenie k databáze.', + 'ok' => 'Pripojenie k databáze je OK.', + ), + 'ctype' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na kontrolu typu znakov (php-ctype).', + 'ok' => 'Našla sa požadovaná knižnica na kontrolu typu znakov (ctype).', + ), + 'curl' => array( + 'nok' => 'Nepodarilo sa nájsť knižnicu cURL (balík php-curl).', + 'ok' => 'Našla sa knižnica cURL.', + ), + 'data' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku údajov sú OK.', + ), + 'database' => 'Inštalácia databázy', + 'dom' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na prehliadanie DOM.', + 'ok' => 'Našla sa požadovaná knižnica na prehliadanie DOM.', + ), + 'entries' => array( + 'nok' => 'Tabuľka článkov je nesprávne nastavená.', + 'ok' => 'Tabuľka článkov je OK.', + ), + 'favicons' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data/favicons</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku ikôn obľúbených sú OK.', + ), + 'feeds' => array( + 'nok' => 'Tabuľka kanálov je nesprávne nastavená.', + 'ok' => 'Tabuľka kanálov je OK.', + ), + 'fileinfo' => array( + 'nok' => 'Nepodarilo sa nájsť knižniuc PHP fileinfo (balík fileinfo).', + 'ok' => 'Našla sa knižnica fileinfo.', + ), + 'files' => 'Inštalácia súborov', + 'json' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na spracovanie formátu JSON.', + 'ok' => 'Našla sa požadovaná knižnica na spracovanie formátu JSON.', + ), + 'mbstring' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu mbstring pre Unicode.', + 'ok' => 'Našla sa požadovaná knižnica mbstring pre Unicode.', + ), + 'minz' => array( + 'nok' => 'Nepodarilo sa nájsť framework Minz.', + 'ok' => 'Našiel sa framework Minz.', + ), + 'pcre' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu pre regulárne výrazy (php-pcre).', + 'ok' => 'Našla sa požadovaná knižnica pre regulárne výrazy (PCRE).', + ), + 'pdo' => array( + 'nok' => 'Nepodarilo sa nájsť PDO alebo niektorý z podporovaných ovládačov (pdo_mysql, pdo_sqlite, pdo_pgsql).', + 'ok' => 'Našiel sa PDO a aspoň jeden z podporovaných ovládačov (pdo_mysql, pdo_sqlite, pdo_pgsql).', + ), + 'php' => array( + '_' => 'Inštalácia PHP', + 'nok' => 'Vaša verzia PHP je %s, ale FreshRSS vyžaduje minimálne verziu %s.', + 'ok' => 'Vaša verzia PHP %s je kompatibilná s FreshRSS.', + ), + 'tables' => array( + 'nok' => 'V databáze chýba jedna alebo viacero tabuliek.', + 'ok' => 'V databáze sa nachádzajú všetky potrebné tabuľky.', + ), + 'title' => 'Kontrola inštalácie', + 'tokens' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data/tokens</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku tokens sú OK.', + ), + 'users' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data/users</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku používateľov sú OK.', + ), + 'zip' => array( + 'nok' => 'Nepodarilo sa nájsť rozšírenie ZIP (balík php-zip).', + 'ok' => 'Rozšírenie ZIP sa našlo.', + ), + ), + 'extensions' => array( + 'author' => 'Autor', + 'community' => 'Rozšírenia od komunity', + 'description' => 'Popis', + 'disabled' => 'Zakázané', + 'empty_list' => 'Žiadne nainštalované rozšírenia', + 'enabled' => 'Povolené', + 'latest' => 'Nainštalované', + 'name' => 'Názov', + 'no_configure_view' => 'Toto rozšírenie nemá nastavenia.', + 'system' => array( + '_' => 'Systémové rozšírenia', + 'no_rights' => 'Systémové rozšírenie (nemáte oprávnenia)', + ), + 'title' => 'Rozšírenia', + 'update' => 'Sú dostupné aktualizácie', + 'user' => 'Používateľské rozšírenia', + 'version' => 'Verzia', + ), + 'stats' => array( + '_' => 'Štatistiky', + 'all_feeds' => 'Všetky kanály', + 'category' => 'Kategória', + 'entry_count' => 'Počet položiek', + 'entry_per_category' => 'Položiek v kategórii', + 'entry_per_day' => 'Položiek za deň (posledných 30 dní)', + 'entry_per_day_of_week' => 'Za deň v týždni (priemer: %.2f správy)', + 'entry_per_hour' => 'Za hodinu (priemer: %.2f správy)', + 'entry_per_month' => 'Za mesiac (priemer: %.2f správy)', + 'entry_repartition' => 'Rozdelenie článkov', + 'feed' => 'Kanál', + 'feed_per_category' => 'Kanálov v kategórii', + 'idle' => 'Neaktívne kanály', + 'main' => 'Hlavné štatistiky', + 'main_stream' => 'Všetky kanály', + 'menu' => array( + 'idle' => 'Neaktívne kanály', + 'main' => 'Hlavné štatistiky', + 'repartition' => 'Rozdelenie článkov', + ), + 'no_idle' => 'Žiadne neaktívne kanály!', + 'number_entries' => 'Počet článkov: %d', + 'percent_of_total' => 'Z celkového počtu: %%', + 'repartition' => 'Rozdelenie článkov', + 'status_favorites' => 'Obľúbené', + 'status_read' => 'Prečítané', + 'status_total' => 'Spolu', + 'status_unread' => 'Neprečítané', + 'title' => 'Štatistiky', + 'top_feed' => 'Top 10 kanálov', + ), + 'system' => array( + '_' => 'Nastavenia systému', + 'auto-update-url' => 'Odkaz na aktualizačný server', + 'instance-name' => 'Názov inštancie', + 'max-categories' => 'Limit počtu kategórií pre používateľa', + 'max-feeds' => 'Limit počtu kanálov pre používateľov', + 'cookie-duration' => array( + 'help' => 'v sekundách', + 'number' => 'Dobra, počas ktorej ste prihlásený', + ), + 'registration' => array( + 'help' => '0 znamená žiadny limit počtu účtov', + 'number' => 'Maximálny počt účtov', + ), + ), + 'update' => array( + '_' => 'Aktualizácia systému', + 'apply' => 'Použiť', + 'check' => 'Skontrolovať aktualizácie', + 'current_version' => 'Vaša aktuálna verzia FreshRSS: %s', + 'last' => 'Posledná kontrola: %s', + 'none' => 'Žiadna nová aktualizácia', + 'title' => 'Aktualizácia systému', + ), + 'user' => array( + 'articles_and_size' => '%s článkov (%s)', + 'create' => 'Vytvoriť nového používateľa', + 'delete_users' => 'Zmazať používateľa', + 'language' => 'Jazyk', + 'number' => 'Je vytvorený používateľ: %d', + 'numbers' => 'Je vytvorených používateľov: %d', + 'password_form' => 'Heslo<br /><small>(pre spôsob prihlásenia cez webový formulár)</small>', + 'password_format' => 'Minimálne 7 znakov', + 'selected' => 'Označený používateľ', + 'title' => 'Správa používateľov', + 'update_users' => 'Sktualizovať používateľov', + 'user_list' => 'Zoznam používateľov', + 'username' => 'Používateľské meno', + 'users' => 'Používatelia', + ), +); diff --git a/app/i18n/sk/conf.php b/app/i18n/sk/conf.php new file mode 100644 index 000000000..2e2289b79 --- /dev/null +++ b/app/i18n/sk/conf.php @@ -0,0 +1,188 @@ +<?php + +return array( + 'archiving' => array( + '_' => 'Archivovanie', + 'advanced' => 'Pokročilé', + 'delete_after' => 'Vymazať články po', + 'help' => 'Viac možností nájdete v nastaveniach kanála', + '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', + 'title' => 'Archivovanie', + 'ttl' => 'Neaktualizovať častejšie ako', + ), + 'display' => array( + '_' => 'Zobrazenie', + 'icon' => array( + 'bottom_line' => 'Spodný riadok', + 'display_authors' => 'Autori', + 'entry' => 'Ikony článku', + 'publication_date' => 'Dátum zverejnenia', + 'related_tags' => 'Značky článku', + 'sharing' => 'Zdieľanie', + 'top_line' => 'Horný riadok', + ), + 'language' => 'Jazyk', + 'notif_html5' => array( + 'seconds' => 'sekundy (0 znamená bez limitu)', + 'timeout' => 'Limit HTML5 oznámenia', + ), + 'show_nav_buttons' => 'Zobraziť tlačidlá oznámenia', + 'theme' => 'Vzhľad', + 'title' => 'Zobraziť', + 'width' => array( + 'content' => 'Šírka obsahu', + 'large' => 'Veľká', + 'medium' => 'Stredná', + 'no_limit' => 'Bez obmedzenia', + 'thin' => 'Úzka', + ), + ), + 'profile' => array( + '_' => 'Správca profilu', + 'delete' => array( + '_' => 'Vymazanie účtu', + 'warn' => 'Váš účet a všetky údaje v ňom budú vymazané.', + ), + 'password_api' => 'Heslo API<br /><small>(pre mobilné aplikácie)</small>', + 'password_form' => 'Heslo<br /><small>(pre spôsob prihlásenia cez webový formulár)</small>', + 'password_format' => 'Najmenej 7 znakov', + 'title' => 'Profil', + ), + 'query' => array( + '_' => 'Dopyty používateľa', + 'deprecated' => 'Tento dopyt už nie je platný. Kategória alebo kanál boli vymazané.', + 'display' => 'Zobraziť výsledky dopytu používateľa', + 'filter' => 'Použitý filter:', + 'get_all' => 'Zobraziť všetky články', + 'get_category' => 'Zobraziť kategóriu "%s"', + 'get_favorite' => 'Zobraziť obľúbené články', + 'get_feed' => 'Zobraziť kanál "%s"', + 'no_filter' => 'Žiadny filter', + 'none' => 'Zatiaľ ste nevytvorili používateľský dopyt.', + 'number' => 'Dopyt číslo %d', + 'order_asc' => 'Zobraziť staršie články hore', + 'order_desc' => 'Zobraziť novšie články hore', + 'remove' => 'Vymazať dopyt používateľa', + 'search' => 'Vyhľadáva sa: "%s"', + 'state_0' => 'Zobraziť všetky články', + 'state_1' => 'Zobraziť prečítané články', + 'state_2' => 'Zobraziť neprečítané články', + 'state_3' => 'Zobraziť všetky články', + 'state_4' => 'Zobraziť obľúbené články', + 'state_5' => 'Zobraziť prečítané obľúbené články', + 'state_6' => 'Zobraziť neprečítané obľúbené články', + 'state_7' => 'Zobraziť obľúbené články', + 'state_8' => 'Zobraziť neobľúbené články', + 'state_9' => 'Zobraziť prečítané neobľúbené články', + 'state_10' => 'Zobraziť neprečítané neobľúbené články', + 'state_11' => 'Zobraziť neobľúbené články', + 'state_12' => 'Zobraziť všetky články', + 'state_13' => 'Zobraziť prečítané články', + 'state_14' => 'Zobraziť neprečítané články', + 'state_15' => 'Zobraziť všetky články', + 'title' => 'Používateľské dopyty', + ), + 'reading' => array( + '_' => 'Čítanie', + 'after_onread' => 'Po “Označiť všetko ako prečítané”,', + 'articles_per_page' => 'Počet článkov na jednu stranu', + 'auto_load_more' => 'Načítať ďalšie články dolu na stránke', + 'auto_remove_article' => 'Skryť články po prečítaní', + 'confirm_enabled' => 'Zobraziť potvrdzovací dialóg po kliknutí na “Označiť všetko ako prečítané”', + 'display_articles_unfolded' => 'Zobraziť články otvorené', + 'display_categories_unfolded' => 'Zobraziť kategórie otvorené', + 'hide_read_feeds' => 'Skryť kategórie a kanály s nulovým počtom neprečítaných článkov (nefunguje s nastaveným “Zobraziť všetky články”)', + 'img_with_lazyload' => 'Pre načítanie obrázkov použiť "lazy load"', + 'jump_next' => 'skočiť na ďalší neprečítaný (kanál ale kategóriu)', + 'mark_updated_article_unread' => 'Označiť aktualizované články ako neprečítané', + 'number_divided_when_reader' => 'V režime čítania predeliť na dve časti.', + 'read' => array( + 'article_open_on_website' => 'keď je článok otvorený na svojej webovej stránke', + 'article_viewed' => 'keď je článok zobrazený', + 'scroll' => 'počas skrolovania', + 'upon_reception' => 'po načítaní článku', + 'when' => 'Označiť článok ako prečítaný…', + ), + 'show' => array( + '_' => 'Článkov na zobrazenie', + 'adaptive' => 'Vyberte zobrazenie', + 'all_articles' => 'Zobraziť všetky články', + 'unread' => 'Zobraziť iba neprečítané', + ), + 'sides_close_article' => 'Po kliknutí mimo textu článku sa článok zatvorí', + 'sort' => array( + '_' => 'Poradie', + 'newer_first' => 'Novšie hore', + 'older_first' => 'Staršie hore', + ), + 'sticky_post' => 'Po otvorení posunúť článok hore', + 'title' => 'Čítanie', + 'view' => array( + 'default' => 'Prednastavené zobrazenie', + 'global' => 'Prehľadné zobrazenie', + 'normal' => 'Základné zobrazenie', + 'reader' => 'Zobrazenie na čítanie', + ), + ), + 'sharing' => array( + '_' => 'Zdieľanie', + 'add' => 'Pridať spôsob zdieľania', + 'blogotext' => 'Blogotext', + 'diaspora' => 'Diaspora*', + 'email' => 'E-mail', + 'facebook' => 'Facebook', + 'g+' => 'Google+', + 'more_information' => 'Viac informácií', + 'print' => 'Tlač', + 'remove' => 'Odstrániť spôsob zdieľania', + 'shaarli' => 'Shaarli', + 'share_name' => 'Meno pre zobrazenie', + 'share_url' => 'Zdieľaný odkaz', + 'title' => 'Zdieľanie', + 'twitter' => 'Twitter', + 'wallabag' => 'wallabag', + ), + 'shortcut' => array( + '_' => 'Skratky', + 'article_action' => 'Akcie článku', + 'auto_share' => 'Zdieľať', + 'auto_share_help' => 'Ak je nastavený iba jeden spôsob zdieľania, použije sa. Inak si spôsoby zdieľania vyberá používateľ podľa čísla.', + 'close_dropdown' => 'Zavrie menu', + 'collapse_article' => 'Zroluje článok', + 'first_article' => 'Otvorí prvý článok', + 'focus_search' => 'Vyhľadávanie', + 'global_view' => 'Prepne do prehľadného zobrazenia', + 'help' => 'Zobrazí dokumentáciu', + 'javascript' => 'JavaScript musí byť povolený, ak chcete používať skratky', + 'last_article' => 'Otvorí posledný článok', + 'load_more' => 'Načíta viac článkov', + 'mark_favorite' => 'O(d)značí ako obľúbené', + 'mark_read' => 'O(d)značí ako prečítané', + 'navigation' => 'Navigácia', + 'navigation_help' => 'Po stlačení skratky s klávesou "Shift", sa skratky navigácie vzťahujú na kanály.<br/>Po stlačení skratky s klávesou "Alt", sa skratky navigácie vzťahujú na kategórie.', + 'navigation_no_mod_help' => 'Tieto skratky navigácie nepodporujú klávesy "Shift" a "Alt".', + 'next_article' => 'Otvorí ďalší článok', + 'normal_view' => 'Prepne do základného zobrazenia', + 'other_action' => 'Ostatné akcie', + 'previous_article' => 'Otvorí predošlý článok', + 'reading_view' => 'Prepne do zobrazenia na čítanie', + 'rss_view' => 'Otvorí zobrazenie RSS v novej záložke', + 'see_on_website' => 'Zobrazí na webovej stránke', + 'shift_for_all_read' => '+ <code>shift</code> na označenie všetkých článkov ako prečítaných', + 'skip_next_article' => 'Prejde na ďalší bez otvorenia', + 'skip_previous_article' => 'Prejde na predošlý bez otvorenia', + 'title' => 'Skratky', + 'user_filter' => 'Použiť používateľské filtre', + 'user_filter_help' => 'Ak je nastavený iba jeden spôsob zdieľania, použije sa. Inak si spôsoby zdieľania vyberá používateľ podľa čísla.', + 'views' => 'Zobrazenia', + ), + 'user' => array( + 'articles_and_size' => '%s článkov (%s)', + 'current' => 'Aktuálny používateľ', + 'is_admin' => 'je administrátor', + 'users' => 'Používatelia', + ), +); diff --git a/app/i18n/sk/feedback.php b/app/i18n/sk/feedback.php new file mode 100644 index 000000000..9aee79068 --- /dev/null +++ b/app/i18n/sk/feedback.php @@ -0,0 +1,116 @@ +<?php + +return array( + 'admin' => array( + 'optimization_complete' => 'Optimalizácia dokončená', + ), + 'access' => array( + 'denied' => 'Na prístup k tejto stránke nemáte oprávnenie', + 'not_found' => 'Hľadáte stránku, ktorá neexistuje', + ), + 'auth' => array( + 'form' => array( + 'not_set' => 'Nastavl problém pri nastavovaní prihlasovacieho systému. Prosím, skúste to znova neskôr.', + 'set' => 'Webový formulár je teraz váš prednastavený prihlasovací spôsob.', + ), + 'login' => array( + 'invalid' => 'Nesprávne prihlasovacie údaje', + 'success' => 'Úspešne ste sa prihlásili', + ), + 'logout' => array( + 'success' => 'Boli ste odhlásený', + ), + 'no_password_set' => 'Heslo administrátora nebolo nastavené. Táto funkcia nie je dostupná.', + ), + 'conf' => array( + 'error' => 'Vyskytla sa chyba počas ukladania nastavaní', + 'query_created' => 'Dopyt "%s" bol vytvorený.', + 'shortcuts_updated' => 'Skratky boli aktualizované', + 'updated' => 'Nastavenia boli aktualizované', + ), + 'extensions' => array( + 'already_enabled' => '%s už je povolené', + 'disable' => array( + 'ko' => '%s sa nepodarilo nainštalovať. <a href="%s">Prečítajte si záznamy FreshRSS</a>, ak chcete poznať podrobnosti.', + 'ok' => '%s je teraz zakázaný', + ), + 'enable' => array( + 'ko' => '%s sa nepodarilo povoliť. <a href="%s">Prečítajte si záznamy FreshRSS</a>, ak chcete poznať podrobnosti.', + 'ok' => '%s je teraz povolený', + ), + 'no_access' => 'Nemáte prístup k %s', + 'not_enabled' => '%s nie je povolený', + 'not_found' => '%s neexistuje', + ), + 'import_export' => array( + 'export_no_zip_extension' => 'ZIP rozšírenie sa na vašom serveri nenachádza. Prosím, skúste exportovať súbory pojednom.', + 'feeds_imported' => 'Váš kanál bol importovaný a bude aktualizovaný', + 'feeds_imported_with_errors' => 'Vaše kanály boli importované, ale vyskytli sa chyby', + 'file_cannot_be_uploaded' => 'Súbor sa nepodarilo nahrať!', + 'no_zip_extension' => 'ZIP rozšírenie sa na vašom serveri nenachádza.', + 'zip_error' => 'Počas importovania ZIP sa vyskytla chyba.', + ), + 'profile' => array( + 'error' => 'Váš profil nie je možné upraviť', + 'updated' => 'Váš profil bol upravený', + ), + 'sub' => array( + 'actualize' => 'Aktualizácia', + 'articles' => array( + 'marked_read' => 'Vybraté články boli označené ako prečítané.', + 'marked_unread' => 'Články boli označené ako neprečítané.', + ), + 'category' => array( + 'created' => 'Kategória %s bola vytvorená.', + 'deleted' => 'Kategória bola odstránená.', + 'emptied' => 'Kategória bola vyprázdnená', + 'error' => 'Nepodarilo sa aktualizovať kategóriu', + 'name_exists' => 'Názov kategórie už existuje.', + 'no_id' => 'Musíte zadať ID kategórie.', + 'no_name' => 'Názov kategórie nemôže byť prázdny.', + 'not_delete_default' => 'Nemôžete odstrániť prednastavenú kategóriu!', + 'not_exist' => 'Kategória neexistuje!', + 'over_max' => 'Dosiahli ste limit počtu kategórií (%d)', + 'updated' => 'Kategória bola aktualizovaná.', + ), + 'feed' => array( + 'actualized' => '<em>%s</em> bol aktualizovaný', + 'actualizeds' => 'RSS kanál bol aktualizovaný', + 'added' => 'RSS kanál <em>%s</em> bol pridaný', + 'already_subscribed' => 'Tento RSS kanál už odoberáte: <em>%s</em>', + 'deleted' => 'Kanál bol vymazaný', + 'error' => 'Kanál sa nepodarilo aktualizovať', + 'internal_problem' => 'Kanál sa nepodarilo pridať. <a href="%s">Prečítajte si záznamy FreshRSS</a>, ak chcete poznať podrobnosti. Skúste pridať kanál pomocou <code>#force_feed</code> v odkaze (URL).', + 'invalid_url' => 'Odkaz <em>%s</em> je neplatný', + 'n_actualized' => 'Počet aktualizovaných kanálov: %d', + 'n_entries_deleted' => 'Počet vymazaných článkov: %d', + 'no_refresh' => 'Žiadny kanál sa neaktualizoval…', + 'not_added' => 'Kanál <em>%s</em> sa nepodarilo pridať', + 'over_max' => 'Dosiahli ste limit počtu kanálov (%d)', + 'updated' => 'Kanál bol aktualizovaný', + ), + 'purge_completed' => 'Čistenie ukončené. Počet vymazaných článkov: %d', + ), + 'update' => array( + 'can_apply' => 'FreshRSS sa teraz aktualizuje <strong>na verziu %s</strong>.', + 'error' => 'Počas aktualizácie sa vyskytla chyba: %s', + 'file_is_nok' => 'Je dostupná nová <strong>verzia %s</strong>, ale skontrolujte prístupové práva priečinka <em>%s</em>. HTTP server musí mať právo doň zapisovať.', + 'finished' => 'Aktualizácia prebehla úspešne!', + 'none' => 'Žiadne aktualizácie', + 'server_not_found' => 'Nepodarilo sa nájsť server s aktualizáciami. [%s]', + ), + 'user' => array( + 'created' => array( + '_' => 'Používateľ %s bol vytvorený', + 'error' => 'Používateľ %s nebol vytvorený', + ), + 'deleted' => array( + '_' => 'Používateľ %s bol vymazaný', + 'error' => 'Používateľ %s nebol vymazaný', + ), + 'updated' => array( + '_' => 'Používateľ %s bol aktualizovaný', + 'error' => 'Používateľ %s nebol aktualizovaný', + ), + ), +); diff --git a/app/i18n/sk/gen.php b/app/i18n/sk/gen.php new file mode 100644 index 000000000..7303ffa9f --- /dev/null +++ b/app/i18n/sk/gen.php @@ -0,0 +1,181 @@ +<?php + +return array( + 'action' => array( + 'actualize' => 'Aktualizovať', + 'back_to_rss_feeds' => '← Späť na vaše RSS kanály', + 'cancel' => 'Zrušiť', + 'create' => 'Vytvoriť', + 'disable' => 'Zakázať', + 'empty' => 'Vyprázdniť', + 'enable' => 'Povoliť', + 'export' => 'Exportovať', + 'filter' => 'Filtrovať', + 'import' => 'Importovať', + 'manage' => 'Spravovať', + 'mark_favorite' => 'Označiť ako obľúbené', + 'mark_read' => 'Označiť ako prečítané', + 'remove' => 'Odstrániť', + 'see_website' => 'Zobraziť webovú stránku', + 'submit' => 'Poslať', + 'truncate' => 'Vymazať všetky články', + 'update' => 'Aktualizovať', + ), + 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation + 'email' => 'E-mailová adresa', + 'keep_logged_in' => 'Zostať prihlásený <small>(počet dní: %s)</small>', + 'login' => 'Prihlásiť', + 'logout' => 'Odhlásiť', + 'password' => array( + '_' => 'Heslo', + 'format' => '<small>Najmenej 7 znakov</small>', + ), + 'registration' => array( + '_' => 'Nový účet', + 'ask' => 'Vytvoriť účet?', + 'title' => 'Vytvorenie účtu', + ), + 'reset' => 'Reset prihlásenia', + 'username' => array( + '_' => 'Používateľské meno', + 'admin' => 'Administrátorské používateľské meno', + 'format' => '<small>maximálne 16 alfanumerických znakov</small>', + ), + ), + 'date' => array( + 'Apr' => '\\A\\p\\r\\í\\l', + 'apr' => 'Apr.', + 'april' => 'Apríl', + 'Aug' => '\\A\\u\\g\\u\\s\\t', + 'aug' => 'Aug.', + 'august' => 'August', + 'before_yesterday' => 'Predvčerom', + 'Dec' => '\\D\\e\\c\\e\\m\\b\\e\\r', + 'dec' => 'Dec.', + 'december' => 'December', + 'Feb' => '\\F\\e\\b\\r\\u\\á\\r', + 'feb' => 'Feb.', + 'february' => 'Február', + 'format_date' => '%s j\\<\\s\\u\\p\\>S\\<\\/\\s\\u\\p\\> Y', + 'format_date_hour' => '%s j\\<\\s\\u\\p\\>S\\<\\/\\s\\u\\p\\> Y \\a\\t H\\:i', + 'fri' => 'Pi', + 'Jan' => '\\J\\a\\n\\u\\á\\r', + 'jan' => 'Jan.', + 'january' => 'Január', + 'Jul' => '\\J\\ú\\l', + 'jul' => 'Júl', + 'july' => 'Júl', + 'Jun' => '\\J\\ú\\n', + 'jun' => 'Jún', + 'june' => 'Jún', + 'last_3_month' => 'Posledné 3 mesiace', + 'last_6_month' => 'Posledných 6 mesiacov', + 'last_month' => 'Posledný mesiac', + 'last_week' => 'Posledný týždeň', + 'last_year' => 'Posledný rok', + 'Mar' => '\\M\\a\\r\\e\\c', + 'mar' => 'Mar.', + 'march' => 'Marec', + 'May' => '\\M\\á\\j', + 'may' => 'Máj', + 'may_' => 'Máj', + 'mon' => 'Po', + 'month' => 'mesiace', + 'Nov' => '\\N\\o\\v\\e\\m\\b\\e\\r', + 'nov' => 'Nov.', + 'november' => 'November', + 'Oct' => '\\O\\k\\t\\ó\\b\\e\\r', + 'oct' => 'Okt.', + 'october' => 'Október', + 'Sep' => '\\S\\e\\p\\t\\e\\m\\b\\e\\r', + 'sat' => 'So', + 'sep' => 'Sept.', + 'september' => 'September', + 'sun' => 'Ne', + 'thu' => 'Št', + 'today' => 'Dnes', + 'tue' => 'Ut', + 'wed' => 'St', + 'yesterday' => 'Včera', + ), + 'freshrss' => array( + '_' => 'FreshRSS', + 'about' => 'O FreshRSS', + ), + 'js' => array( + 'category_empty' => 'Prázdna kategória', + 'confirm_action' => 'Určite chcete vykonať túto akciu? Zmeny budú nezvratné!', + 'confirm_action_feed_cat' => 'Určite chcete vykonať túto akciu? Prídete o súvisiace obľúbené a používateľské dopyty. Zmeny budú nezvratné!', + 'feedback' => array( + 'body_new_articles' => 'Počet nových článkov v čítačke FreshRSS: %%d', + 'request_failed' => 'Nepodarilo sa spracovať váš dopyt, pravdepodobne kvôli problému s pripojením do internetu.', + 'title_new_articles' => 'FreshRSS: nové články!', + ), + 'new_article' => 'Našli sa nové články. Kliknite na obnovenie stránky.', + 'should_be_activated' => 'Musíte povoliť JavaScript', + ), + 'menu' => array( + 'about' => 'O FreshRSS', + 'admin' => 'Administrácia', + 'archiving' => 'Archivácia', + 'authentication' => 'Prihlásenie', + 'check_install' => 'Kontroloa inštalácie', + 'configuration' => 'Nastavenia', + 'display' => 'Zobrazenie', + 'extensions' => 'Rozšírenia', + 'logs' => 'Záznamy', + 'queries' => 'Používateľské dopyty', + 'reading' => 'Čítanie', + 'search' => 'Hľadajte slová alebo #značky', + 'sharing' => 'Zdieľanie', + 'shortcuts' => 'Skratky', + 'stats' => 'Štatistiky', + 'system' => 'Nastavenie systému', + 'update' => 'Aktualizácia', + 'user_management' => 'Spravovať používateľov', + 'user_profile' => 'Profil', + ), + 'pagination' => array( + 'first' => 'Prvý', + 'last' => 'Posledný', + 'load_more' => 'Načítať viac článkov', + 'mark_all_read' => 'Označiť všetko prečítané', + 'next' => 'Ďalší', + 'nothing_to_load' => 'Žiadne nové články', + 'previous' => 'Predošlý', + ), + 'share' => array( + 'blogotext' => 'Blogotext', + 'diaspora' => 'Diaspora*', + 'email' => 'E-mail', + 'facebook' => 'Facebook', + 'g+' => 'Google+', + 'gnusocial' => 'GNU social', + 'jdh' => 'Journal du hacker', + 'Known' => 'Stránky založené na Known', + 'linkedin' => 'LinkedIn', + 'mastodon' => 'Mastodon', + 'movim' => 'Movim', + 'pinboard' => 'Pinboard', + 'pocket' => 'Pocket', + 'print' => 'Print', + 'shaarli' => 'Shaarli', + 'twitter' => 'Twitter', + 'wallabag' => 'wallabag v1', + 'wallabagv2' => 'wallabag v2', + ), + 'short' => array( + 'attention' => 'Upozornenie!', + 'blank_to_disable' => 'Ak chcete zakázať, ponechajte prázdne', + 'by_author' => 'Od:', + 'by_default' => 'Prednastavené', + 'damn' => 'Sakra!', + 'default_category' => 'Bez kategórie', + 'no' => 'Nie', + 'not_applicable' => 'Nie je k dispozícii', + 'ok' => 'OK', + 'or' => 'alebo', + 'yes' => 'Áno', + ), +); diff --git a/app/i18n/sk/index.php b/app/i18n/sk/index.php new file mode 100644 index 000000000..ae5a077b0 --- /dev/null +++ b/app/i18n/sk/index.php @@ -0,0 +1,66 @@ +<?php + +return array( + 'about' => array( + '_' => 'O FreshRSS', + 'agpl3' => '<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPL 3</a>', + 'bugs_reports' => 'Nahlásiť chybu', + 'credits' => 'Poďakovanie', + 'credits_content' => 'Niektoré časti vzhľadu pochádzajú z <a href="http://twitter.github.io/bootstrap/">Bootstrap</a>u, aj keď FreshRSS tento framework nepoužíva. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">Ikony</a> sú z <a href="https://www.gnome.org/">GNOME project</a>. Font <em>Open Sans</em> zabezpečil <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a>. FreshRSS je založený na PHP frameworku <a href="https://github.com/marienfressinaud/MINZ">Minz</a>.', + 'freshrss_description' => 'FreshRSS je čítačka RSS kanálov, ktorú môžete nasadiť na vlastný server podobne ako <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> alebo <a href="https://github.com/LeedRSS/Leed">Leed</a>. Ide o jednoduchý a zároveň dobre nastaviteľný nástroj.', + 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">na Github</a>e', + 'license' => 'Licencia', + 'project_website' => 'Webová stránka projektu', + 'title' => 'O FreshRSS', + 'version' => 'Verzia', + 'website' => 'Webová stránka', + ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), + 'feed' => array( + 'add' => 'Môžete pridať kanály.', + 'empty' => 'Žiadne články.', + 'rss_of' => 'RSS kanál pre %s', + 'title' => 'Vaše RSS kanály', + 'title_global' => 'Prehľad', + 'title_fav' => 'Vaše obľúbené', + ), + 'log' => array( + '_' => 'Záznamy', + 'clear' => 'Vymazať záznamy', + 'empty' => 'Súbor záznamu je prázdny', + 'title' => 'Záznamy', + ), + 'menu' => array( + 'about' => 'O FreshRSS', + 'add_query' => 'Vytvoriť dopyt', + 'before_one_day' => 'Pred 1 dňom', + 'before_one_week' => 'Pred 1 týždňom', + 'favorites' => 'Obľúbené (%s)', + 'global_view' => 'Prehľad', + 'main_stream' => 'Všetky kanály', + 'mark_all_read' => 'Označiť všetko ako prečítané', + 'mark_cat_read' => 'Označiť kategóriu ako prečítanú', + 'mark_feed_read' => 'Označiť kanál ako prečítaný', + 'mark_selection_unread' => 'Označiť označené ako prečítané', + 'newer_first' => 'Novšie hore', + 'non-starred' => 'Zobraziť všetko okrem obľúbených', + 'normal_view' => 'Základné zobrazenie', + 'older_first' => 'Staršie hore', + 'queries' => 'Používateľské dopyty', + 'read' => 'Zobraziť prečítané', + 'reader_view' => 'Zobrazenie na čítanie', + 'rss_view' => 'RSS kanál', + 'search_short' => 'Hľadať', + 'starred' => 'Zobraziť obľúbené', + 'stats' => 'Štatistiky', + 'subscription' => 'Správca odberov', + 'tags' => 'Moje nálepky', + 'unread' => 'Zobraziť neprečítané', + ), + 'share' => 'Zdieľať', + 'tag' => array( + 'related' => 'Značky článku', + ), +); diff --git a/app/i18n/sk/install.php b/app/i18n/sk/install.php new file mode 100644 index 000000000..08fbfeef9 --- /dev/null +++ b/app/i18n/sk/install.php @@ -0,0 +1,123 @@ +<?php + +return array( + 'action' => array( + 'finish' => 'Dokončiť inštaláciu', + 'fix_errors_before' => 'Prosím, pred pokračovaním opravte chyby.', + 'keep_install' => 'Použiť predošlé nastavenia', + 'next_step' => 'Ďalší krok', + 'reinstall' => 'Preinštalovať FreshRSS', + ), + 'auth' => array( + 'form' => 'Webový formulár (tradičný, vyžaduje JavaScript)', + 'http' => 'HTTP (pre pokročilých používateľov s HTTPS)', + 'none' => 'Žiadny (nebezpečné)', + 'password_form' => 'Heslo<br /><small>(pre prihlásenie cez webový formulár)</small>', + 'password_format' => 'Najmenej 7 znakov', + 'type' => 'Spôsob prihlásenia', + ), + 'bdd' => array( + '_' => 'Databáza', + 'conf' => array( + '_' => 'Nastavenia databázy', + 'ko' => 'Skontrolovať vaše informácie o databáze.', + 'ok' => 'Nastavenia databázy boli uložené.', + ), + 'host' => 'Server', + 'password' => 'Heslo databázy', + 'prefix' => 'Predpona názvu tabuľky', + 'type' => 'Druh databázy', + 'username' => 'Používateľské meno databázy', + ), + 'check' => array( + '_' => 'Kontrola', + 'already_installed' => 'Zistilo sa, že FreshRSS je už nainštalovaný!', + 'cache' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data/cache</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku vyrovnávacej pamäte sú OK.', + ), + 'ctype' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na kontrolu typu znakov (php-ctype).', + 'ok' => 'Našla sa požadovaná knižnica na kontrolu typu znakov (ctype).', + ), + 'curl' => array( + 'nok' => 'Nepodarilo sa nájsť knižnicu cURL (balík php-curl).', + 'ok' => 'Našla sa knižnica cURL.', + ), + 'data' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku údajov sú OK.', + ), + 'dom' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na prehliadanie DOM.', + 'ok' => 'Našla sa požadovaná knižnica na prehliadanie DOM.', + ), + 'favicons' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data/favicons</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku ikôn obľúbených sú OK.', + ), + 'fileinfo' => array( + 'nok' => 'Nepodarilo sa nájsť knižniuc PHP fileinfo (balík fileinfo).', + 'ok' => 'Našla sa knižnica fileinfo.', + ), + 'http_referer' => array( + 'nok' => 'Prosím, skontrolujte, či ste nezmenili váš HTTP REFERER.', + 'ok' => 'Váš HTTP REFERER je OK.', + ), + 'json' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na spracovanie formátu JSON.', + 'ok' => 'Našla sa požadovaná knižnica na spracovanie formátu JSON.', + ), + 'mbstring' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu mbstring pre Unicode.', + 'ok' => 'Našla sa požadovaná knižnica mbstring pre Unicode.', + ), + 'minz' => array( + 'nok' => 'Nepodarilo sa nájsť framework Minz.', + 'ok' => 'Našiel sa framework Minz.', + ), + 'pcre' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu pre regulárne výrazy (php-pcre).', + 'ok' => 'Našla sa požadovaná knižnica pre regulárne výrazy (PCRE).', + ), + 'pdo' => array( + 'nok' => 'Nepodarilo sa nájsť PDO alebo niektorý z podporovaných ovládačov (pdo_mysql, pdo_sqlite, pdo_pgsql).', + 'ok' => 'Našiel sa PDO a aspoň jeden z podporovaných ovládačov (pdo_mysql, pdo_sqlite, pdo_pgsql).', + ), + 'php' => array( + 'nok' => 'Vaša verzia PHP je %s, ale FreshRSS vyžaduje minimálne verziu %s.', + 'ok' => 'Vaša verzia PHP %s je kompatibilná s FreshRSS.', + ), + 'users' => array( + 'nok' => 'Skontrolujte oprávnenia prístupu do priečinku <em>./data/users</em>. HTTP server musí mať právo doň zapisovať.', + 'ok' => 'Oprávnenia prístupu do priečinku používateľov sú OK.', + ), + 'xml' => array( + 'nok' => 'Nepodarilo sa nájsť požadovanú knižnicu na spracovanie formátu XML.', + 'ok' => 'Našla sa požadovaná knižnica na spracovanie formátu XML.', + ), + ), + 'conf' => array( + '_' => 'Hlavné nastavenia', + 'ok' => 'Hlavné nastavenia boli uložené.', + ), + 'congratulations' => 'Nastavenia!', + 'default_user' => 'Hlavné používateľské meno <small>(najviac 16 alfanumerických znakov)</small>', + 'delete_articles_after' => 'Vymazať články po', + 'fix_errors_before' => 'Prosím, pred pokračovaním opravte chyby.', + 'javascript_is_better' => 'FreshRSS si užijete viac, keď povolíte JavaScript', + 'js' => array( + 'confirm_reinstall' => 'Ak budete pokračovať v preinštalovaní FreshRSS, stratíte vaše predošlé nastavenia. Naozaj chcete pokračovať?', + ), + 'language' => array( + '_' => 'Jazyk', + 'choose' => 'Vyberte jazyk pre FreshRSS', + 'defined' => 'Jazyk bol nastavený.', + ), + 'not_deleted' => 'Niečo sa nepodarilo. Musíte ručne zmazať súbor <em>%s</em>.', + 'ok' => 'Inštalácia bola úspešná.', + 'step' => 'krok %d', + 'steps' => 'Kroky', + 'title' => 'Inštalácia · FreshRSS', + 'this_is_the_end' => 'Toto je koniec', +); diff --git a/app/i18n/sk/sub.php b/app/i18n/sk/sub.php new file mode 100644 index 000000000..3149c370b --- /dev/null +++ b/app/i18n/sk/sub.php @@ -0,0 +1,103 @@ +<?php + +return array( + 'api' => array( + 'documentation' => 'Skopírujte tento odkaz a použite ho v inom programe.', + 'title' => 'API', + ), + 'bookmarklet' => array( + 'documentation' => 'Presunte toto tlačidlo do vašich záložiek, alebo kliknite pravým a zvoľte "Uložiť odkaz do záložiek". Potom kliknite na tlačidlo "Odoberať" na ktorejkoľvek stránke, ktorú chcete odoberať.', + 'label' => 'Odoberať', + 'title' => 'Záložka', + ), + 'category' => array( + '_' => 'Kategória', + 'add' => 'Pridať kategóriu', + 'empty' => 'Prázdna kategória', + 'information' => 'Informácia', + 'new' => 'Nová kategória', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation + 'title' => 'Názov', + ), + 'feed' => array( + 'add' => 'Pridať RSS kanál', + 'advanced' => 'Pokročilé', + 'archiving' => 'Archivovanie', + 'auth' => array( + 'configuration' => 'Prihlásenie', + 'help' => 'Povoliť prístup do kanálov chránených cez HTTP.', + 'http' => 'Prihlásenie cez HTTP', + 'password' => 'Heslo pre HTTP', + 'username' => 'Používateľské meno pre HTTP', + ), + 'clear_cache' => 'Vždy vymazať vyrovnávaciu pamäť', + 'css_help' => 'Stiahnuť skrátenú verziu RSS kanála (pozor, vyžaduje viac času!)', + 'css_path' => 'Pôvodný CSS súbor článku z webovej stránky', + 'description' => 'Popis', + 'empty' => 'Tento kanál je prázdny. Overte, prosím, či je ešte spravovaný autorom.', + 'error' => 'Vyskytol sa problém s týmto kanálom. Overte, prosím, či kanál stále existuje, potom ho obnovte.', + 'filteractions' => array( + '_' => 'Filtrovať akcie', + 'help' => 'Napíšte jeden výraz hľadania na riadok.', + ), + 'information' => 'Informácia', + '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 <em>%s</em>.', + 'mute' => 'stíšiť', + 'no_selected' => 'Nevybrali ste kanál.', + 'number_entries' => 'Počet článkov: %d', + 'priority' => array( + '_' => 'Viditeľnosť', + 'archived' => 'Nezobrazovať (archivované)', + 'main_stream' => 'Zobraziť v prehľade kanálov', + 'normal' => 'Zobraziť vo svojej kategórii', + ), + 'websub' => 'Okamžité oznámenia cez WebSub', + 'show' => array( + 'all' => 'Zobraziť všetky kanály', + 'error' => 'Zobraziť iba kanály s chybou', + ), + 'showing' => array( + 'error' => 'Zobraziť iba kanály s chybou', + ), + 'ssl_verify' => 'Overiť bezpečnosť SSL', + 'stats' => 'Štatistiky', + 'think_to_add' => 'Mali by ste pridať kanály.', + 'timeout' => 'Doba platnosti dá v sekundách', + 'title' => 'Nadpis', + 'title_add' => 'Pridať kanál RSS', + 'ttl' => 'Automaticky neaktualizovať častejšie ako', + 'url' => 'Odkaz kanála', + 'validator' => 'Skontrolovať platnosť kanála', + 'website' => 'Odkaz webovej stránky', + ), + 'firefox' => array( + 'documentation' => 'Pridajte RSS kanály do Firefoxu <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">pomocou tohto návodu</a>.', + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation + 'title' => 'RSS čítačka vo Firefoxe', + ), + 'import_export' => array( + 'export' => 'Exportovať', + 'export_opml' => 'Exportovať zoznam kanálov (OPML)', + 'export_starred' => 'Exportovať vaše obľúbené', + 'export_labelled' => 'Exportovať vaše označené články', + 'feed_list' => 'Zoznam článkov %s', + 'file_to_import' => 'Súbor na import<br />(OPML, JSON alebo ZIP)', + 'file_to_import_no_zip' => 'Súbor na import<br />(OPML alebo JSON)', + 'import' => 'Importovať', + 'starred_list' => 'Zoznam obľúbených článkov', + 'title' => 'Import / export', + ), + 'menu' => array( + 'bookmark' => 'Odoberať (záložka FreshRSS)', + 'import_export' => 'Import / export', + 'subscription_management' => 'Správa odoberaných kanálov', + 'subscription_tools' => 'Nástroje na odoberanie kanálov', + ), + 'title' => array( + '_' => 'Správa odoberaných kanálov', + 'feed_management' => 'Správa RSS kanálov', + 'subscription_tools' => 'Nástroje na odoberanie kanálov', + ), +); diff --git a/app/i18n/sk/user.php b/app/i18n/sk/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/sk/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/tr/admin.php b/app/i18n/tr/admin.php index b1d6671ca..2c7d0fd6d 100644 --- a/app/i18n/tr/admin.php +++ b/app/i18n/tr/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => 'Sistem yapılandırması', 'auto-update-url' => 'Otomatik güncelleme sunucu URL', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => 'Örnek isim', 'max-categories' => 'Kullanıcı başına kategori limiti', 'max-feeds' => 'Kullanıcı başına akış limiti', diff --git a/app/i18n/tr/conf.php b/app/i18n/tr/conf.php index 6c57d39da..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', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => 'Yayınlama Tarihi', 'related_tags' => 'İlgili etiketler', //TODO - Translation 'sharing' => 'Paylaşım', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => 'Üst çizgi', ), 'language' => 'Dil', @@ -45,6 +54,7 @@ return array( '_' => 'Hesap silme', 'warn' => 'Hesabınız ve tüm verileriniz silinecek.', ), + 'email' => 'Email adresleri', 'password_api' => 'API Şifresi<br /><small>(ör. mobil uygulamalar için)</small>', 'password_form' => 'Şifre<br /><small>(Tarayıcı girişi için)</small>', 'password_format' => 'En az 7 karakter', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => 'Daha fazla bilgi', 'print' => 'Yazdır', 'remove' => 'Remove sharing method', //TODO - Translation diff --git a/app/i18n/tr/gen.php b/app/i18n/tr/gen.php index a84c39f20..ccc5b9ee6 100644 --- a/app/i18n/tr/gen.php +++ b/app/i18n/tr/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => 'Yenile', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← RSS akışlarınız için geri gidin', 'cancel' => 'İptal', 'create' => 'Oluştur', @@ -22,6 +23,7 @@ return array( 'update' => 'Update', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Email adresleri', 'keep_logged_in' => '<small>(%s günler)</small> oturumu açık tut', 'login' => 'Giriş', @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => 'Known based sites', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/tr/index.php b/app/i18n/tr/index.php index d6db514dd..e284d78db 100644 --- a/app/i18n/tr/index.php +++ b/app/i18n/tr/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Hata raporu', 'credits' => 'Tanıtım', 'credits_content' => 'Bu frameworkü kullanmamasına rağmen FreshRSS bazı tasarım ögelerini <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> dan almıştır. <a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">İkonlar</a> <a href="https://www.gnome.org/">GNOME projesinden</a> alınmıştır. <em>Open Sans</em> yazı tipi <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a> tarafından oluşturulmuştur. FreshRSS bir PHP framework olan <a href="https://github.com/marienfressinaud/MINZ">Minz</a> i temel alır.', - 'freshrss_description' => 'FreshRSS <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> veya <a href="http://leed.idleman.fr/">Leed</a> gibi kendi hostunuzda çalışan bir RSS akış toplayıcısıdır. Güçlü ve yapılandırılabilir araçlarıyla basit ve kullanımı kolay bir uygulamadır.', + 'freshrss_description' => 'FreshRSS <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> veya <a href="https://github.com/LeedRSS/Leed">Leed</a> gibi kendi hostunuzda çalışan bir RSS akış toplayıcısıdır. Güçlü ve yapılandırılabilir araçlarıyla basit ve kullanımı kolay bir uygulamadır.', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">Github sayfası</a>', 'license' => 'Lisans', 'project_website' => 'Proje sayfası', @@ -15,6 +15,9 @@ return array( 'version' => 'Versiyon', 'website' => 'Website', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => 'Akış ekleyebilirsin.', 'empty' => 'Gösterilecek makale yok.', diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index 858d15758..ed2a7ce76 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => 'Kategori', 'add' => 'Kategori ekle', + 'archiving' => 'Arşiv', 'empty' => 'Boş kategori', 'information' => 'Bilgi', 'new' => 'Yeni kategori', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => 'Başlık', ), 'feed' => array( @@ -40,7 +43,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 <em>%s</em> içerisine yerleşir.', 'mute' => 'mute', //TODO - Translation 'no_selected' => 'Hiçbir akış seçilmedi.', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => 'Follow the steps described <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">here</a> to add FreshRSS to Firefox feed reader list.', //TODO - Translation + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox feed reader', //TODO - Translation ), 'import_export' => array( diff --git a/app/i18n/tr/user.php b/app/i18n/tr/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/tr/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/i18n/zh-cn/admin.php b/app/i18n/zh-cn/admin.php index 74f57b6e8..cdc8449a3 100644 --- a/app/i18n/zh-cn/admin.php +++ b/app/i18n/zh-cn/admin.php @@ -159,6 +159,7 @@ return array( 'system' => array( '_' => '系统配置', 'auto-update-url' => '自动升级服务器 URL', + 'force_email_validation' => 'Force email addresses validation', //TODO - Translation 'instance-name' => '实例名称', 'max-categories' => '每用户分类限制', 'max-feeds' => '每用户 RSS 源限制', diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php index 216e4590a..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' => '最小自动更新时间', ), @@ -21,6 +29,7 @@ return array( 'publication_date' => '更新日期', 'related_tags' => '相关标签', 'sharing' => '分享', + 'display_authors' => 'Authors', //TODO - Translation 'top_line' => '顶栏', ), 'language' => '语言', @@ -45,6 +54,7 @@ return array( '_' => '账户删除', 'warn' => '你的帐户和所有相关数据都将被删除。', ), + 'email' => 'Email 地址', 'password_api' => 'API 密码<br /><small>(例如,用于手机 APP)</small>', 'password_form' => '密码<br /><small>(用于 Web-form 登录方式)</small>', 'password_format' => '至少 7 个字符', @@ -133,7 +143,6 @@ return array( 'diaspora' => 'Diaspora*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'more_information' => '更多信息', 'print' => '打印', 'remove' => '删除分享方式', diff --git a/app/i18n/zh-cn/gen.php b/app/i18n/zh-cn/gen.php index 11d4efdb3..31817260e 100644 --- a/app/i18n/zh-cn/gen.php +++ b/app/i18n/zh-cn/gen.php @@ -3,6 +3,7 @@ return array( 'action' => array( 'actualize' => '获取', + 'back' => '← Go back', //TODO - Translation 'back_to_rss_feeds' => '← 返回', 'cancel' => '取消', 'create' => '创建', @@ -22,6 +23,7 @@ return array( 'update' => '更新', //TODO - Translation ), 'auth' => array( + 'accept_tos' => 'I accept the <a href="%s">Terms of Service</a>.', // TODO - Translation 'email' => 'Email 地址', 'keep_logged_in' => '自动登录<small>(%s 天)</small>', 'login' => '登录', @@ -160,15 +162,22 @@ 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*', 'email' => 'Email', 'facebook' => 'Facebook', - 'g+' => 'Google+', 'gnusocial' => 'GNU social', 'jdh' => 'Journal du hacker', 'Known' => '基于 Known 的站点', + 'lemmy' => 'Lemmy', 'linkedin' => 'LinkedIn', 'mastodon' => 'Mastodon', 'movim' => 'Movim', diff --git a/app/i18n/zh-cn/index.php b/app/i18n/zh-cn/index.php index 018813c3e..3e448608e 100644 --- a/app/i18n/zh-cn/index.php +++ b/app/i18n/zh-cn/index.php @@ -7,7 +7,7 @@ return array( 'bugs_reports' => 'Bug 报告', 'credits' => '致谢', 'credits_content' => '某些设计元素来自于 <a href="http://twitter.github.io/bootstrap/">Bootstrap</a> ,尽管 FreshRSS 并没有使用此框架。<a href="https://git.gnome.org/browse/gnome-icon-theme-symbolic">图标</a> 来自于 <a href="https://www.gnome.org/">GNOME 项目</a>。<em>Open Sans</em> 字体出自 <a href="https://fonts.google.com/specimen/Open+Sans">Steve Matteson</a> 之手。FreshRSS 基于 PHP 框架 <a href="https://github.com/marienfressinaud/MINZ">Minz</a>。', - 'freshrss_description' => 'FreshRSS 是一个自托管的 RSS 聚合服务,类似于 <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> 或 <a href="http://leed.idleman.fr/">Leed</a>。 它不仅轻快又易用,而且强大又易于配置。', + 'freshrss_description' => 'FreshRSS 是一个自托管的 RSS 聚合服务,类似于 <a href="http://tontof.net/kriss/feed/">Kriss Feed</a> 或 <a href="https://github.com/LeedRSS/Leed">Leed</a>。 它不仅轻快又易用,而且强大又易于配置。', 'github' => '<a href="https://github.com/FreshRSS/FreshRSS/issues">Github Issues</a>', 'license' => '授权', 'project_website' => '项目网站', @@ -15,6 +15,9 @@ return array( 'version' => '版本', 'website' => '网站', ), + 'tos' => array( + 'title' => 'Terms of Service', // TODO - Translation + ), 'feed' => array( 'add' => '你可以添加一些 RSS 源。', 'empty' => '暂时没有文章可显示。', diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php index bf517756b..944d6986f 100644 --- a/app/i18n/zh-cn/sub.php +++ b/app/i18n/zh-cn/sub.php @@ -13,9 +13,12 @@ return array( 'category' => array( '_' => '分类', 'add' => '添加分类', + 'archiving' => '存档', 'empty' => '空分类', 'information' => '信息', 'new' => '新分类', + 'position' => 'Display position', //TODO - Translation + 'position_help' => 'To control category sort order', //TODO - Translation 'title' => '标题', ), 'feed' => array( @@ -40,7 +43,7 @@ return array( 'help' => 'Write one search filter per line.', //TODO - Translation ), 'information' => '信息', - 'keep_history' => '至少保存的文章数', + 'keep_min' => '至少保存的文章数', 'moved_category_deleted' => '删除分类时,其中的 RSS 源会自动归类到 <em>%s</em>', 'mute' => '暂停', 'no_selected' => '未选择 RSS 源。', @@ -72,6 +75,7 @@ return array( ), 'firefox' => array( 'documentation' => '按照 <a href="https://developer.mozilla.org/en-US/Firefox/Releases/2/Adding_feed_readers_to_Firefox#Adding_a_new_feed_reader_manually">这里</a> 描述的步骤可将 FreshRSS 添加到 Firefox 阅读器列表', + 'obsolete_63' => 'From version 63 and onwards, Firefox has removed the ability to add your own subscription services that are not standalone programs.', //TODO - Translation 'title' => 'Firefox RSS 阅读器', ), 'import_export' => array( diff --git a/app/i18n/zh-cn/user.php b/app/i18n/zh-cn/user.php new file mode 100644 index 000000000..3a8343c11 --- /dev/null +++ b/app/i18n/zh-cn/user.php @@ -0,0 +1,37 @@ +<?php + +return array( + 'email' => array( + 'feedback' => array( + 'invalid' => 'The email address is invalid.', //TODO - Translation + 'required' => 'The email address is required.', //TODO - Translation + ), + 'validation' => array( + 'change_email' => 'You can change your email address <a href="%s">on the profile page</a>.', //TODO - Translation + 'email_sent_to' => 'We sent you an email at <strong>%s</strong>, please follow its indications to validate your address.', //TODO - Translation + 'feedback' => array( + 'email_failed' => 'We couldn’t send you an email because of a misconfiguration of the server.', //TODO - Translation + 'email_sent' => 'An email has been sent to your address.', //TODO - Translation + 'error' => 'The email address failed to be validated.', //TODO - Translation + 'ok' => 'The email address has been validated.', //TODO - Translation + 'unneccessary' => 'The email address was already validated.', //TODO - Translation + 'wrong_token' => 'The email address failed to be validated due to a wrong token.', //TODO - Translation + ), + 'need_to' => 'You need to validate your email address before being able to use %s.', //TODO - Translation + 'resend_email' => 'Resend the email', //TODO - Translation + 'title' => 'Email address validation', //TODO - Translation + ), + ), + 'tos' => array( + 'feedback' => array( + 'invalid' => 'You must accept the Terms of Service to be able to register.', // TODO - Translation + ), + ), + 'mailer' => array( + 'email_need_validation' => array( + 'title' => 'You need to validate your account', //TODO - Translation + 'welcome' => 'Welcome %s,', //TODO - Translation + 'body' => 'You’ve just registered on %s but you still need to validate your email. For that, just follow the link:', //TODO - Translation + ), + ), +); diff --git a/app/install.php b/app/install.php index 961a7c171..96bee34a1 100644 --- a/app/install.php +++ b/app/install.php @@ -17,24 +17,10 @@ if (isset($_GET['step'])) { define('STEP', 0); } -if (STEP === 3 && isset($_POST['type'])) { +if (STEP === 2 && isset($_POST['type'])) { $_SESSION['bd_type'] = $_POST['type']; } -if (isset($_SESSION['bd_type'])) { - switch ($_SESSION['bd_type']) { - case 'mysql': - include_once(APP_PATH . '/SQL/install.sql.mysql.php'); - break; - case 'sqlite': - include_once(APP_PATH . '/SQL/install.sql.sqlite.php'); - break; - case 'pgsql': - include_once(APP_PATH . '/SQL/install.sql.pgsql.php'); - break; - } -} - function param($key, $default = false) { if (isset($_POST[$key])) { return $_POST[$key]; @@ -43,7 +29,6 @@ function param($key, $default = false) { } } - // gestion internationalisation function initTranslate() { Minz_Translate::init(); @@ -61,7 +46,7 @@ function initTranslate() { } function get_best_language() { - $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE']; + $accept = empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? '' : $_SERVER['HTTP_ACCEPT_LANGUAGE']; return strtolower(substr($accept, 0, 2)); } @@ -101,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; @@ -119,68 +103,13 @@ function saveStep1() { } function saveStep2() { - $user_default_config = Minz_Configuration::get('default_user'); - 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', ''); - } - - $password_plain = param('passwordPlain', false); - if ($password_plain !== false && cryptAvailable()) { - $_SESSION['passwordHash'] = FreshRSS_user_Controller::hashPassword($password_plain); - } - - if (empty($_SESSION['old_entries']) || - empty($_SESSION['auth_type']) || - empty($_SESSION['default_user'])) { - return false; - } - - if ($_SESSION['auth_type'] === 'form' && empty($_SESSION['passwordHash'])) { - return false; - } - - $_SESSION['salt'] = generateSalt(); - if ((!ctype_digit($_SESSION['old_entries'])) ||($_SESSION['old_entries'] < 1)) { - $_SESSION['old_entries'] = $user_default_config->old_entries; - } - - $token = ''; - - $config_array = array( - 'language' => $_SESSION['language'], - 'theme' => $user_default_config->theme, - 'old_entries' => $_SESSION['old_entries'], - 'passwordHash' => $_SESSION['passwordHash'], - 'token' => $token, - ); - - // Create default user files but first, we delete previous data to - // avoid access right problems. - $user_dir = join_path(USERS_PATH, $_SESSION['default_user']); - $user_config_path = join_path($user_dir, 'config.php'); - - recursive_unlink($user_dir); - mkdir($user_dir); - file_put_contents($user_config_path, "<?php\n return " . var_export($config_array, true) . ";\n"); - - header('Location: index.php?step=3'); - } -} - -function saveStep3() { if (!empty($_POST)) { if ($_SESSION['bd_type'] === 'sqlite') { - $_SESSION['bd_base'] = $_SESSION['default_user']; + $_SESSION['bd_base'] = ''; $_SESSION['bd_host'] = ''; $_SESSION['bd_user'] = ''; $_SESSION['bd_password'] = ''; $_SESSION['bd_prefix'] = ''; - $_SESSION['bd_prefix_user'] = ''; //No prefix for SQLite } else { if (empty($_POST['type']) || empty($_POST['host']) || @@ -193,7 +122,6 @@ function saveStep3() { $_SESSION['bd_user'] = $_POST['user']; $_SESSION['bd_password'] = $_POST['pass']; $_SESSION['bd_prefix'] = substr($_POST['prefix'], 0, 16); - $_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] . (empty($_SESSION['default_user']) ? '' : ($_SESSION['default_user'] . '_')); } if ($_SESSION['bd_type'] === 'pgsql') { $_SESSION['bd_base'] = strtolower($_SESSION['bd_base']); @@ -201,44 +129,108 @@ function saveStep3() { // We use dirname to remove the /i part $base_url = dirname(Minz_Request::guessBaseUrl()); - $config_array = array( - 'salt' => $_SESSION['salt'], + $config_array = [ + 'salt' => generateSalt(), 'base_url' => $base_url, - 'title' => $_SESSION['title'], - 'default_user' => $_SESSION['default_user'], - 'auth_type' => $_SESSION['auth_type'], - 'db' => array( + 'default_user' => 'admin', + 'db' => [ 'type' => $_SESSION['bd_type'], 'host' => $_SESSION['bd_host'], 'user' => $_SESSION['bd_user'], 'password' => $_SESSION['bd_password'], 'base' => $_SESSION['bd_base'], 'prefix' => $_SESSION['bd_prefix'], - 'pdo_options' => array(), - ), + 'pdo_options' => [], + ], 'pubsubhubbub_enabled' => server_is_public($base_url), - ); + ]; + if (!empty($_SESSION['title'])) { + $config_array['title'] = $_SESSION['title']; + } + if (!empty($_SESSION['auth_type'])) { + $config_array['auth_type'] = $_SESSION['auth_type']; + } + + @unlink(DATA_PATH . '/config.php'); //To avoid access-rights problems + file_put_contents(DATA_PATH . '/config.php', "<?php\n return " . var_export($config_array, true) . ";\n"); - @unlink(join_path(DATA_PATH, 'config.php')); //To avoid access-rights problems - file_put_contents(join_path(DATA_PATH, 'config.php'), "<?php\n return " . var_export($config_array, true) . ";\n"); + Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php'); + FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); - $config_array['db']['default_user'] = $config_array['default_user']; - $config_array['db']['prefix_user'] = $_SESSION['bd_prefix_user']; - $ok = checkDb($config_array['db']) && checkDbUser($config_array['db']); + $ok = false; + try { + $ok = checkDb(); + } catch (Exception $ex) { + $_SESSION['bd_error'] = $ex->getMessage(); + $ok = false; + } if (!$ok) { @unlink(join_path(DATA_PATH, 'config.php')); } if ($ok) { $_SESSION['bd_error'] = ''; - header('Location: index.php?step=4'); - } else { - $_SESSION['bd_error'] = empty($config_array['db']['error']) ? 'Unknown error!' : $config_array['db']['error']; + header('Location: index.php?step=3'); + } elseif (empty($_SESSION['bd_error'])) { + $_SESSION['bd_error'] = 'Unknown error!'; } } invalidateHttpCache(); } +function saveStep3() { + $user_default_config = Minz_Configuration::get('default_user'); + if (!empty($_POST)) { + $system_default_config = Minz_Configuration::get('default_system'); + $_SESSION['title'] = $system_default_config->title; + $_SESSION['auth_type'] = param('auth_type', 'form'); + if (FreshRSS_user_Controller::checkUsername(param('default_user', ''))) { + $_SESSION['default_user'] = param('default_user', ''); + } + + if (empty($_SESSION['auth_type']) || + empty($_SESSION['default_user'])) { + return false; + } + + $password_plain = param('passwordPlain', false); + if ($_SESSION['auth_type'] === 'form' && $password_plain == '') { + return false; + } + + Minz_Configuration::register('system', DATA_PATH . '/config.php', FRESHRSS_PATH . '/config.default.php'); + FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); + Minz_Translate::init($_SESSION['language']); + + FreshRSS_Context::$system_conf->default_user = $_SESSION['default_user']; + FreshRSS_Context::$system_conf->save(); + + // Create default user files but first, we delete previous data to + // avoid access right problems. + recursive_unlink(USERS_PATH . '/' . $_SESSION['default_user']); + + $ok = false; + try { + $ok = FreshRSS_user_Controller::createUser( + $_SESSION['default_user'], + '', //TODO: Add e-mail + $password_plain, + '', + [ + 'language' => $_SESSION['language'], + ] + ); + } catch (Exception $e) { + $_SESSION['bd_error'] = $e->getMessage(); + $ok = false; + } + if (!$ok) { + return false; + } + + header('Location: index.php?step=4'); + } +} /*** VÉRIFICATIONS ***/ function checkStep() { @@ -297,29 +289,6 @@ function freshrss_already_installed() { } function checkStep2() { - $conf = !empty($_SESSION['old_entries']) && - !empty($_SESSION['default_user']); - - $form = ( - isset($_SESSION['auth_type']) && - ($_SESSION['auth_type'] != 'form' || !empty($_SESSION['passwordHash'])) - ); - - $defaultUser = empty($_POST['default_user']) ? null : $_POST['default_user']; - if ($defaultUser === null) { - $defaultUser = empty($_SESSION['default_user']) ? '' : $_SESSION['default_user']; - } - $data = is_writable(join_path(USERS_PATH, $defaultUser, 'config.php')); - - return array( - 'conf' => $conf ? 'ok' : 'ko', - 'form' => $form ? 'ok' : 'ko', - 'data' => $data ? 'ok' : 'ko', - 'all' => $conf && $form && $data ? 'ok' : 'ko' - ); -} - -function checkStep3() { $conf = is_writable(join_path(DATA_PATH, 'config.php')); $bd = isset($_SESSION['bd_type']) && @@ -331,60 +300,52 @@ function checkStep3() { isset($_SESSION['bd_error']); $conn = empty($_SESSION['bd_error']); - return array( + return [ 'bd' => $bd ? 'ok' : 'ko', 'conn' => $conn ? 'ok' : 'ko', 'conf' => $conf ? 'ok' : 'ko', - 'all' => $bd && $conn && $conf ? 'ok' : 'ko' - ); + 'all' => $bd && $conn && $conf ? 'ok' : 'ko', + ]; } -function checkDbUser(&$dbOptions) { - $ok = false; - $str = $dbOptions['dsn']; - $driver_options = $dbOptions['options']; - try { - $c = new PDO($str, $dbOptions['user'], $dbOptions['password'], $driver_options); - if (defined('SQL_CREATE_TABLES')) { - $sql = sprintf(SQL_CREATE_TABLES . SQL_CREATE_TABLE_ENTRYTMP . SQL_CREATE_TABLE_TAGS . SQL_INSERT_FEEDS, - $dbOptions['prefix_user'], _t('gen.short.default_category')); - $stm = $c->prepare($sql); - $ok = $stm && $stm->execute(); - } else { - global $SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP, $SQL_CREATE_TABLE_TAGS, $SQL_INSERT_FEEDS; - $instructions = array_merge($SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP, $SQL_CREATE_TABLE_TAGS, $SQL_INSERT_FEEDS); - $ok = !empty($instructions); - foreach ($instructions as $instruction) { - $sql = sprintf($instruction, $dbOptions['prefix_user'], _t('gen.short.default_category')); - $stm = $c->prepare($sql); - $ok &= $stm && $stm->execute(); - } - } - } catch (PDOException $e) { - $ok = false; - $dbOptions['error'] = $e->getMessage(); +function checkStep3() { + $conf = !empty($_SESSION['default_user']); + + $form = isset($_SESSION['auth_type']); + + $defaultUser = empty($_POST['default_user']) ? null : $_POST['default_user']; + if ($defaultUser === null) { + $defaultUser = empty($_SESSION['default_user']) ? '' : $_SESSION['default_user']; } - return $ok; + $data = is_writable(join_path(USERS_PATH, $defaultUser, 'config.php')); + + return [ + 'conf' => $conf ? 'ok' : 'ko', + 'form' => $form ? 'ok' : 'ko', + 'data' => $data ? 'ok' : 'ko', + 'all' => $conf && $form && $data ? 'ok' : 'ko', + ]; } + /*** AFFICHAGE ***/ function printStep0() { $actual = Minz_Translate::language(); $languages = Minz_Translate::availableLanguages(); ?> <?php $s0 = checkStep0(); if ($s0['all'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.language.defined'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.language.defined') ?></p> <?php } ?> <form action="index.php?step=0" method="post"> - <legend><?php echo _t('install.language.choose'); ?></legend> + <legend><?= _t('install.language.choose') ?></legend> <div class="form-group"> - <label class="group-name" for="language"><?php echo _t('install.language'); ?></label> + <label class="group-name" for="language"><?= _t('install.language') ?></label> <div class="group-controls"> <select name="language" id="language" tabindex="1" > <?php foreach ($languages as $lang) { ?> - <option value="<?php echo $lang; ?>"<?php echo $actual == $lang ? ' selected="selected"' : ''; ?>> - <?php echo _t('gen.lang.' . $lang); ?> + <option value="<?= $lang ?>"<?= $actual == $lang ? ' selected="selected"' : '' ?>> + <?= _t('gen.lang.' . $lang) ?> </option> <?php } ?> </select> @@ -393,10 +354,10 @@ function printStep0() { <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important" tabindex="2" ><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn" tabindex="3" ><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important" tabindex="2" ><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn" tabindex="3" ><?= _t('gen.action.cancel') ?></button> <?php if ($s0['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=1" tabindex="4" ><?php echo _t('install.action.next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=1" tabindex="4" ><?= _t('install.action.next_step') ?></a> <?php } ?> </div> </div> @@ -408,203 +369,135 @@ function printStep0() { function printStep1() { $res = checkRequirements(); ?> - <noscript><p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('install.javascript_is_better'); ?></p></noscript> + <noscript><p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('install.javascript_is_better') ?></p></noscript> <?php if ($res['php'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.php.ok', PHP_VERSION); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.php.ok', PHP_VERSION) ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.php.nok', PHP_VERSION, '5.3.8'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.php.nok', PHP_VERSION, '5.6.0') ?></p> <?php } ?> <?php if ($res['minz'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.minz.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.minz.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.minz.nok', join_path(LIB_PATH, 'Minz')); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.minz.nok', join_path(LIB_PATH, 'Minz')) ?></p> <?php } ?> <?php if ($res['pdo'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.pdo.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.pdo.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.pdo.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.pdo.nok') ?></p> <?php } ?> <?php if ($res['curl'] == 'ok') { ?> <?php $version = curl_version(); ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.curl.ok', $version['version']); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.curl.ok', $version['version']) ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.curl.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.curl.nok') ?></p> <?php } ?> <?php if ($res['json'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.json.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.json.ok') ?></p> <?php } else { ?> - <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.json.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.json.nok') ?></p> <?php } ?> <?php if ($res['pcre'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.pcre.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.pcre.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.pcre.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.pcre.nok') ?></p> <?php } ?> <?php if ($res['ctype'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.ctype.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.ctype.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.ctype.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.ctype.nok') ?></p> <?php } ?> <?php if ($res['dom'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.dom.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.dom.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.dom.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.dom.nok') ?></p> <?php } ?> <?php if ($res['xml'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.xml.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.xml.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.xml.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.xml.nok') ?></p> <?php } ?> <?php if ($res['mbstring'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.mbstring.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.mbstring.ok') ?></p> <?php } else { ?> - <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.mbstring.nok'); ?></p> + <p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.mbstring.nok') ?></p> <?php } ?> <?php if ($res['fileinfo'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.fileinfo.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.fileinfo.ok') ?></p> <?php } else { ?> - <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.fileinfo.nok'); ?></p> + <p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.fileinfo.nok') ?></p> <?php } ?> <?php if ($res['data'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.data.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.data.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.data.nok', DATA_PATH); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.data.nok', DATA_PATH) ?></p> <?php } ?> <?php if ($res['cache'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.cache.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.cache.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.cache.nok', CACHE_PATH); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.cache.nok', CACHE_PATH) ?></p> <?php } ?> <?php if ($res['users'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.users.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.users.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.users.nok', USERS_PATH); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.users.nok', USERS_PATH) ?></p> <?php } ?> <?php if ($res['favicons'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.favicons.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.favicons.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.favicons.nok', DATA_PATH . '/favicons'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.favicons.nok', DATA_PATH . '/favicons') ?></p> <?php } ?> <?php if ($res['http_referer'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.check.http_referer.ok'); ?></p> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.check.http_referer.ok') ?></p> <?php } else { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.check.http_referer.nok'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.check.http_referer.nok') ?></p> <?php } ?> <?php if (freshrss_already_installed() && $res['all'] == 'ok') { ?> - <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('install.check.already_installed'); ?></p> + <p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('install.check.already_installed') ?></p> <form action="index.php?step=1" method="post"> <input type="hidden" name="freshrss-keep-install" value="1" /> - <button type="submit" class="btn btn-important next-step" tabindex="1" ><?php echo _t('install.action.keep_install'); ?></button> - <a class="btn btn-attention next-step confirm" data-str-confirm="<?php echo _t('install.js.confirm_reinstall'); ?>" href="?step=2" tabindex="2" ><?php echo _t('install.action.reinstall'); ?></a> + <button type="submit" class="btn btn-important next-step" tabindex="1" ><?= _t('install.action.keep_install') ?></button> + <a class="btn btn-attention next-step confirm" data-str-confirm="<?= _t('install.js.confirm_reinstall') ?>" href="?step=2" tabindex="2" ><?= _t('install.action.reinstall') ?></a> </form> <?php } elseif ($res['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=2" tabindex="1" ><?php echo _t('install.action.next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=2" tabindex="1" ><?= _t('install.action.next_step') ?></a> <?php } else { ?> - <p class="alert alert-error"><?php echo _t('install.action.fix_errors_before'); ?></p> + <p class="alert alert-error"><?= _t('install.action.fix_errors_before') ?></p> <?php } ?> <?php } function printStep2() { - $user_default_config = Minz_Configuration::get('default_user'); -?> - <?php $s2 = checkStep2(); if ($s2['all'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.conf.ok'); ?></p> - <?php } elseif (!empty($_POST)) { ?> - <p class="alert alert-error"><?php echo _t('install.fix_errors_before'); ?></p> - <?php } ?> - - <form action="index.php?step=2" method="post"> - <legend><?php echo _t('install.conf'); ?></legend> - - <div class="form-group"> - <label class="group-name" for="old_entries"><?php echo _t('install.delete_articles_after'); ?></label> - <div class="group-controls"> - <input type="number" id="old_entries" name="old_entries" required="required" min="1" max="1200" value="<?php echo isset($_SESSION['old_entries']) ? $_SESSION['old_entries'] : $user_default_config->old_entries; ?>" tabindex="2" /> <?php echo _t('gen.date.month'); ?> - </div> - </div> - - <div class="form-group"> - <label class="group-name" for="default_user"><?php echo _t('install.default_user'); ?></label> - <div class="group-controls"> - <input type="text" id="default_user" name="default_user" autocomplete="username" required="required" size="16" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" value="<?php echo isset($_SESSION['default_user']) ? $_SESSION['default_user'] : ''; ?>" placeholder="<?php echo httpAuthUser() == '' ? 'alice' : httpAuthUser(); ?>" tabindex="3" /> - </div> - </div> - - <div class="form-group"> - <label class="group-name" for="auth_type"><?php echo _t('install.auth.type'); ?></label> - <div class="group-controls"> - <select id="auth_type" name="auth_type" required="required" tabindex="4"> - <?php - function no_auth($auth_type) { - return !in_array($auth_type, array('form', 'http_auth', 'none')); - } - $auth_type = isset($_SESSION['auth_type']) ? $_SESSION['auth_type'] : ''; - ?> - <option value="form"<?php echo $auth_type === 'form' || (no_auth($auth_type) && cryptAvailable()) ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"'; ?>><?php echo _t('install.auth.form'); ?></option> - <option value="http_auth"<?php echo $auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('install.auth.http'); ?>(REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option> - <option value="none"<?php echo $auth_type === 'none' || (no_auth($auth_type) && !cryptAvailable()) ? ' selected="selected"' : ''; ?>><?php echo _t('install.auth.none'); ?></option> - </select> - </div> - </div> - - <div class="form-group"> - <label class="group-name" for="passwordPlain"><?php echo _t('install.auth.password_form'); ?></label> - <div class="group-controls"> - <div class="stick"> - <input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?php echo $auth_type === 'form' ? ' required="required"' : ''; ?> tabindex="5" /> - <a class="btn toggle-password" data-toggle="passwordPlain"><?php echo FreshRSS_Themes::icon('key'); ?></a> - </div> - <?php echo _i('help'); ?> <?php echo _t('install.auth.password_format'); ?> - <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> - </div> - </div> - - <div class="form-group form-actions"> - <div class="group-controls"> - <button type="submit" class="btn btn-important" tabindex="7" ><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn" tabindex="8" ><?php echo _t('gen.action.cancel'); ?></button> - <?php if ($s2['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=3" tabindex="9" ><?php echo _t('install.action.next_step'); ?></a> - <?php } ?> - </div> - </div> - </form> -<?php -} - -function printStep3() { $system_default_config = Minz_Configuration::get('default_system'); ?> - <?php $s3 = checkStep3(); if ($s3['all'] == 'ok') { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('gen.short.ok'); ?></span> <?php echo _t('install.bdd.conf.ok'); ?></p> - <?php } elseif ($s3['conn'] == 'ko') { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.bdd.conf.ko'),(empty($_SESSION['bd_error']) ? '' : ' : ' . $_SESSION['bd_error']); ?></p> + <?php $s2 = checkStep2(); if ($s2['all'] == 'ok') { ?> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.bdd.conf.ok') ?></p> + <?php } elseif ($s2['conn'] == 'ko') { ?> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.bdd.conf.ko'),(empty($_SESSION['bd_error']) ? '' : ' : ' . $_SESSION['bd_error']) ?></p> <?php } ?> - <form action="index.php?step=3" method="post" autocomplete="off"> - <legend><?php echo _t('install.bdd.conf'); ?></legend> + <form action="index.php?step=2" method="post" autocomplete="off"> + <legend><?= _t('install.bdd.conf') ?></legend> <div class="form-group"> - <label class="group-name" for="type"><?php echo _t('install.bdd.type'); ?></label> + <label class="group-name" for="type"><?= _t('install.bdd.type') ?></label> <div class="group-controls"> <select name="type" id="type" tabindex="1"> <?php if (extension_loaded('pdo_sqlite')) {?> @@ -631,47 +524,108 @@ function printStep3() { <div id="mysql"> <div class="form-group"> - <label class="group-name" for="host"><?php echo _t('install.bdd.host'); ?></label> + <label class="group-name" for="host"><?= _t('install.bdd.host') ?></label> <div class="group-controls"> - <input type="text" id="host" name="host" pattern="[0-9A-Z/a-z_.-]{1,64}(:[0-9]{2,5})?" value="<?php echo isset($_SESSION['bd_host']) ? $_SESSION['bd_host'] : $system_default_config->db['host']; ?>" tabindex="2" /> + <input type="text" id="host" name="host" pattern="[0-9A-Z/a-z_.-]{1,64}(:[0-9]{2,5})?" value="<?= isset($_SESSION['bd_host']) ? $_SESSION['bd_host'] : $system_default_config->db['host'] ?>" tabindex="2" /> </div> </div> <div class="form-group"> - <label class="group-name" for="user"><?php echo _t('install.bdd.username'); ?></label> + <label class="group-name" for="user"><?= _t('install.bdd.username') ?></label> <div class="group-controls"> - <input type="text" id="user" name="user" maxlength="64" pattern="[0-9A-Za-z_.-]{1,64}" value="<?php echo isset($_SESSION['bd_user']) ? $_SESSION['bd_user'] : ''; ?>" tabindex="3" /> + <input type="text" id="user" name="user" maxlength="64" pattern="[0-9A-Za-z_.-]{1,64}" value="<?= isset($_SESSION['bd_user']) ? $_SESSION['bd_user'] : '' ?>" tabindex="3" /> </div> </div> <div class="form-group"> - <label class="group-name" for="pass"><?php echo _t('install.bdd.password'); ?></label> + <label class="group-name" for="pass"><?= _t('install.bdd.password') ?></label> <div class="group-controls"> - <input type="password" id="pass" name="pass" value="<?php echo isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" tabindex="4" autocomplete="off" /> + <input type="password" id="pass" name="pass" value="<?= isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : '' ?>" tabindex="4" autocomplete="off" /> + </div> + </div> + + <div class="form-group"> + <label class="group-name" for="base"><?= _t('install.bdd') ?></label> + <div class="group-controls"> + <input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_-]{1,64}" value="<?= isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : '' ?>" tabindex="5" /> + </div> + </div> + + <div class="form-group"> + <label class="group-name" for="prefix"><?= _t('install.bdd.prefix') ?></label> + <div class="group-controls"> + <input type="text" id="prefix" name="prefix" maxlength="16" pattern="[0-9A-Za-z_]{1,16}" value="<?= isset($_SESSION['bd_prefix']) ? $_SESSION['bd_prefix'] : $system_default_config->db['prefix'] ?>" tabindex="6" /> + </div> + </div> + </div> + + <div class="form-group form-actions"> + <div class="group-controls"> + <button type="submit" class="btn btn-important" tabindex="7" ><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn" tabindex="8" ><?= _t('gen.action.cancel') ?></button> + <?php if ($s2['all'] == 'ok') { ?> + <a class="btn btn-important next-step" href="?step=3" tabindex="9" ><?= _t('install.action.next_step') ?></a> + <?php } ?> </div> </div> + </form> +<?php +} + +function printStep3() { + $user_default_config = Minz_Configuration::get('default_user'); +?> + <?php $s3 = checkStep3(); if ($s3['all'] == 'ok') { ?> + <p class="alert alert-success"><span class="alert-head"><?= _t('gen.short.ok') ?></span> <?= _t('install.conf.ok') ?></p> + <?php } elseif (!empty($_POST)) { ?> + <p class="alert alert-error"><?= _t('install.fix_errors_before') ?></p> + <?php } ?> + + <form action="index.php?step=3" method="post"> + <legend><?= _t('install.conf') ?></legend> <div class="form-group"> - <label class="group-name" for="base"><?php echo _t('install.bdd'); ?></label> + <label class="group-name" for="default_user"><?= _t('install.default_user') ?></label> <div class="group-controls"> - <input type="text" id="base" name="base" maxlength="64" pattern="[0-9A-Za-z_-]{1,64}" value="<?php echo isset($_SESSION['bd_base']) ? $_SESSION['bd_base'] : ''; ?>" tabindex="5" /> + <input type="text" id="default_user" name="default_user" autocomplete="username" required="required" size="16" pattern="<?= FreshRSS_user_Controller::USERNAME_PATTERN ?>" value="<?= isset($_SESSION['default_user']) ? $_SESSION['default_user'] : '' ?>" placeholder="<?= httpAuthUser() == '' ? 'alice' : httpAuthUser() ?>" tabindex="3" /> </div> </div> <div class="form-group"> - <label class="group-name" for="prefix"><?php echo _t('install.bdd.prefix'); ?></label> + <label class="group-name" for="auth_type"><?= _t('install.auth.type') ?></label> <div class="group-controls"> - <input type="text" id="prefix" name="prefix" maxlength="16" pattern="[0-9A-Za-z_]{1,16}" value="<?php echo isset($_SESSION['bd_prefix']) ? $_SESSION['bd_prefix'] : $system_default_config->db['prefix']; ?>" tabindex="6" /> + <select id="auth_type" name="auth_type" required="required" tabindex="4"> + <?php + function no_auth($auth_type) { + return !in_array($auth_type, array('form', 'http_auth', 'none')); + } + $auth_type = isset($_SESSION['auth_type']) ? $_SESSION['auth_type'] : ''; + ?> + <option value="form"<?= $auth_type === 'form' || (no_auth($auth_type) && cryptAvailable()) ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"' ?>><?= _t('install.auth.form') ?></option> + <option value="http_auth"<?= $auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : '' ?>><?= _t('install.auth.http') ?>(REMOTE_USER = '<?= httpAuthUser() ?>')</option> + <option value="none"<?= $auth_type === 'none' || (no_auth($auth_type) && !cryptAvailable()) ? ' selected="selected"' : '' ?>><?= _t('install.auth.none') ?></option> + </select> </div> </div> + + <div class="form-group"> + <label class="group-name" for="passwordPlain"><?= _t('install.auth.password_form') ?></label> + <div class="group-controls"> + <div class="stick"> + <input type="password" id="passwordPlain" name="passwordPlain" pattern=".{7,}" autocomplete="off" <?= $auth_type === 'form' ? ' required="required"' : '' ?> tabindex="5" /> + <a class="btn toggle-password" data-toggle="passwordPlain"><?= FreshRSS_Themes::icon('key') ?></a> + </div> + <?= _i('help') ?> <?= _t('install.auth.password_format') ?> + <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> + </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important" tabindex="7" ><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn" tabindex="8" ><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important" tabindex="7" ><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn" tabindex="8" ><?= _t('gen.action.cancel') ?></button> <?php if ($s3['all'] == 'ok') { ?> - <a class="btn btn-important next-step" href="?step=4" tabindex="9" ><?php echo _t('install.action.next_step'); ?></a> + <a class="btn btn-important next-step" href="?step=4" tabindex="9" ><?= _t('install.action.next_step') ?></a> <?php } ?> </div> </div> @@ -681,14 +635,14 @@ function printStep3() { function printStep4() { ?> - <p class="alert alert-success"><span class="alert-head"><?php echo _t('install.congratulations'); ?></span> <?php echo _t('install.ok'); ?></p> - <a class="btn btn-important next-step" href="?step=5" tabindex="1"><?php echo _t('install.action.finish'); ?></a> + <p class="alert alert-success"><span class="alert-head"><?= _t('install.congratulations') ?></span> <?= _t('install.ok') ?></p> + <a class="btn btn-important next-step" href="?step=5" tabindex="1"><?= _t('install.action.finish') ?></a> <?php } function printStep5() { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.not_deleted', DATA_PATH . '/do-install.txt'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('install.not_deleted', DATA_PATH . '/do-install.txt') ?></p> <?php } @@ -725,28 +679,28 @@ case 5: <meta charset="UTF-8" /> <meta name="viewport" content="initial-scale=1.0" /> <script id="jsonVars" type="application/json">{}</script> - <title><?php echo _t('install.title'); ?></title> - <link rel="stylesheet" href="../themes/base-theme/template.css?<?php echo @filemtime(PUBLIC_PATH . '/themes/base-theme/template.css'); ?>" /> - <link rel="stylesheet" href="../themes/Origine/origine.css?<?php echo @filemtime(PUBLIC_PATH . '/themes/Origine/origine.css'); ?>" /> + <title><?= _t('install.title') ?></title> + <link rel="stylesheet" href="../themes/base-theme/template.css?<?= @filemtime(PUBLIC_PATH . '/themes/base-theme/template.css') ?>" /> + <link rel="stylesheet" href="../themes/Origine/origine.css?<?= @filemtime(PUBLIC_PATH . '/themes/Origine/origine.css') ?>" /> <meta name="robots" content="noindex,nofollow" /> </head> <body> <div class="header"> <div class="item title"> - <h1><a href="index.php"><?php echo _t('install.title'); ?></a></h1> - <h2><?php echo _t('install.step', STEP); ?></h2> + <h1><a href="index.php"><?= _t('install.title') ?></a></h1> + <h2><?= _t('install.step', STEP) ?></h2> </div> </div> <div id="global"> <ul class="nav nav-list aside"> - <li class="nav-header"><?php echo _t('install.steps'); ?></li> - <li class="item<?php echo STEP == 0 ? ' active' : ''; ?>"><a href="?step=0"><?php echo _t('install.language'); ?></a></li> - <li class="item<?php echo STEP == 1 ? ' active' : ''; ?>"><a href="?step=1"><?php echo _t('install.check'); ?></a></li> - <li class="item<?php echo STEP == 2 ? ' active' : ''; ?>"><a href="?step=2"><?php echo _t('install.conf'); ?></a></li> - <li class="item<?php echo STEP == 3 ? ' active' : ''; ?>"><a href="?step=3"><?php echo _t('install.bdd.conf'); ?></a></li> - <li class="item<?php echo STEP == 4 ? ' active' : ''; ?>"><a href="?step=4"><?php echo _t('install.this_is_the_end'); ?></a></li> + <li class="nav-header"><?= _t('install.steps') ?></li> + <li class="item<?= STEP == 0 ? ' active' : '' ?>"><a href="?step=0"><?= _t('install.language') ?></a></li> + <li class="item<?= STEP == 1 ? ' active' : '' ?>"><a href="?step=1"><?= _t('install.check') ?></a></li> + <li class="item<?= STEP == 2 ? ' active' : '' ?>"><a href="?step=2"><?= _t('install.bdd.conf') ?></a></li> + <li class="item<?= STEP == 3 ? ' active' : '' ?>"><a href="?step=3"><?= _t('install.conf') ?></a></li> + <li class="item<?= STEP == 4 ? ' active' : '' ?>"><a href="?step=4"><?= _t('install.this_is_the_end') ?></a></li> </ul> <div class="post"> @@ -775,6 +729,6 @@ case 5: ?> </div> </div> - <script src="../scripts/install.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/install.js'); ?>"></script> + <script src="../scripts/install.js?<?= @filemtime(PUBLIC_PATH . '/scripts/install.js') ?>"></script> </body> </html> diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index 94f5b1f6c..1267f747c 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -1,51 +1,54 @@ <ul class="nav nav-list aside"> - <li class="nav-header"><?php echo _t('gen.menu.configuration'); ?></li> - <li class="item<?php echo Minz_Request::actionName() === 'display' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'display'); ?>"><?php echo _t('gen.menu.display'); ?></a> + <li class="nav-header"><?= _t('gen.menu.configuration') ?></li> + <li class="item<?= Minz_Request::actionName() === 'display' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'display') ?>"><?= _t('gen.menu.display') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() === 'reading' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'reading'); ?>"><?php echo _t('gen.menu.reading'); ?></a> + <li class="item<?= Minz_Request::actionName() === 'reading' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'reading') ?>"><?= _t('gen.menu.reading') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() === 'archiving' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'archiving'); ?>"><?php echo _t('gen.menu.archiving'); ?></a> + <li class="item<?= Minz_Request::actionName() === 'archiving' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'archiving') ?>"><?= _t('gen.menu.archiving') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() === 'sharing' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'sharing'); ?>"><?php echo _t('gen.menu.sharing'); ?></a> + <li class="item<?= Minz_Request::actionName() === 'sharing' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'sharing') ?>"><?= _t('gen.menu.sharing') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() === 'shortcut' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'shortcut'); ?>"><?php echo _t('gen.menu.shortcuts'); ?></a> + <li class="item<?= Minz_Request::actionName() === 'shortcut' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'shortcut') ?>"><?= _t('gen.menu.shortcuts') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() === 'queries' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('gen.menu.queries'); ?></a> + <li class="item<?= Minz_Request::actionName() === 'queries' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'queries') ?>"><?= _t('gen.menu.queries') ?></a> </li> <li class="item<?php echo Minz_Request::controllerName() === 'user' && Minz_Request::actionName() === 'profile'? ' active' : ''; ?>"> - <a href="<?php echo _url('user', 'profile'); ?>"><?php echo _t('gen.menu.user_profile'); ?></a> + <a href="<?= _url('user', 'profile') ?>"><?= _t('gen.menu.user_profile') ?></a> </li> - <li class="item<?php echo Minz_Request::controllerName() === 'extension' ? ' active' : ''; ?>"> - <a href="<?php echo _url('extension', 'index'); ?>"><?php echo _t('gen.menu.extensions'); ?></a> + <li class="item<?= Minz_Request::controllerName() === 'extension' ? ' active' : '' ?>"> + <a href="<?= _url('extension', 'index') ?>"><?= _t('gen.menu.extensions') ?></a> </li> + <?= Minz_ExtensionManager::callHook('menu_configuration_entry') ?> + <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> - <li class="nav-header"><?php echo _t('gen.menu.admin'); ?></li> - <li class="item<?php echo Minz_Request::actionName() === 'system' ? ' active' : ''; ?>"> - <a href="<?php echo _url('configure', 'system')?>"><?php echo _t('gen.menu.system'); ?></a> + <li class="nav-header"><?= _t('gen.menu.admin') ?></li> + <li class="item<?= Minz_Request::actionName() === 'system' ? ' active' : '' ?>"> + <a href="<?= _url('configure', 'system') ?>"><?= _t('gen.menu.system') ?></a> </li> <li class="item<?php echo Minz_Request::controllerName() === 'user' && Minz_Request::actionName() === 'manage' ? ' active' : ''; ?>"> - <a href="<?php echo _url('user', 'manage'); ?>"><?php echo _t('gen.menu.user_management'); ?></a> + <a href="<?= _url('user', 'manage') ?>"><?= _t('gen.menu.user_management') ?></a> </li> - <li class="item<?php echo Minz_Request::controllerName() === 'auth' ? ' active' : ''; ?>"> - <a href="<?php echo _url('auth', 'index'); ?>"><?php echo _t('gen.menu.authentication'); ?></a> + <li class="item<?= Minz_Request::controllerName() === 'auth' ? ' active' : '' ?>"> + <a href="<?= _url('auth', 'index') ?>"><?= _t('gen.menu.authentication') ?></a> </li> <li class="item<?php echo Minz_Request::controllerName() === 'update' && Minz_Request::actionName() === 'checkInstall' ? ' active' : ''; ?>"> - <a href="<?php echo _url('update', 'checkInstall'); ?>"><?php echo _t('gen.menu.check_install'); ?></a> + <a href="<?= _url('update', 'checkInstall') ?>"><?= _t('gen.menu.check_install') ?></a> </li> <?php if (!Minz_Configuration::get('system')->disable_update) { ?> <li class="item<?php echo Minz_Request::controllerName() === 'update' && Minz_Request::actionName() === 'index' ? ' active' : ''; ?>"> - <a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('gen.menu.update'); ?></a> + <a href="<?= _url('update', 'index') ?>"><?= _t('gen.menu.update') ?></a> </li> <?php } ?> + <?= Minz_ExtensionManager::callHook('menu_admin_entry') ?> <?php } ?> </ul> diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index 637acf4a4..e0c90282f 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -8,30 +8,30 @@ } ?> -<div class="aside aside_feed<?php echo $class; ?>" id="aside_feed"> - <a class="toggle_aside" href="#close"><?php echo _i('close'); ?></a> +<div class="aside aside_feed<?= $class ?>" id="aside_feed"> + <a class="toggle_aside" href="#close"><?= _i('close') ?></a> <?php if (FreshRSS_Auth::hasAccess()) { ?> <div class="stick configure-feeds no-mobile"> - <a id="btn-subscription" class="btn btn-important" href="<?php echo _url('subscription', 'index'); ?>"><?php echo _t('index.menu.subscription'); ?></a> - <a id="btn-importExport" class="btn btn-important" href="<?php echo _url('importExport', 'index'); ?>"><?php echo _i('import'); ?></a> + <a id="btn-subscription" class="btn btn-important" href="<?= _url('subscription', 'index') ?>"><?= _t('index.menu.subscription') ?></a> + <a id="btn-importExport" class="btn btn-important" href="<?= _url('importExport', 'index') ?>"><?= _i('import') ?></a> </div> <?php } elseif (FreshRSS_Auth::accessNeedsLogin()) { ?> - <a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('index.menu.about'); ?></a> + <a href="<?= _url('index', 'about') ?>"><?= _t('index.menu.about') ?></a> <?php } ?> <form id="mark-read-aside" method="post"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <ul id="sidebar" class="tree"> - <li class="tree-folder category all<?php echo FreshRSS_Context::isCurrentGet('a') ? ' active' : ''; ?>"> + <li class="tree-folder category all<?= FreshRSS_Context::isCurrentGet('a') ? ' active' : '' ?>"> <div class="tree-folder-title"> - <?php echo _i('all'); ?> <a class="title" data-unread="<?php echo format_number(FreshRSS_Context::$total_unread); ?>" href="<?php echo _url('index', $actual_view); ?>"><?php echo _t('index.menu.main_stream'); ?></a> + <?= _i('all') ?> <a class="title" data-unread="<?= format_number(FreshRSS_Context::$total_unread) ?>" href="<?= _url('index', $actual_view) ?>"><?= _t('index.menu.main_stream') ?></a> </div> </li> - <li class="tree-folder category favorites<?php echo FreshRSS_Context::isCurrentGet('s') ? ' active' : ''; ?>"> + <li class="tree-folder category favorites<?= FreshRSS_Context::isCurrentGet('s') ? ' active' : '' ?>"> <div class="tree-folder-title"> - <?php echo _i('bookmark'); ?> <a class="title" data-unread="<?php echo format_number(FreshRSS_Context::$total_starred['unread']); ?>" href="<?php echo _url('index', $actual_view, 'get', 's'); ?>"><?php echo _t('index.menu.favorites', format_number(FreshRSS_Context::$total_starred['all'])); ?></a> + <?= _i('bookmark') ?> <a class="title" data-unread="<?= format_number(FreshRSS_Context::$total_starred['unread']) ?>" href="<?= _url('index', $actual_view, 'get', 's') ?>"><?= _t('index.menu.favorites', format_number(FreshRSS_Context::$total_starred['all'])) ?></a> </div> </li> @@ -39,22 +39,22 @@ $t_active = FreshRSS_Context::isCurrentGet('T'); $t_show = $t_active || FreshRSS_Context::$user_conf->display_categories; ?> - <li class="tree-folder category tags<?php echo $t_active ? ' active' : ''; ?>"> + <li class="tree-folder category tags<?= $t_active ? ' active' : '' ?>"> <div class="tree-folder-title"> - <a class="dropdown-toggle" href="#"><?php echo _i($t_active ? 'up' : 'down'); ?></a> - <a class="title" data-unread="<?php echo format_number($this->nbUnreadTags); ?>" href="<?php echo _url('index', $actual_view, 'get', 'T'); ?>"><?php echo _t('index.menu.tags'); ?></a> + <a class="dropdown-toggle" href="#"><?= _i($t_active ? 'up' : 'down') ?></a> + <a class="title" data-unread="<?= format_number($this->nbUnreadTags) ?>" href="<?= _url('index', $actual_view, 'get', 'T') ?>"><?= _t('index.menu.tags') ?></a> </div> - <ul class="tree-folder-items<?php echo $t_show ? ' active' : ''; ?>"> + <ul class="tree-folder-items<?= $t_show ? ' active' : '' ?>"> <?php foreach ($this->tags as $tag): ?> - <li id="t_<?php echo $tag->id(); ?>" class="item feed<?php echo FreshRSS_Context::isCurrentGet('t_' . $tag->id()) ? ' active' : ''; ?>" data-unread="<?php echo $tag->nbUnread(); ?>"> + <li id="t_<?= $tag->id() ?>" class="item feed<?= FreshRSS_Context::isCurrentGet('t_' . $tag->id()) ? ' active' : '' ?>" data-unread="<?= $tag->nbUnread() ?>"> <div class="dropdown no-mobile"> <div class="dropdown-target"></div> - <a class="dropdown-toggle"><?php echo _i('configure'); ?></a> + <a class="dropdown-toggle"><?= _i('configure') ?></a> <?php /* tag_config_template */ ?> </div> - <?php echo FreshRSS_Themes::alt('label'); ?> <a class="item-title" data-unread="<?php echo format_number($tag->nbUnread()); ?>" href="<?php echo _url('index', $actual_view, 'get', 't_' . $tag->id()); ?>"><?php echo $tag->name(); ?></a> + <?= FreshRSS_Themes::alt('label') ?> <a class="item-title" data-unread="<?= format_number($tag->nbUnread()) ?>" href="<?= _url('index', $actual_view, 'get', 't_' . $tag->id()) ?>"><?= $tag->name() ?></a> </li> <?php endforeach; ?> </ul> @@ -63,28 +63,29 @@ <?php foreach ($this->categories as $cat) { $feeds = $cat->feeds(); + $position = $cat->attributes('position'); if (!empty($feeds)) { $c_active = FreshRSS_Context::isCurrentGet('c_' . $cat->id()); $c_show = $c_active || FreshRSS_Context::$user_conf->display_categories; ?> - <li class="tree-folder category<?php echo $c_active ? ' active' : ''; ?>" data-unread="<?php echo $cat->nbNotRead(); ?>"> + <li class="tree-folder category<?= $c_active ? ' active' : '' ?>"<?= null === $position ? '' : "data-position='$position'" ?> data-unread="<?= $cat->nbNotRead() ?>"> <div class="tree-folder-title"> - <a class="dropdown-toggle" href="#"><?php echo _i($c_show ? 'up' : 'down'); ?></a> - <a class="title<?php echo $cat->hasFeedsWithError() ? ' error' : ''; ?>" data-unread="<?php echo format_number($cat->nbNotRead()); ?>" href="<?php echo _url('index', $actual_view, 'get', 'c_' . $cat->id()); ?>"><?php echo $cat->name(); ?></a> + <a class="dropdown-toggle" href="#"><?= _i($c_show ? 'up' : 'down') ?></a> + <a class="title<?= $cat->hasFeedsWithError() ? ' error' : '' ?>" data-unread="<?= format_number($cat->nbNotRead()) ?>" href="<?= _url('index', $actual_view, 'get', 'c_' . $cat->id()) ?>"><?= $cat->name() ?></a> </div> - <ul class="tree-folder-items<?php echo $c_show ? ' active' : ''; ?>"> + <ul class="tree-folder-items<?= $c_show ? ' active' : '' ?>"> <?php foreach ($feeds as $feed) { $f_active = FreshRSS_Context::isCurrentGet('f_' . $feed->id()); ?> - <li id="f_<?php echo $feed->id(); ?>" class="item feed<?php echo $f_active ? ' active' : '', $feed->mute() ? ' mute' : ''; ?><?php echo $feed->inError() ? ' error' : ''; ?><?php echo $feed->nbEntries() <= 0 ? ' empty' : ''; ?>" data-unread="<?php echo $feed->nbNotRead(); ?>" data-priority="<?php echo $feed->priority(); ?>"> + <li id="f_<?= $feed->id() ?>" class="item feed<?= $f_active ? ' active' : '', $feed->mute() ? ' mute' : '' ?><?= $feed->inError() ? ' error' : '' ?><?= $feed->nbEntries() <= 0 ? ' empty' : '' ?>" data-unread="<?= $feed->nbNotRead() ?>" data-priority="<?= $feed->priority() ?>"> <div class="dropdown no-mobile"> <div class="dropdown-target"></div> - <a class="dropdown-toggle" data-fweb="<?php echo $feed->website(); ?>"><?php echo _i('configure'); ?></a> + <a class="dropdown-toggle" data-fweb="<?= $feed->website() ?>"><?= _i('configure') ?></a> <?php /* feed_config_template */ ?> </div> - <img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> <a class="item-title" data-unread="<?php echo format_number($feed->nbNotRead()); ?>" href="<?php echo _url('index', $actual_view, 'get', 'f_' . $feed->id()); ?>"><?php echo $feed->name(); ?></a> + <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" /> <a class="item-title" data-unread="<?= format_number($feed->nbNotRead()) ?>" href="<?= _url('index', $actual_view, 'get', 'f_' . $feed->id()) ?>"><?= $feed->name() ?></a> </li> <?php } ?> </ul> @@ -103,8 +104,8 @@ <li class="dropdown-close"><a href="#close">❌</a></li> <li class="item"> <button class="as-link confirm" disabled="disabled" - form="mark-read-aside" formaction="<?php echo _url('tag', 'delete', 'id_tag', '------'); ?>" - type="submit"><?php echo _t('gen.action.remove'); ?></button> + form="mark-read-aside" formaction="<?= _url('tag', 'delete', 'id_tag', '------') ?>" + type="submit"><?= _t('gen.action.remove') ?></button> </li> </ul> </script> @@ -112,21 +113,21 @@ <script id="feed_config_template" type="text/html"> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> - <li class="item"><a href="<?php echo _url('index', $actual_view, 'get', 'f_------'); ?>"><?php echo _t('gen.action.filter'); ?></a></li> + <li class="item"><a href="<?= _url('index', $actual_view, 'get', 'f_------') ?>"><?= _t('gen.action.filter') ?></a></li> <?php if (FreshRSS_Auth::hasAccess()) { ?> - <li class="item"><a href="<?php echo _url('stats', 'repartition', 'id', '------'); ?>"><?php echo _t('index.menu.stats'); ?></a></li> + <li class="item"><a href="<?= _url('stats', 'repartition', 'id', '------') ?>"><?= _t('index.menu.stats') ?></a></li> <?php } ?> - <li class="item"><a target="_blank" rel="noreferrer" href="http://example.net/"><?php echo _t('gen.action.see_website'); ?></a></li> + <li class="item"><a target="_blank" rel="noreferrer" href="http://example.net/"><?= _t('gen.action.see_website') ?></a></li> <?php if (FreshRSS_Auth::hasAccess()) { ?> <li class="separator"></li> - <li class="item"><a href="<?php echo _url('subscription', 'index', 'id', '------'); ?>"><?php echo _t('gen.action.manage'); ?></a></li> - <li class="item"><a href="<?php echo _url('feed', 'actualize', 'id', '------'); ?>"><?php echo _t('gen.action.actualize'); ?></a></li> + <li class="item"><a href="<?= _url('subscription', 'index', 'id', '------') ?>"><?= _t('gen.action.manage') ?></a></li> + <li class="item"><a href="<?= _url('feed', 'actualize', 'id', '------') ?>"><?= _t('gen.action.actualize') ?></a></li> <li class="item"> <?php $confirm = FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : ''; ?> - <button class="read_all as-link <?php echo $confirm; ?>" + <button class="read_all as-link <?= $confirm ?>" form="mark-read-aside" - formaction="<?php echo _url('entry', 'read', 'get', 'f_------'); ?>" - type="submit"><?php echo _t('gen.action.mark_read'); ?></button> + formaction="<?= _url('entry', 'read', 'get', 'f_------') ?>" + type="submit"><?= _t('gen.action.mark_read') ?></button> </li> <?php } ?> </ul> diff --git a/app/layout/aside_stats.phtml b/app/layout/aside_stats.phtml index 4bdaf7165..705e6ce86 100644 --- a/app/layout/aside_stats.phtml +++ b/app/layout/aside_stats.phtml @@ -1,12 +1,12 @@ <ul class="nav nav-list aside"> - <li class="nav-header"><?php echo _t('admin.stats'); ?></li> - <li class="item<?php echo Minz_Request::actionName() == 'index' ? ' active' : ''; ?>"> - <a href="<?php echo _url('stats', 'index'); ?>"><?php echo _t('admin.stats.menu.main'); ?></a> + <li class="nav-header"><?= _t('admin.stats') ?></li> + <li class="item<?= Minz_Request::actionName() == 'index' ? ' active' : '' ?>"> + <a href="<?= _url('stats', 'index') ?>"><?= _t('admin.stats.menu.main') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() == 'idle' ? ' active' : ''; ?>"> - <a href="<?php echo _url('stats', 'idle'); ?>"><?php echo _t('admin.stats.menu.idle'); ?></a> + <li class="item<?= Minz_Request::actionName() == 'idle' ? ' active' : '' ?>"> + <a href="<?= _url('stats', 'idle') ?>"><?= _t('admin.stats.menu.idle') ?></a> </li> - <li class="item<?php echo Minz_Request::actionName() == 'repartition' ? ' active' : ''; ?>"> - <a href="<?php echo _url('stats', 'repartition'); ?>"><?php echo _t('admin.stats.menu.repartition'); ?></a> + <li class="item<?= Minz_Request::actionName() == 'repartition' ? ' active' : '' ?>"> + <a href="<?= _url('stats', 'repartition') ?>"><?= _t('admin.stats.menu.repartition') ?></a> </li> </ul> diff --git a/app/layout/aside_subscription.phtml b/app/layout/aside_subscription.phtml index e6a378837..fa1c1aa2d 100644 --- a/app/layout/aside_subscription.phtml +++ b/app/layout/aside_subscription.phtml @@ -1,15 +1,15 @@ <ul class="nav nav-list aside"> - <li class="nav-header"><?php echo _t('sub.menu.subscription_management'); ?></li> + <li class="nav-header"><?= _t('sub.menu.subscription_management') ?></li> - <li class="item<?php echo Minz_Request::controllerName() === 'subscription' && Minz_Request::actionName() !== 'bookmarklet' ? ' active' : ''; ?>"> - <a href="<?php echo _url('subscription', 'index'); ?>"><?php echo _t('sub.menu.subscription_management'); ?></a> + <li class="item<?= Minz_Request::controllerName() === 'subscription' && Minz_Request::actionName() !== 'bookmarklet' ? ' active' : '' ?>"> + <a href="<?= _url('subscription', 'index') ?>"><?= _t('sub.menu.subscription_management') ?></a> </li> - <li class="item<?php echo Minz_Request::controllerName() === 'importExport' ? ' active' : ''; ?>"> - <a href="<?php echo _url('importExport', 'index'); ?>"><?php echo _t('sub.menu.import_export'); ?></a> + <li class="item<?= Minz_Request::controllerName() === 'importExport' ? ' active' : '' ?>"> + <a href="<?= _url('importExport', 'index') ?>"><?= _t('sub.menu.import_export') ?></a> </li> - <li class="item<?php echo Minz_Request::controllerName() === 'subscription' && Minz_Request::actionName() === 'bookmarklet' ? ' active' : ''; ?>"> - <a href="<?php echo _url('subscription', 'bookmarklet'); ?>"><?php echo _t('sub.menu.subscription_tools'); ?></a> + <li class="item<?= Minz_Request::controllerName() === 'subscription' && Minz_Request::actionName() === 'bookmarklet' ? ' active' : '' ?>"> + <a href="<?= _url('subscription', 'bookmarklet') ?>"><?= _t('sub.menu.subscription_tools') ?></a> </li> </ul> diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 410ac1ff0..3f7bd80e3 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -3,10 +3,10 @@ if (FreshRSS_Auth::accessNeedsAction()) { ?><ul class="nav nav-head nav-login"><?php if (FreshRSS_Auth::hasAccess()) { - ?><li class="item"><?php echo _i('logout'); ?> <a class="signout" href="<?php echo _url('auth', 'logout'); ?>"><?php + ?><li class="item"><?= _i('logout') ?> <a class="signout" href="<?= _url('auth', 'logout') ?>"><?php echo _t('gen.auth.logout') . ' (' . htmlspecialchars(Minz_Session::param('currentUser', '_'), ENT_NOQUOTES, 'UTF-8') . ')'; ?></a></li><?php } else { - ?><li class="item"><?php echo _i('login'); ?> <a class="signin" href="<?php echo _url('auth', 'login'); ?>"><?php echo _t('gen.auth.login'); ?></a></li><?php + ?><li class="item"><?= _i('login') ?> <a class="signin" href="<?= _url('auth', 'login') ?>"><?= _t('gen.auth.login') ?></a></li><?php } ?></ul><?php } @@ -15,36 +15,36 @@ if (FreshRSS_Auth::accessNeedsAction()) { <div class="header"> <div class="item title"> <h1> - <a href="<?php echo _url('index', 'index'); ?>"> - <img class="logo" src="<?php echo _i('icon', true); ?>" alt="" /> - <?php echo FreshRSS_Context::$system_conf->title; ?> + <a href="<?= _url('index', 'index') ?>"> + <img class="logo" src="<?= _i('icon', true) ?>" alt="" /> + <?= FreshRSS_Context::$system_conf->title ?> </a> </h1> </div> <div class="item search"> <?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous) { ?> - <form action="<?php echo _url('index', 'index'); ?>" method="get"> + <form action="<?= _url('index', 'index') ?>" method="get"> <div class="stick"> <input type="search" name="search" id="search" class="extend" value="<?php - echo htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>" placeholder="<?php echo _t('gen.menu.search'); ?>" /> + echo htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>" placeholder="<?= _t('gen.menu.search') ?>" /> <?php $get = Minz_Request::param('get', ''); ?> <?php if ($get != '') { ?> - <input type="hidden" name="get" value="<?php echo $get; ?>" /> + <input type="hidden" name="get" value="<?= $get ?>" /> <?php } ?> <?php $order = Minz_Request::param('order', ''); ?> <?php if ($order != '') { ?> - <input type="hidden" name="order" value="<?php echo $order; ?>" /> + <input type="hidden" name="order" value="<?= $order ?>" /> <?php } ?> <?php $state = Minz_Request::param('state', ''); ?> <?php if ($state != '') { ?> - <input type="hidden" name="state" value="<?php echo $state; ?>" /> + <input type="hidden" name="state" value="<?= $state ?>" /> <?php } ?> - <button class="btn" type="submit"><?php echo _i('search'); ?></button> + <button class="btn" type="submit"><?= _i('search') ?></button> </div> </form> <?php } ?> @@ -54,46 +54,52 @@ if (FreshRSS_Auth::accessNeedsAction()) { <div class="item configure"> <div class="dropdown"> <div id="dropdown-configure" class="dropdown-target"></div> - <a class="btn dropdown-toggle" href="#dropdown-configure"><?php echo _i('configure'); ?></a> + <a class="btn dropdown-toggle" href="#dropdown-configure"><?= _i('configure') ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> - <li class="dropdown-header"><?php echo _t('gen.menu.configuration'); ?></li> - <li class="item"><a href="<?php echo _url('configure', 'display'); ?>"><?php echo _t('gen.menu.display'); ?></a></li> - <li class="item"><a href="<?php echo _url('configure', 'reading'); ?>"><?php echo _t('gen.menu.reading'); ?></a></li> - <li class="item"><a href="<?php echo _url('configure', 'archiving'); ?>"><?php echo _t('gen.menu.archiving'); ?></a></li> - <li class="item"><a href="<?php echo _url('configure', 'sharing'); ?>"><?php echo _t('gen.menu.sharing'); ?></a></li> - <li class="item"><a href="<?php echo _url('configure', 'shortcut'); ?>"><?php echo _t('gen.menu.shortcuts'); ?></a></li> - <li class="item"><a href="<?php echo _url('configure', 'queries'); ?>"><?php echo _t('gen.menu.queries'); ?></a></li> - <li class="item"><a href="<?php echo _url('user', 'profile'); ?>"><?php echo _t('gen.menu.user_profile'); ?></a></li> - <li class="item"><a href="<?php echo _url('extension', 'index'); ?>"><?php echo _t('gen.menu.extensions'); ?></a></li> + <li class="dropdown-header"><?= _t('gen.menu.configuration') ?></li> + <li class="item"><a href="<?= _url('configure', 'display') ?>"><?= _t('gen.menu.display') ?></a></li> + <li class="item"><a href="<?= _url('configure', 'reading') ?>"><?= _t('gen.menu.reading') ?></a></li> + <li class="item"><a href="<?= _url('configure', 'archiving') ?>"><?= _t('gen.menu.archiving') ?></a></li> + <li class="item"><a href="<?= _url('configure', 'sharing') ?>"><?= _t('gen.menu.sharing') ?></a></li> + <li class="item"><a href="<?= _url('configure', 'shortcut') ?>"><?= _t('gen.menu.shortcuts') ?></a></li> + <li class="item"><a href="<?= _url('configure', 'queries') ?>"><?= _t('gen.menu.queries') ?></a></li> + <li class="item"><a href="<?= _url('user', 'profile') ?>"><?= _t('gen.menu.user_profile') ?></a></li> + <li class="item"><a href="<?= _url('extension', 'index') ?>"><?= _t('gen.menu.extensions') ?></a></li> + <?= Minz_ExtensionManager::callHook('menu_configuration_entry') ?> + <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> <li class="separator"></li> - <li class="dropdown-header"><?php echo _t('gen.menu.admin'); ?></li> - <li class="item"><a href="<?php echo _url('configure', 'system'); ?>"><?php echo _t('gen.menu.system'); ?></a></li> - <li class="item"><a href="<?php echo _url('user', 'manage'); ?>"><?php echo _t('gen.menu.user_management'); ?></a></li> - <li class="item"><a href="<?php echo _url('auth', 'index'); ?>"><?php echo _t('gen.menu.authentication'); ?></a></li> - <li class="item"><a href="<?php echo _url('update', 'checkInstall'); ?>"><?php echo _t('gen.menu.check_install'); ?></a></li> + <li class="dropdown-header"><?= _t('gen.menu.admin') ?></li> + <li class="item"><a href="<?= _url('configure', 'system') ?>"><?= _t('gen.menu.system') ?></a></li> + <li class="item"><a href="<?= _url('user', 'manage') ?>"><?= _t('gen.menu.user_management') ?></a></li> + <li class="item"><a href="<?= _url('auth', 'index') ?>"><?= _t('gen.menu.authentication') ?></a></li> + <li class="item"><a href="<?= _url('update', 'checkInstall') ?>"><?= _t('gen.menu.check_install') ?></a></li> <?php if (!Minz_Configuration::get('system')->disable_update) { ?> - <li class="item"><a href="<?php echo _url('update', 'index'); ?>"><?php echo _t('gen.menu.update'); ?></a></li> + <li class="item"><a href="<?= _url('update', 'index') ?>"><?= _t('gen.menu.update') ?></a></li> <?php } ?> + <?= Minz_ExtensionManager::callHook('menu_admin_entry') ?> <?php } ?> + <li class="separator"></li> - <li class="item"><a href="<?php echo _url('stats', 'index'); ?>"><?php echo _t('gen.menu.stats'); ?></a></li> - <li class="item"><a href="<?php echo _url('index', 'logs'); ?>"><?php echo _t('gen.menu.logs'); ?></a></li> - <li class="item"><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.menu.about'); ?></a></li> + <li class="item"><a href="<?= _url('stats', 'index') ?>"><?= _t('gen.menu.stats') ?></a></li> + <li class="item"><a href="<?= _url('index', 'logs') ?>"><?= _t('gen.menu.logs') ?></a></li> + <li class="item"><a href="<?= _url('index', 'about') ?>"><?= _t('gen.menu.about') ?></a></li> + <?= Minz_ExtensionManager::callHook('menu_other_entry') ?> + <li class="separator"></li> <?php if (FreshRSS_Auth::accessNeedsAction()): ?> - <li class="item"><a class="signout" href="<?php echo _url('auth', 'logout'); ?>"><?php + <li class="item"><a class="signout" href="<?= _url('auth', 'logout') ?>"><?php echo _i('logout') . ' ' . _t('gen.auth.logout') . ' (' . htmlspecialchars(Minz_Session::param('currentUser', '_'), ENT_NOQUOTES, 'UTF-8') . ')'; ?></a></li> <?php else: ?> - <li class="item"><span class="signout">(<?php echo htmlspecialchars(Minz_Session::param('currentUser', '_'), ENT_NOQUOTES, 'UTF-8'); ?>)</span></li> + <li class="item"><span class="signout">(<?= htmlspecialchars(Minz_Session::param('currentUser', '_'), ENT_NOQUOTES, 'UTF-8') ?>)</span></li> <?php endif; ?> </ul> </div> </div> <?php } elseif (FreshRSS_Auth::accessNeedsAction()) { ?> <div class="item configure"> - <?php echo _i('login'); ?><a class="signin" href="<?php echo _url('auth', 'login'); ?>"><?php echo _t('gen.auth.login'); ?></a> + <?= _i('login') ?><a class="signin" href="<?= _url('auth', 'login') ?>"><?= _t('gen.auth.login') ?></a> </div> <?php } ?> </div> diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml index 2e16672e6..498cc4470 100644 --- a/app/layout/layout.phtml +++ b/app/layout/layout.phtml @@ -1,25 +1,25 @@ <?php FreshRSS::preLayout(); ?> <!DOCTYPE html> -<html lang="<?php echo FreshRSS_Context::$user_conf->language; ?>" xml:lang="<?php echo FreshRSS_Context::$user_conf->language; ?>"> +<html lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="initial-scale=1.0" /> - <?php echo self::headStyle(); ?> + <?= self::headStyle() ?> <script id="jsonVars" type="application/json"> <?php $this->renderHelper('javascript_vars'); ?> </script> - <?php echo self::headScript(); ?> - <link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="<?php echo Minz_Url::display('/favicon.ico'); ?>" /> - <link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="<?php echo Minz_Url::display('/themes/icons/favicon-256.png'); ?>" /> - <link rel="apple-touch-icon" href="<?php echo Minz_Url::display('/themes/icons/apple-touch-icon.png'); ?>" /> + <?= self::headScript() ?> + <link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="<?= Minz_Url::display('/favicon.ico') ?>" /> + <link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="<?= Minz_Url::display('/themes/icons/favicon-256.png') ?>" /> + <link rel="apple-touch-icon" href="<?= Minz_Url::display('/themes/icons/apple-touch-icon.png') ?>" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" /> - <meta name="apple-mobile-web-app-title" content="<?php echo FreshRSS_Context::$system_conf->title; ?>"> + <meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::$system_conf->title ?>"> <meta name="msapplication-TileColor" content="#FFF" /> <?php if (!FreshRSS_Context::$system_conf->allow_referrer) { ?> <meta name="referrer" content="never" /> <?php } ?> - <?php echo self::headTitle(); ?> + <?= self::headTitle() ?> <?php $url_base = Minz_Request::currentRequest(); if (isset($this->rss_title)) { @@ -29,14 +29,14 @@ $url_rss['params']['hours'] = FreshRSS_Context::$user_conf->since_hours_posts_per_rss; } ?> - <link rel="alternate" type="application/rss+xml" title="<?php echo $this->rss_title; ?>" href="<?php echo Minz_Url::display($url_rss); ?>" /> + <link rel="alternate" type="application/rss+xml" title="<?= $this->rss_title ?>" href="<?= Minz_Url::display($url_rss) ?>" /> <?php } if (FreshRSS_Context::$system_conf->allow_robots) { ?> - <meta name="description" content="<?php echo htmlspecialchars(FreshRSS_Context::$name . ' | ' . FreshRSS_Context::$description, ENT_COMPAT, 'UTF-8'); ?>" /> + <meta name="description" content="<?= htmlspecialchars(FreshRSS_Context::$name . ' | ' . FreshRSS_Context::$description, ENT_COMPAT, 'UTF-8') ?>" /> <?php } else { ?> <meta name="robots" content="noindex,nofollow" /> <?php } ?> </head> - <body class="<?php echo Minz_Request::actionName(); ?>"> + <body class="<?= Minz_Request::actionName() ?>"> <?php flush(); $this->partial('header'); @@ -62,9 +62,9 @@ invalidateHttpCache(); } ?> -<div id="notification" class="notification <?php echo $status; ?>"> - <span class="msg"><?php echo $msg; ?></span> - <a class="close" href=""><?php echo _i('close'); ?></a> +<div id="notification" class="notification <?= $status ?>"> + <span class="msg"><?= $msg ?></span> + <a class="close" href=""><?= _i('close') ?></a> </div> </body> </html> diff --git a/app/layout/nav_entries.phtml b/app/layout/nav_entries.phtml index ca6849193..cbc514737 100644 --- a/app/layout/nav_entries.phtml +++ b/app/layout/nav_entries.phtml @@ -1,5 +1,5 @@ <ul id="nav_entries"> - <li class="item"><a class="previous_entry" href="#"><?php echo _i('prev'); ?></a></li> - <li class="item"><a class="up" href="#"><?php echo _i('up'); ?></a></li> - <li class="item"><a class="next_entry" href="#"><?php echo _i('next'); ?></a></li> + <li class="item"><a class="previous_entry" href="#"><?= _i('prev') ?></a></li> + <li class="item"><a class="up" href="#"><?= _i('up') ?></a></li> + <li class="item"><a class="next_entry" href="#"><?= _i('next') ?></a></li> </ul>
\ No newline at end of file diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index af7267bac..da33d3e20 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -9,7 +9,7 @@ <div class="nav_menu"> <?php if ($actual_view === 'normal' || $actual_view === 'reader' ) { ?> - <a class="btn toggle_aside" href="#aside_feed"><?php echo _i('category'); ?></a> + <a class="btn toggle_aside" href="#aside_feed"><?= _i('category') ?></a> <?php } ?> <?php if (FreshRSS_Auth::hasAccess()) { ?> @@ -27,28 +27,28 @@ $url_state = Minz_Request::currentRequest(); $url_state['params']['state'] = FreshRSS_Context::getRevertState($state); ?> - <a id="toggle-<?php echo $state_str; ?>" - class="btn <?php echo $state_enabled ? 'active' : ''; ?>" - role="checkbox" aria-checked="<?php echo $state_enabled ? 'true' : 'false'; ?>" - title="<?php echo _t('index.menu.' . $state_str); ?>" - href="<?php echo Minz_Url::display($url_state); ?>"><?php echo _i($state_str); ?></a> + <a id="toggle-<?= $state_str ?>" + class="btn <?= $state_enabled ? 'active' : '' ?>" + role="checkbox" aria-checked="<?= $state_enabled ? 'true' : 'false' ?>" + title="<?= _t('index.menu.' . $state_str) ?>" + href="<?= Minz_Url::display($url_state) ?>"><?= _i($state_str) ?></a> <?php } ?> <div class="dropdown"> <div id="dropdown-query" class="dropdown-target"></div> - <a class="dropdown-toggle btn" href="#dropdown-query"><?php echo _i('down'); ?></a> + <a class="dropdown-toggle btn" href="#dropdown-query"><?= _i('down') ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> <li class="dropdown-header"> - <?php echo _t('index.menu.queries'); ?> - <a class="no-mobile" href="<?php echo _url('configure', 'queries'); ?>"><?php echo _i('configure'); ?></a> + <?= _t('index.menu.queries') ?> + <a class="no-mobile" href="<?= _url('configure', 'queries') ?>"><?= _i('configure') ?></a> </li> <?php foreach (FreshRSS_Context::$user_conf->queries as $query) { ?> <li class="item query"> - <a href="<?php echo $query['url']; ?>"><?php echo $query['name']; ?></a> + <a href="<?= $query['url'] ?>"><?= $query['name'] ?></a> </li> <?php } ?> @@ -61,7 +61,7 @@ $url_query['c'] = 'configure'; $url_query['a'] = 'addQuery'; ?> - <li class="item no-mobile"><a href="<?php echo Minz_Url::display($url_query); ?>"><?php echo _i('bookmark-add'); ?> <?php echo _t('index.menu.add_query'); ?></a></li> + <li class="item no-mobile"><a href="<?= Minz_Url::display($url_query) ?>"><?= _i('bookmark-add') ?> <?= _t('index.menu.add_query') ?></a></li> </ul> </div> </div> @@ -96,24 +96,24 @@ <div class="stick" id="nav_menu_read_all"> <form id="mark-read-menu" method="post"> <?php $confirm = FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : ''; ?> - <button class="read_all btn <?php echo $confirm; ?>" + <button class="read_all btn <?= $confirm ?>" form="mark-read-menu" - formaction="<?php echo Minz_Url::display($mark_read_url); ?>" - type="submit"><?php echo _t('gen.action.mark_read'); ?></button> + formaction="<?= Minz_Url::display($mark_read_url) ?>" + type="submit"><?= _t('gen.action.mark_read') ?></button> <div class="dropdown"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <div id="dropdown-read" class="dropdown-target"></div> - <a class="dropdown-toggle btn" href="#dropdown-read"><?php echo _i('down'); ?></a> + <a class="dropdown-toggle btn" href="#dropdown-read"><?= _i('down') ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> <li class="item"> - <button class="as-link <?php echo $confirm; ?>" + <button class="as-link <?= $confirm ?>" form="mark-read-menu" - formaction="<?php echo Minz_Url::display($mark_read_url); ?>" - type="submit"><?php echo $string_mark; ?></button> + formaction="<?= Minz_Url::display($mark_read_url) ?>" + type="submit"><?= $string_mark ?></button> </li> <li class="separator"></li> <?php @@ -125,23 +125,23 @@ $mark_unread_enabled = FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_READ) or !FreshRSS_Context::isStateEnabled(FreshRSS_Entry::STATE_NOT_READ); ?> <li class="item"> - <button class="as-link <?php echo $confirm; ?>" + <button class="as-link <?= $confirm ?>" form="mark-read-menu" - formaction="<?php echo Minz_Url::display($mark_before_today); ?>" - type="submit"><?php echo _t('index.menu.before_one_day'); ?></button> + formaction="<?= Minz_Url::display($mark_before_today) ?>" + type="submit"><?= _t('index.menu.before_one_day') ?></button> </li> <li class="item"> - <button class="as-link <?php echo $confirm; ?>" + <button class="as-link <?= $confirm ?>" form="mark-read-menu" - formaction="<?php echo Minz_Url::display($mark_before_one_week); ?>" - type="submit"><?php echo _t('index.menu.before_one_week'); ?></button> + formaction="<?= Minz_Url::display($mark_before_one_week) ?>" + type="submit"><?= _t('index.menu.before_one_week') ?></button> </li> <li class="separator"></li> <li class="item"> - <button class="as-link <?php echo $mark_unread_enabled ? $confirm : '" disabled="disabled'; ?>" + <button class="as-link <?= $mark_unread_enabled ? $confirm : '" disabled="disabled' ?>" form="mark-read-menu" - formaction="<?php echo Minz_Url::display($mark_unread_url); ?>" - type="submit"><?php echo $string_unmark; ?></button> + formaction="<?= Minz_Url::display($mark_unread_url) ?>" + type="submit"><?= $string_unmark ?></button> </li> </ul> </div> @@ -158,8 +158,8 @@ /** @var FreshRSS_ReadingMode $mode */ foreach ($readingModes as $mode) { ?> - <a class="<?php echo $mode->getId(); ?> btn <?php if ($mode->isActive()) { echo 'active'; } ?>" title="<?php echo $mode->getTitle(); ?>" href="<?php echo Minz_Url::display($mode->getUrlParams()); ?>"> - <?php echo $mode->getName(); ?> + <a class="<?= $mode->getId() ?> btn <?php if ($mode->isActive()) { echo 'active'; } ?>" title="<?= $mode->getTitle() ?>" href="<?= Minz_Url::display($mode->getUrlParams()) ?>"> + <?= $mode->getName() ?> </a> <?php } @@ -175,29 +175,29 @@ $url_output['params']['hours'] = FreshRSS_Context::$user_conf->since_hours_posts_per_rss; } ?> - <a class="view-rss btn" target="_blank" rel="noreferrer" title="<?php echo _t('index.menu.rss_view'); ?>" href="<?php echo Minz_Url::display($url_output); ?>"> - <?php echo _i('rss'); ?> + <a class="view-rss btn" target="_blank" rel="noreferrer" title="<?= _t('index.menu.rss_view') ?>" href="<?= Minz_Url::display($url_output) ?>"> + <?= _i('rss') ?> </a> </div> <div class="item search"> - <form action="<?php echo _url('index', 'index'); ?>" method="get"> + <form action="<?= _url('index', 'index') ?>" method="get"> <input type="search" name="search" class="extend" value="<?php - echo htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>" placeholder="<?php echo _t('index.menu.search_short'); ?>" /> + echo htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search, ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>" placeholder="<?= _t('index.menu.search_short') ?>" /> <?php $get = Minz_Request::param('get', ''); ?> <?php if($get != '') { ?> - <input type="hidden" name="get" value="<?php echo $get; ?>" /> + <input type="hidden" name="get" value="<?= $get ?>" /> <?php } ?> <?php $order = Minz_Request::param('order', ''); ?> <?php if($order != '') { ?> - <input type="hidden" name="order" value="<?php echo $order; ?>" /> + <input type="hidden" name="order" value="<?= $order ?>" /> <?php } ?> <?php $state = Minz_Request::param('state', ''); ?> <?php if($state != '') { ?> - <input type="hidden" name="state" value="<?php echo $state; ?>" /> + <input type="hidden" name="state" value="<?= $state ?>" /> <?php } ?> </form> </div> @@ -215,11 +215,11 @@ $url_order = Minz_Request::currentRequest(); $url_order['params']['order'] = $order; ?> - <a id="toggle-order" class="btn" href="<?php echo Minz_Url::display($url_order); ?>" title="<?php echo $title; ?>"> - <?php echo _i($icon); ?> + <a id="toggle-order" class="btn" href="<?= Minz_Url::display($url_order) ?>" title="<?= $title ?>"> + <?= _i($icon) ?> </a> <?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::$system_conf->allow_anonymous_refresh) { ?> - <a id="actualize" class="btn" href="<?php echo _url('feed', 'actualize'); ?>" title="<?php echo _t('gen.action.actualize'); ?>"><?php echo _i('refresh'); ?></a> + <a id="actualize" class="btn" href="<?= _url('feed', 'actualize') ?>" title="<?= _t('gen.action.actualize') ?>"><?= _i('refresh') ?></a> <?php } ?> </div> diff --git a/app/layout/simple.phtml b/app/layout/simple.phtml new file mode 100644 index 000000000..89fe69005 --- /dev/null +++ b/app/layout/simple.phtml @@ -0,0 +1,69 @@ +<?php FreshRSS::preLayout(); ?> +<!DOCTYPE html> +<html lang="<?= FreshRSS_Context::$user_conf->language ?>" xml:lang="<?= FreshRSS_Context::$user_conf->language ?>"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="initial-scale=1.0" /> + <?= self::headStyle() ?> + <script id="jsonVars" type="application/json"> +<?php $this->renderHelper('javascript_vars'); ?> + </script> + <?= self::headScript() ?> + <link rel="shortcut icon" id="favicon" type="image/x-icon" sizes="16x16 64x64" href="<?= Minz_Url::display('/favicon.ico') ?>" /> + <link rel="icon msapplication-TileImage apple-touch-icon" type="image/png" sizes="256x256" href="<?= Minz_Url::display('/themes/icons/favicon-256.png') ?>" /> + <link rel="apple-touch-icon" href="<?= Minz_Url::display('/themes/icons/apple-touch-icon.png') ?>" /> + <meta name="apple-mobile-web-app-capable" content="yes" /> + <meta name="apple-mobile-web-app-status-bar-style" content="black" /> + <meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::$system_conf->title ?>"> + <meta name="msapplication-TileColor" content="#FFF" /> + <meta name="referrer" content="never" /> + <meta name="robots" content="noindex,nofollow" /> + <?= self::headTitle() ?> + </head> + <body> + +<?php flush(); ?> +<div class="app-layout app-layout-simple"> + <div class="header"> + <div class="item title"> + <h1> + <a href="<?= _url('index', 'index') ?>"> + <img class="logo" src="<?= _i('icon', true) ?>" alt="" /> + <?= FreshRSS_Context::$system_conf->title ?> + </a> + </h1> + </div> + + <div class="item"></div> + + <div class="item"> + <?php if (FreshRSS_Auth::accessNeedsAction()) { ?> + <a class="signout" href="<?= _url('auth', 'logout') ?>"> + <?= _i('logout') . _t('gen.auth.logout') ?> + + (<?= htmlspecialchars(Minz_Session::param('currentUser', '_'), ENT_NOQUOTES, 'UTF-8') ?>) + </a> + <?php } ?> + </div> + </div> + + <?php $this->render(); ?> +</div> + +<?php + $msg = ''; + $status = 'closed'; + if (isset($this->notification)) { + $msg = $this->notification['content']; + $status = $this->notification['type']; + + invalidateHttpCache(); + } +?> +<div id="notification" class="notification <?= $status ?>"> + <span class="msg"><?= $msg ?></span> + <a class="close" href=""><?= _i('close') ?></a> +</div> + + </body> +</html> diff --git a/app/shares.php b/app/shares.php index 4f7fde3ed..9df83617a 100644 --- a/app/shares.php +++ b/app/shares.php @@ -75,12 +75,6 @@ return array( 'form' => 'simple', 'method' => 'GET', ), - 'g+' => array( - 'url' => 'https://plus.google.com/share?url=~LINK~', - 'transform' => array('rawurlencode'), - 'form' => 'simple', - 'method' => 'GET', - ), 'facebook' => array( 'url' => 'https://www.facebook.com/sharer.php?u=~LINK~&t=~TITLE~', 'transform' => array('rawurlencode'), @@ -144,4 +138,10 @@ return array( 'form' => 'simple', 'method' => 'GET', ), + 'lemmy' => array( + 'url' => '~URL~/create_post?url=~LINK~&name=~TITLE~', + 'transform' => array('rawurlencode'), + 'form' => 'advanced', + 'method' => 'GET', + ), ); diff --git a/app/views/auth/formLogin.phtml b/app/views/auth/formLogin.phtml index 01d1d4736..ecac7aced 100644 --- a/app/views/auth/formLogin.phtml +++ b/app/views/auth/formLogin.phtml @@ -1,33 +1,33 @@ <div class="prompt"> - <h1><?php echo _t('gen.auth.login'); ?></h1> + <h1><?= _t('gen.auth.login') ?></h1> <?php if (!max_registrations_reached()) { ?> - <a href="<?php echo _url('auth', 'register'); ?>"><?php echo _t('gen.auth.registration.ask'); ?></a> + <a href="<?= _url('auth', 'register') ?>"><?= _t('gen.auth.registration.ask') ?></a> <?php } ?> - <form id="crypto-form" method="post" action="<?php echo _url('auth', 'login'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <form id="crypto-form" method="post" action="<?= _url('auth', 'login') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <div> - <label for="username"><?php echo _t('gen.auth.username'); ?></label> - <input type="text" id="username" name="username" autocomplete="username" size="16" required="required" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" autofocus="autofocus" /> + <label for="username"><?= _t('gen.auth.username') ?></label> + <input type="text" id="username" name="username" autocomplete="username" size="16" required="required" pattern="<?= FreshRSS_user_Controller::USERNAME_PATTERN ?>" autofocus="autofocus" /> </div> <div> - <label for="passwordPlain"><?php echo _t('gen.auth.password'); ?></label> + <label for="passwordPlain"><?= _t('gen.auth.password') ?></label> <input type="password" id="passwordPlain" required="required" /> <input type="hidden" id="challenge" name="challenge" /><br /> - <noscript><strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <noscript><strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </div> <div> <label class="checkbox" for="keep_logged_in"> <input type="checkbox" name="keep_logged_in" id="keep_logged_in" value="1" /> - <?php echo _t('gen.auth.keep_logged_in', $this->cookie_days); ?> + <?= _t('gen.auth.keep_logged_in', $this->cookie_days) ?> </label> <br /> </div> <div> - <button id="loginButton" type="submit" class="btn btn-important"><?php echo _t('gen.auth.login'); ?></button> + <button id="loginButton" type="submit" class="btn btn-important"><?= _t('gen.auth.login') ?></button> </div> </form> - <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.freshrss.about'); ?></a></p> + <p><a href="<?= _url('index', 'about') ?>"><?= _t('gen.freshrss.about') ?></a></p> </div> diff --git a/app/views/auth/index.phtml b/app/views/auth/index.phtml index 20966f24e..c1cf95b2b 100644 --- a/app/views/auth/index.phtml +++ b/app/views/auth/index.phtml @@ -1,22 +1,22 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('auth', 'index'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('admin.auth.type'); ?></legend> + <form method="post" action="<?= _url('auth', 'index') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('admin.auth.type') ?></legend> <div class="form-group"> - <label class="group-name" for="auth_type"><?php echo _t('admin.auth.type'); ?></label> + <label class="group-name" for="auth_type"><?= _t('admin.auth.type') ?></label> <div class="group-controls"> - <select id="auth_type" name="auth_type" required="required" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->auth_type; ?>"> + <select id="auth_type" name="auth_type" required="required" data-leave-validation="<?= FreshRSS_Context::$system_conf->auth_type ?>"> <?php if (!in_array(FreshRSS_Context::$system_conf->auth_type, array('form', 'http_auth', 'none'))) { ?> <option selected="selected"></option> <?php } ?> - <option value="form"<?php echo FreshRSS_Context::$system_conf->auth_type === 'form' ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"'; ?>><?php echo _t('admin.auth.form'); ?></option> - <option value="http_auth"<?php echo FreshRSS_Context::$system_conf->auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : ''; ?>><?php echo _t('admin.auth.http'); ?> (REMOTE_USER = '<?php echo httpAuthUser(); ?>')</option> - <option value="none"<?php echo FreshRSS_Context::$system_conf->auth_type === 'none' ? ' selected="selected"' : ''; ?>><?php echo _t('admin.auth.none'); ?></option> + <option value="form"<?= FreshRSS_Context::$system_conf->auth_type === 'form' ? ' selected="selected"' : '', cryptAvailable() ? '' : ' disabled="disabled"' ?>><?= _t('admin.auth.form') ?></option> + <option value="http_auth"<?= FreshRSS_Context::$system_conf->auth_type === 'http_auth' ? ' selected="selected"' : '', httpAuthUser() == '' ? ' disabled="disabled"' : '' ?>><?= _t('admin.auth.http') ?> (REMOTE_USER = '<?= httpAuthUser() ?>')</option> + <option value="none"<?= FreshRSS_Context::$system_conf->auth_type === 'none' ? ' selected="selected"' : '' ?>><?= _t('admin.auth.none') ?></option> </select> </div> </div> @@ -25,8 +25,8 @@ <div class="group-controls"> <label class="checkbox" for="anon_access"> <input type="checkbox" name="anon_access" id="anon_access" value="1"<?php echo FreshRSS_Context::$system_conf->allow_anonymous ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?php echo FreshRSS_Context::$system_conf->allow_anonymous; ?>"/> - <?php echo _t('admin.auth.allow_anonymous', FreshRSS_Context::$system_conf->default_user); ?> + FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->allow_anonymous ?>"/> + <?= _t('admin.auth.allow_anonymous', FreshRSS_Context::$system_conf->default_user) ?> </label> </div> </div> @@ -35,8 +35,8 @@ <div class="group-controls"> <label class="checkbox" for="anon_refresh"> <input type="checkbox" name="anon_refresh" id="anon_refresh" value="1"<?php echo FreshRSS_Context::$system_conf->allow_anonymous_refresh ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?php echo FreshRSS_Context::$system_conf->allow_anonymous_refresh; ?>"/> - <?php echo _t('admin.auth.allow_anonymous_refresh'); ?> + FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->allow_anonymous_refresh ?>"/> + <?= _t('admin.auth.allow_anonymous_refresh') ?> </label> </div> </div> @@ -45,9 +45,9 @@ <div class="group-controls"> <label class="checkbox" for="unsafe_autologin"> <input type="checkbox" name="unsafe_autologin" id="unsafe_autologin" value="1"<?php echo FreshRSS_Context::$system_conf->unsafe_autologin_enabled ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?php echo FreshRSS_Context::$system_conf->unsafe_autologin_enabled; ?>"/> - <?php echo _t('admin.auth.unsafe_autologin'); ?> - <kbd><?php echo Minz_Url::display(array('c' => 'auth', 'a' => 'login', 'params' => array('u' => 'alice', 'p' => '1234')), 'html', true); ?></kbd> + FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->unsafe_autologin_enabled ?>"/> + <?= _t('admin.auth.unsafe_autologin') ?> + <kbd><?= Minz_Url::display(array('c' => 'auth', 'a' => 'login', 'params' => array('u' => 'alice', 'p' => '1234')), 'html', true) ?></kbd> </label> </div> </div> @@ -56,16 +56,16 @@ <div class="group-controls"> <label class="checkbox" for="api_enabled"> <input type="checkbox" name="api_enabled" id="api_enabled" value="1"<?php echo FreshRSS_Context::$system_conf->api_enabled ? ' checked="checked"' : '', - FreshRSS_Auth::accessNeedsLogin() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?php echo FreshRSS_Context::$system_conf->api_enabled; ?>"/> - <?php echo _t('admin.auth.api_enabled'); ?> + FreshRSS_Auth::accessNeedsLogin() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?= FreshRSS_Context::$system_conf->api_enabled ?>"/> + <?= _t('admin.auth.api_enabled') ?> </label> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/auth/register.phtml b/app/views/auth/register.phtml index 19e11ef76..4233f7fd4 100644 --- a/app/views/auth/register.phtml +++ b/app/views/auth/register.phtml @@ -1,22 +1,40 @@ <div class="prompt"> - <h1><?php echo _t('gen.auth.registration'); ?></h1> + <h1><?= _t('gen.auth.registration') ?></h1> - <form method="post" action="<?php echo _url('user', 'create'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <form method="post" action="<?= _url('user', 'create') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <div> - <label class="group-name" for="new_user_name"><?php echo _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format'); ?></label> - <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" autocomplete="off" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" /> + <label class="group-name" for="new_user_name"><?= _t('gen.auth.username'), '<br />', _i('help'), ' ', _t('gen.auth.username.format') ?></label> + <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" autocomplete="off" pattern="<?= FreshRSS_user_Controller::USERNAME_PATTERN ?>" /> </div> + <?php if ($this->show_email_field) { ?> + <div> + <label class="group-name" for="new_user_email"> + <?= _t('gen.auth.email') ?> + </label> + <input id="new_user_email" name="new_user_email" type="email" required /> + </div> + <?php } ?> + <div> - <label class="group-name" for="new_user_passwordPlain"><?php echo _t('gen.auth.password'), '<br />', _i('help'), ' ', _t('gen.auth.password.format'); ?></label> + <label class="group-name" for="new_user_passwordPlain"><?= _t('gen.auth.password'), '<br />', _i('help'), ' ', _t('gen.auth.password.format') ?></label> <div class="stick"> <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" required="required" autocomplete="new-password" pattern=".{7,}" /> - <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a> + <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?= _i('key') ?></a> </div> - <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> + <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> </div> + <?php if ($this->show_tos_checkbox) { ?> + <div> + <label class="checkbox" for="accept-tos"> + <input type="checkbox" name="accept_tos" id="accept-tos" value="1" required /> + <?= _t('gen.auth.accept_tos', _url('index', 'tos')) ?> + </label> + </div> + <?php } ?> + <div> <?php $redirect_url = urlencode(Minz_Url::display( @@ -24,11 +42,11 @@ 'php', true )); ?> - <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" /> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.create'); ?></button> - <a class="btn" href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.cancel'); ?></a> + <input type="hidden" name="r" value="<?= $redirect_url ?>" /> + <button type="submit" class="btn btn-important"><?= _t('gen.action.create') ?></button> + <a class="btn" href="<?= _url('index', 'index') ?>"><?= _t('gen.action.cancel') ?></a> </div> </form> - <p><a href="<?php echo _url('index', 'about'); ?>"><?php echo _t('gen.freshrss.about'); ?></a></p> + <p><a href="<?= _url('index', 'about') ?>"><?= _t('gen.freshrss.about') ?></a></p> </div> diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index 09be55fd9..7d76e4dcc 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -1,34 +1,17 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('configure', 'archiving'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.archiving'); ?></legend> - <p><?php echo _i('help'); ?> <?php echo _t('conf.archiving.help'); ?></p> + <form method="post" action="<?= _url('configure', 'archiving') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.archiving') ?></legend> + <p><?= _i('help') ?> <?= _t('conf.archiving.help') ?></p> <div class="form-group"> - <label class="group-name" for="old_entries"><?php echo _t('conf.archiving.delete_after'); ?></label> + <label class="group-name" for="ttl_default"><?= _t('conf.archiving.ttl') ?></label> <div class="group-controls"> - <input type="number" id="old_entries" name="old_entries" min="1" max="1200" value="<?php echo FreshRSS_Context::$user_conf->old_entries; ?>" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->old_entries; ?>"/> <?php echo _t('gen.date.month'); ?> - <a class="btn confirm" href="<?php echo _url('entry', 'purge'); ?>"><?php echo _t('conf.archiving.purge_now'); ?></a> - </div> - </div> - <div class="form-group"> - <label class="group-name" for="keep_history_default"><?php echo _t('conf.archiving.keep_history_by_feed'); ?></label> - <div class="group-controls"> - <select class="number" name="keep_history_default" id="keep_history_default" required="required" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->keep_history_default; ?>"><?php - foreach (array('' => '', 0 => '0', 10 => '10', 50 => '50', 100 => '100', 500 => '500', 1000 => '1 000', 5000 => '5 000', 10000 => '10 000', FreshRSS_Feed::KEEP_HISTORY_INFINITE => '∞') as $v => $t) { - echo '<option value="' . $v . (FreshRSS_Context::$user_conf->keep_history_default == $v ? '" selected="selected' : '') . '">' . $t . ' </option>'; - } - ?></select> (<?php echo _t('gen.short.by_default'); ?>) - </div> - </div> - <div class="form-group"> - <label class="group-name" for="ttl_default"><?php echo _t('conf.archiving.ttl'); ?></label> - <div class="group-controls"> - <select class="number" name="ttl_default" id="ttl_default" required="required" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->ttl_default; ?>"><?php + <select class="number" name="ttl_default" id="ttl_default" required="required" data-leave-validation="<?= FreshRSS_Context::$user_conf->ttl_default ?>"><?php $found = false; foreach (array(1200 => '20min', 1500 => '25min', 1800 => '30min', 2700 => '45min', 3600 => '1h', 5400 => '1.5h', 7200 => '2h', 10800 => '3h', 14400 => '4h', 18800 => '5h', 21600 => '6h', 25200 => '7h', 28800 => '8h', @@ -43,44 +26,123 @@ if (!$found) { echo '<option value="' . intval(FreshRSS_Context::$user_conf->ttl_default) . '" selected="selected">' . intval(FreshRSS_Context::$user_conf->ttl_default) . 's</option>'; } - ?></select> (<?php echo _t('gen.short.by_default'); ?>) + ?></select> (<?= _t('gen.short.by_default') ?>) + </div> + </div> + + <p class="alert alert-warn"> + <?= _t('conf.archiving.policy_warning') ?> + </p> + + <div class="form-group"> + <label class="group-name"><?= _t('conf.archiving.policy') ?><br /><small>(<?= _t('gen.short.by_default') ?>)</small></label> + <div class="group-controls"> + <label class="checkbox" for="enable_keep_max"> + <input type="checkbox" name="enable_keep_max" id="enable_keep_max" value="1"<?= empty(FreshRSS_Context::$user_conf->archiving['keep_max']) ? '' : ' checked="checked"' ?> data-leave-validation="<?= empty(FreshRSS_Context::$user_conf->archiving['keep_max']) ? 0 : 1 ?>"/> + <?= _t('conf.archiving.keep_max') ?> + <?php $keepMax = empty(FreshRSS_Context::$user_conf->archiving['keep_max']) ? 200 : FreshRSS_Context::$user_conf->archiving['keep_max']; ?> + <input type="number" id="keep_max" name="keep_max" min="0" value="<?= $keepMax ?>" data-leave-validation="<?= $keepMax ?>"/> + </label> + </div> + </div> + + <div class="form-group"> + <div class="group-controls"> + <label class="checkbox" for="enable_keep_period"> + <input type="checkbox" name="enable_keep_period" id="enable_keep_period" value="1"<?= FreshRSS_Context::$user_conf->volatile['enable_keep_period'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->volatile['enable_keep_period'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_period') ?> + <input type="number" id="keep_period_count" name="keep_period_count" min="0" value="<?= FreshRSS_Context::$user_conf->volatile['keep_period_count'] ?>" data-leave-validation="<?= FreshRSS_Context::$user_conf->volatile['keep_period_count'] ?>"/> + <select class="number" name="keep_period_unit" id="keep_period_unit" data-leave-validation="<?= FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ?>"> + <option></option> + <option value="P1Y" <?= 'P1Y' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.years') ?></option> + <option value="P1M" <?= 'P1M' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.months') ?></option> + <option value="P1W" <?= 'P1W' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.weeks') ?></option> + <option value="P1D" <?= 'P1D' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.days') ?></option> + <option value="PT1H" <?= 'PT1H' === FreshRSS_Context::$user_conf->volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.hours') ?></option> + </select> + </label> + </div> + </div> + + <div class="form-group"> + <label class="group-name"><?= _t('conf.archiving.exception') ?><br /><small>(<?= _t('gen.short.by_default') ?>)</small></label> + <div class="group-controls"> + <label class="checkbox" for="keep_favourites"> + <input type="checkbox" name="keep_favourites" id="keep_favourites" value="1"<?= FreshRSS_Context::$user_conf->archiving['keep_favourites'] !== false ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_favourites'] !== false ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_favourites') ?> + </label> + </div> + </div> + + <div class="form-group"> + <div class="group-controls"> + <label class="checkbox" for="keep_labels"> + <input type="checkbox" name="keep_labels" id="keep_labels" value="1"<?= FreshRSS_Context::$user_conf->archiving['keep_labels'] !== false ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_labels'] !== false ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_labels') ?> + </label> + </div> + </div> + + <div class="form-group"> + <div class="group-controls"> + <label class="checkbox" for="keep_unreads"> + <input type="checkbox" name="keep_unreads" id="keep_unreads" value="1"<?= FreshRSS_Context::$user_conf->archiving['keep_unreads'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_unreads'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_unreads') ?> + </label> + </div> + </div> + + <div class="form-group"> + <div class="group-controls"> + <label for="keep_min_default"><?= _t('conf.archiving.keep_min_by_feed') ?> + <input type="number" id="keep_min_default" name="keep_min_default" min="0" value="<?= FreshRSS_Context::$user_conf->archiving['keep_min'] ?>" data-leave-validation="<?= FreshRSS_Context::$user_conf->archiving['keep_min'] ?>"> + </label> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> - <form method="post" action="<?php echo _url('entry', 'optimize'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.archiving.advanced'); ?></legend> + <legend><?= _t('conf.archiving.maintenance') ?></legend> + <form method="post" action="<?= _url('entry', 'purge') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <div class="form-group"> - <label class="group-name"><?php echo _t('conf.user.current'); ?></label> + <label class="group-name"><?= _t('conf.user.current') ?></label> <div class="group-controls"> - <?php echo _t('conf.user.articles_and_size', format_number($this->nb_total), format_bytes($this->size_user)); ?> + <?= _t('conf.user.articles_and_size', format_number($this->nb_total), format_bytes($this->size_user)) ?> </div> </div> - <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> <div class="form-group"> - <label class="group-name"><?php echo _t('conf.user.users'); ?></label> <div class="group-controls"> - <?php echo format_bytes($this->size_total); ?> + <button type="submit" class="btn btn-important confirm"><?= _t('conf.archiving.purge_now') ?></button> </div> </div> + </form> - <div class="form-group form-actions"> + <form method="post" action="<?= _url('entry', 'optimize') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <div class="form-group"> <div class="group-controls"> <input type="hidden" name="optimiseDatabase" value="1" /> - <button type="submit" class="btn btn-important"><?php echo _t('conf.archiving.optimize'); ?></button> - <?php echo _i('help'); ?> <?php echo _t('conf.archiving.optimize_help'); ?> + <button type="submit" class="btn btn-important"><?= _t('conf.archiving.optimize') ?></button> + <?= _i('help') ?> <?= _t('conf.archiving.optimize_help') ?> </div> </div> - <?php } ?> </form> + + <?php if (FreshRSS_Auth::hasAccess('admin')): ?> + <div class="form-group"> + <label class="group-name"><?= _t('conf.user.users') ?></label> + <div class="group-controls"> + <?= format_bytes($this->size_total) ?> + </div> + </div> + <?php endif; ?> </div> diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 58c4e219a..c1cfecc2a 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -1,47 +1,47 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('configure', 'display'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.display'); ?></legend> + <form method="post" action="<?= _url('configure', 'display') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.display') ?></legend> <div class="form-group"> - <label class="group-name" for="language"><?php echo _t('conf.display.language'); ?></label> + <label class="group-name" for="language"><?= _t('conf.display.language') ?></label> <div class="group-controls"> - <select name="language" id="language" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->language; ?>"> + <select name="language" id="language" data-leave-validation="<?= FreshRSS_Context::$user_conf->language ?>"> <?php $languages = Minz_Translate::availableLanguages(); ?> <?php foreach ($languages as $lang) { ?> - <option value="<?php echo $lang; ?>"<?php echo FreshRSS_Context::$user_conf->language === $lang ? ' selected="selected"' : ''; ?>><?php echo _t('gen.lang.' . $lang); ?></option> + <option value="<?= $lang ?>"<?= FreshRSS_Context::$user_conf->language === $lang ? ' selected="selected"' : '' ?>><?= _t('gen.lang.' . $lang) ?></option> <?php } ?> </select> </div> </div> <div class="form-group"> - <label class="group-name" for="theme"><?php echo _t('conf.display.theme'); ?></label> + <label class="group-name" for="theme"><?= _t('conf.display.theme') ?></label> <div class="group-controls"> <ul class="slides"> <?php $slides = count($this->themes); $i = 1; ?> <?php foreach($this->themes as $theme) { ?> - <input type="radio" name="theme" id="img-<?php echo $i ?>" <?php if (FreshRSS_Context::$user_conf->theme === $theme['id']) {echo "checked";}?> value="<?php echo $theme['id'] ?>" data-leave-validation="<?php echo (FreshRSS_Context::$user_conf->theme === $theme['id']) ? 1 : 0; ?>"/> + <input type="radio" name="theme" id="img-<?= $i ?>" <?php if (FreshRSS_Context::$user_conf->theme === $theme['id']) {echo "checked";}?> value="<?= $theme['id'] ?>" data-leave-validation="<?= (FreshRSS_Context::$user_conf->theme === $theme['id']) ? 1 : 0 ?>"/> <li class="slide-container"> <div class="slide"> - <img src="<?php echo Minz_Url::display('/themes/' . $theme['id'] . '/thumbs/original.png')?>"/> + <img src="<?= Minz_Url::display('/themes/' . $theme['id'] . '/thumbs/original.png') ?>"/> </div> <div class="nav"> <?php if ($i !== 1) {?> - <label for="img-<?php echo $i - 1 ?>" class="prev">‹</label> + <label for="img-<?= $i - 1 ?>" class="prev">‹</label> <?php } ?> <?php if ($i !== $slides) {?> - <label for="img-<?php echo $i + 1 ?>" class="next">›</label> + <label for="img-<?= $i + 1 ?>" class="next">›</label> <?php } ?> </div> <div class="properties"> - <div><?php echo sprintf('%s — %s %s', $theme['name'], _t('gen.short.by_author'), $theme['author']); ?></div> - <div><?php echo $theme['description'] ?></div> - <div class="page-number"><?php echo sprintf('%d/%d', $i, $slides) ?></div> + <div><?= sprintf('%s — %s %s', $theme['name'], _t('gen.short.by_author'), $theme['author']) ?></div> + <div><?= $theme['description'] ?></div> + <div class="page-number"><?= sprintf('%d/%d', $i, $slides) ?></div> </div> </li> <?php $i++ ?> @@ -52,81 +52,84 @@ <?php $width = FreshRSS_Context::$user_conf->content_width; ?> <div class="form-group"> - <label class="group-name" for="content_width"><?php echo _t('conf.display.width.content'); ?></label> + <label class="group-name" for="content_width"><?= _t('conf.display.width.content') ?></label> <div class="group-controls"> - <select name="content_width" id="content_width" required="" data-leave-validation="<?php echo $width; ?>"> - <option value="thin" <?php echo $width === 'thin'? 'selected="selected"' : ''; ?>> - <?php echo _t('conf.display.width.thin'); ?> + <select name="content_width" id="content_width" required="" data-leave-validation="<?= $width ?>"> + <option value="thin" <?= $width === 'thin'? 'selected="selected"' : '' ?>> + <?= _t('conf.display.width.thin') ?> </option> - <option value="medium" <?php echo $width === 'medium'? 'selected="selected"' : ''; ?>> - <?php echo _t('conf.display.width.medium'); ?> + <option value="medium" <?= $width === 'medium'? 'selected="selected"' : '' ?>> + <?= _t('conf.display.width.medium') ?> </option> - <option value="large" <?php echo $width === 'large'? 'selected="selected"' : ''; ?>> - <?php echo _t('conf.display.width.large'); ?> + <option value="large" <?= $width === 'large'? 'selected="selected"' : '' ?>> + <?= _t('conf.display.width.large') ?> </option> - <option value="no_limit" <?php echo $width === 'no_limit'? 'selected="selected"' : ''; ?>> - <?php echo _t('conf.display.width.no_limit'); ?> + <option value="no_limit" <?= $width === 'no_limit'? 'selected="selected"' : '' ?>> + <?= _t('conf.display.width.no_limit') ?> </option> </select> </div> </div> <div class="form-group"> - <label class="group-name"><?php echo _t('conf.display.icon.entry'); ?></label> + <label class="group-name"><?= _t('conf.display.icon.entry') ?></label> <table> <thead> <tr> <th> </th> - <th title="<?php echo _t('gen.action.mark_read'); ?>"><?php echo _i('read'); ?></th> - <th title="<?php echo _t('gen.action.mark_favorite'); ?>"><?php echo _i('bookmark'); ?></th> - <th><?php echo _t('conf.display.icon.related_tags'); ?></th> - <th><?php echo _t('conf.display.icon.sharing'); ?></th> - <th><?php echo _t('conf.display.icon.publication_date'); ?></th> - <th><?php echo _i('link'); ?></th> + <th title="<?= _t('gen.action.mark_read') ?>"><?= _i('read') ?></th> + <th title="<?= _t('gen.action.mark_favorite') ?>"><?= _i('bookmark') ?></th> + <th><?= _t('conf.display.icon.related_tags') ?></th> + <th><?= _t('conf.display.icon.sharing') ?></th> + <th><?= _t('conf.display.icon.display_authors') ?></th> + <th><?= _t('conf.display.icon.publication_date') ?></th> + <th><?= _i('link') ?></th> </tr> </thead> <tbody> <tr> - <th><?php echo _t('conf.display.icon.top_line'); ?></th> - <td><input type="checkbox" name="topline_read" value="1"<?php echo FreshRSS_Context::$user_conf->topline_read ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->topline_read; ?>"/></td> - <td><input type="checkbox" name="topline_favorite" value="1"<?php echo FreshRSS_Context::$user_conf->topline_favorite ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->topline_favorite; ?>"/></td> + <th><?= _t('conf.display.icon.top_line') ?></th> + <td><input type="checkbox" name="topline_read" value="1"<?= FreshRSS_Context::$user_conf->topline_read ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_read ?>"/></td> + <td><input type="checkbox" name="topline_favorite" value="1"<?= FreshRSS_Context::$user_conf->topline_favorite ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_favorite ?>"/></td> <td><input type="checkbox" disabled="disabled" /></td> <td><input type="checkbox" disabled="disabled" /></td> - <td><input type="checkbox" name="topline_date" value="1"<?php echo FreshRSS_Context::$user_conf->topline_date ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->topline_date; ?>"/></td> - <td><input type="checkbox" name="topline_link" value="1"<?php echo FreshRSS_Context::$user_conf->topline_link ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->topline_link; ?>"/></td> + <td><input type="checkbox" name="topline_display_authors" value="1"<?= FreshRSS_Context::$user_conf->topline_display_authors ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_display_authors ?>"/></td> + <td><input type="checkbox" name="topline_date" value="1"<?= FreshRSS_Context::$user_conf->topline_date ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_date ?>"/></td> + <td><input type="checkbox" name="topline_link" value="1"<?= FreshRSS_Context::$user_conf->topline_link ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->topline_link ?>"/></td> </tr><tr> - <th><?php echo _t('conf.display.icon.bottom_line'); ?></th> - <td><input type="checkbox" name="bottomline_read" value="1"<?php echo FreshRSS_Context::$user_conf->bottomline_read ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->bottomline_read; ?>"/></td> - <td><input type="checkbox" name="bottomline_favorite" value="1"<?php echo FreshRSS_Context::$user_conf->bottomline_favorite ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->bottomline_favorite; ?>"/></td> - <td><input type="checkbox" name="bottomline_tags" value="1"<?php echo FreshRSS_Context::$user_conf->bottomline_tags ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->bottomline_tags; ?>"/></td> - <td><input type="checkbox" name="bottomline_sharing" value="1"<?php echo FreshRSS_Context::$user_conf->bottomline_sharing ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->bottomline_sharing; ?>"/></td> - <td><input type="checkbox" name="bottomline_date" value="1"<?php echo FreshRSS_Context::$user_conf->bottomline_date ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->bottomline_date; ?>"/></td> - <td><input type="checkbox" name="bottomline_link" value="1"<?php echo FreshRSS_Context::$user_conf->bottomline_link ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->bottomline_link; ?>"/></td> + <th><?= _t('conf.display.icon.bottom_line') ?></th> + <td><input type="checkbox" name="bottomline_read" value="1"<?= FreshRSS_Context::$user_conf->bottomline_read ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_read ?>"/></td> + <td><input type="checkbox" name="bottomline_favorite" value="1"<?= FreshRSS_Context::$user_conf->bottomline_favorite ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_favorite ?>"/></td> + <td><input type="checkbox" name="bottomline_tags" value="1"<?= FreshRSS_Context::$user_conf->bottomline_tags ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_tags ?>"/></td> + <td><input type="checkbox" name="bottomline_sharing" value="1"<?= FreshRSS_Context::$user_conf->bottomline_sharing ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_sharing ?>"/></td> + <td><input type="checkbox" disabled="disabled" /></td> + <td><input type="checkbox" name="bottomline_date" value="1"<?= FreshRSS_Context::$user_conf->bottomline_date ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_date ?>"/></td> + <td><input type="checkbox" name="bottomline_link" value="1"<?= FreshRSS_Context::$user_conf->bottomline_link ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->bottomline_link ?>"/></td> </tr> </tbody> </table><br /> </div> <div class="form-group"> - <label class="group-name" for="html5_notif_timeout"><?php echo _t('conf.display.notif_html5.timeout'); ?></label> + <label class="group-name" for="html5_notif_timeout"><?= _t('conf.display.notif_html5.timeout') ?></label> <div class="group-controls"> - <input type="number" id="html5_notif_timeout" name="html5_notif_timeout" value="<?php echo FreshRSS_Context::$user_conf->html5_notif_timeout; ?>" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->html5_notif_timeout; ?>"/> <?php echo _t('conf.display.notif_html5.seconds'); ?> + <input type="number" id="html5_notif_timeout" name="html5_notif_timeout" value="<?= FreshRSS_Context::$user_conf->html5_notif_timeout ?>" data-leave-validation="<?= FreshRSS_Context::$user_conf->html5_notif_timeout ?>"/> <?= _t('conf.display.notif_html5.seconds') ?> </div> </div> <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="show_nav_buttons"> - <input type="checkbox" name="show_nav_buttons" id="show_nav_buttons" value="1"<?php echo FreshRSS_Context::$user_conf->show_nav_buttons ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->show_nav_buttons; ?>"/> - <?php echo _t('conf.display.show_nav_buttons'); ?> + <input type="checkbox" name="show_nav_buttons" id="show_nav_buttons" value="1"<?= FreshRSS_Context::$user_conf->show_nav_buttons ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->show_nav_buttons ?>"/> + <?= _t('conf.display.show_nav_buttons') ?> </label> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/configure/queries.phtml b/app/views/configure/queries.phtml index baaf74954..a0f600b5d 100644 --- a/app/views/configure/queries.phtml +++ b/app/views/configure/queries.phtml @@ -1,70 +1,70 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('configure', 'queries'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.query'); ?></legend> + <form method="post" action="<?= _url('configure', 'queries') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.query') ?></legend> <?php foreach ($this->queries as $key => $query) { ?> - <div class="form-group" id="query-group-<?php echo $key; ?>"> - <label class="group-name" for="queries_<?php echo $key; ?>_name"> - <?php echo _t('conf.query.number', $key + 1); ?> + <div class="form-group" id="query-group-<?= $key ?>"> + <label class="group-name" for="queries_<?= $key ?>_name"> + <?= _t('conf.query.number', $key + 1) ?> </label> <div class="group-controls"> - <input type="hidden" id="queries_<?php echo $key; ?>_url" name="queries[<?php echo $key; ?>][url]" value="<?php echo $query->getUrl(); ?>"/> - <input type="hidden" id="queries_<?php echo $key; ?>_search" name="queries[<?php echo $key; ?>][search]" value="<?php echo $query->getSearch(); ?>"/> - <input type="hidden" id="queries_<?php echo $key; ?>_state" name="queries[<?php echo $key; ?>][state]" value="<?php echo $query->getState(); ?>"/> - <input type="hidden" id="queries_<?php echo $key; ?>_order" name="queries[<?php echo $key; ?>][order]" value="<?php echo $query->getOrder(); ?>"/> - <input type="hidden" id="queries_<?php echo $key; ?>_get" name="queries[<?php echo $key; ?>][get]" value="<?php echo $query->getGet(); ?>"/> + <input type="hidden" id="queries_<?= $key ?>_url" name="queries[<?= $key ?>][url]" value="<?= $query->getUrl() ?>"/> + <input type="hidden" id="queries_<?= $key ?>_search" name="queries[<?= $key ?>][search]" value="<?= $query->getSearch() ?>"/> + <input type="hidden" id="queries_<?= $key ?>_state" name="queries[<?= $key ?>][state]" value="<?= $query->getState() ?>"/> + <input type="hidden" id="queries_<?= $key ?>_order" name="queries[<?= $key ?>][order]" value="<?= $query->getOrder() ?>"/> + <input type="hidden" id="queries_<?= $key ?>_get" name="queries[<?= $key ?>][get]" value="<?= $query->getGet() ?>"/> <div class="stick"> <input class="extend" type="text" - id="queries_<?php echo $key; ?>_name" - name="queries[<?php echo $key; ?>][name]" - value="<?php echo $query->getName(); ?>" - data-leave-validation="<?php echo $query->getName(); ?>" + id="queries_<?= $key ?>_name" + name="queries[<?= $key ?>][name]" + value="<?= $query->getName() ?>" + data-leave-validation="<?= $query->getName() ?>" /> - <a class="btn" href="<?php echo $query->getUrl(); ?>" title="<?php echo _t('conf.query.display'); ?>"> - <?php echo _i('link'); ?> + <a class="btn" href="<?= $query->getUrl() ?>" title="<?= _t('conf.query.display') ?>"> + <?= _i('link') ?> </a> - <a class="btn btn-attention remove" href="#" data-remove="query-group-<?php echo $key; ?>" title="<?php echo _t('conf.query.remove'); ?>"> - <?php echo _i('close'); ?> + <a class="btn btn-attention remove" href="#" data-remove="query-group-<?= $key ?>" title="<?= _t('conf.query.remove') ?>"> + <?= _i('close') ?> </a> </div> <?php if (!$query->hasParameters()) { ?> <div class="alert alert-warn"> - <div class="alert-head"><?php echo _t('conf.query.no_filter'); ?></div> + <div class="alert-head"><?= _t('conf.query.no_filter') ?></div> </div> <?php } elseif ($query->isDeprecated()) { ?> <div class="alert alert-error"> - <div class="alert-head"><?php echo _t('conf.query.deprecated'); ?></div> + <div class="alert-head"><?= _t('conf.query.deprecated') ?></div> </div> <?php } else { ?> <div class="alert alert-success"> - <div class="alert-head"><?php echo _t('conf.query.filter'); ?></div> + <div class="alert-head"><?= _t('conf.query.filter') ?></div> <ul> <?php if ($query->hasSearch()) { ?> - <li class="item"><?php echo _t('conf.query.search', $query->getSearch()->getRawInput()); ?></li> + <li class="item"><?= _t('conf.query.search', $query->getSearch()->getRawInput()) ?></li> <?php } ?> <?php if ($query->getState()) { ?> - <li class="item"><?php echo _t('conf.query.state_' . $query->getState()); ?></li> + <li class="item"><?= _t('conf.query.state_' . $query->getState()) ?></li> <?php } ?> <?php if ($query->getOrder()) { ?> - <li class="item"><?php echo _t('conf.query.order_' . strtolower($query->getOrder())); ?></li> + <li class="item"><?= _t('conf.query.order_' . strtolower($query->getOrder())) ?></li> <?php } ?> <?php if ($query->getGet()) { ?> - <li class="item"><?php echo _t('conf.query.get_' . $query->getGetType(), $query->getGetName()); ?></li> + <li class="item"><?= _t('conf.query.get_' . $query->getGetType(), $query->getGetName()) ?></li> <?php } ?> </ul> </div> @@ -76,12 +76,12 @@ <?php if (count(FreshRSS_Context::$user_conf->queries) > 0) { ?> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> <?php } else { ?> - <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('conf.query.none'); ?></p> + <p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('conf.query.none') ?></p> <?php } ?> </form> diff --git a/app/views/configure/reading.phtml b/app/views/configure/reading.phtml index ebb00c97b..7c42b59f1 100644 --- a/app/views/configure/reading.phtml +++ b/app/views/configure/reading.phtml @@ -1,48 +1,48 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('configure', 'reading'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.reading'); ?></legend> + <form method="post" action="<?= _url('configure', 'reading') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.reading') ?></legend> <div class="form-group"> - <label class="group-name" for="posts_per_page"><?php echo _t('conf.reading.articles_per_page'); ?></label> + <label class="group-name" for="posts_per_page"><?= _t('conf.reading.articles_per_page') ?></label> <div class="group-controls"> - <input type="number" id="posts_per_page" name="posts_per_page" value="<?php echo FreshRSS_Context::$user_conf->posts_per_page; ?>" min="5" max="500" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->posts_per_page; ?>"/> - <?php echo _i('help'); ?> <?php echo _t('conf.reading.number_divided_when_reader'); ?> + <input type="number" id="posts_per_page" name="posts_per_page" value="<?= FreshRSS_Context::$user_conf->posts_per_page ?>" min="5" max="500" data-leave-validation="<?= FreshRSS_Context::$user_conf->posts_per_page ?>"/> + <?= _i('help') ?> <?= _t('conf.reading.number_divided_when_reader') ?> </div> </div> <div class="form-group"> - <label class="group-name" for="sort_order"><?php echo _t('conf.reading.sort'); ?></label> + <label class="group-name" for="sort_order"><?= _t('conf.reading.sort') ?></label> <div class="group-controls"> - <select name="sort_order" id="sort_order" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->sort_order; ?>"> - <option value="DESC"<?php echo FreshRSS_Context::$user_conf->sort_order === 'DESC' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.sort.newer_first'); ?></option> - <option value="ASC"<?php echo FreshRSS_Context::$user_conf->sort_order === 'ASC' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.sort.older_first'); ?></option> + <select name="sort_order" id="sort_order" data-leave-validation="<?= FreshRSS_Context::$user_conf->sort_order ?>"> + <option value="DESC"<?= FreshRSS_Context::$user_conf->sort_order === 'DESC' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.sort.newer_first') ?></option> + <option value="ASC"<?= FreshRSS_Context::$user_conf->sort_order === 'ASC' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.sort.older_first') ?></option> </select> </div> </div> <div class="form-group"> - <label class="group-name" for="view_mode"><?php echo _t('conf.reading.view.default'); ?></label> + <label class="group-name" for="view_mode"><?= _t('conf.reading.view.default') ?></label> <div class="group-controls"> - <select name="view_mode" id="view_mode" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->view_mode; ?>"> - <option value="normal"<?php echo FreshRSS_Context::$user_conf->view_mode === 'normal' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.view.normal'); ?></option> - <option value="reader"<?php echo FreshRSS_Context::$user_conf->view_mode === 'reader' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.view.reader'); ?></option> - <option value="global"<?php echo FreshRSS_Context::$user_conf->view_mode === 'global' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.view.global'); ?></option> + <select name="view_mode" id="view_mode" data-leave-validation="<?= FreshRSS_Context::$user_conf->view_mode ?>"> + <option value="normal"<?= FreshRSS_Context::$user_conf->view_mode === 'normal' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.normal') ?></option> + <option value="reader"<?= FreshRSS_Context::$user_conf->view_mode === 'reader' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.reader') ?></option> + <option value="global"<?= FreshRSS_Context::$user_conf->view_mode === 'global' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.view.global') ?></option> </select> </div> </div> <div class="form-group"> - <label class="group-name" for="view_mode"><?php echo _t('conf.reading.show'); ?></label> + <label class="group-name" for="view_mode"><?= _t('conf.reading.show') ?></label> <div class="group-controls"> - <select name="default_view" id="default_view" data-leave-validation="<?php echo FreshRSS_Context::$user_conf->default_view; ?>"> - <option value="adaptive"<?php echo FreshRSS_Context::$user_conf->default_view === 'adaptive' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.show.adaptive'); ?></option> - <option value="all"<?php echo FreshRSS_Context::$user_conf->default_view === 'all' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.show.all_articles'); ?></option> - <option value="unread"<?php echo FreshRSS_Context::$user_conf->default_view === 'unread' ? ' selected="selected"' : ''; ?>><?php echo _t('conf.reading.show.unread'); ?></option> + <select name="default_view" id="default_view" data-leave-validation="<?= FreshRSS_Context::$user_conf->default_view ?>"> + <option value="adaptive"<?= FreshRSS_Context::$user_conf->default_view === 'adaptive' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.adaptive') ?></option> + <option value="all"<?= FreshRSS_Context::$user_conf->default_view === 'all' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.all_articles') ?></option> + <option value="unread"<?= FreshRSS_Context::$user_conf->default_view === 'unread' ? ' selected="selected"' : '' ?>><?= _t('conf.reading.show.unread') ?></option> </select> </div> </div> @@ -50,8 +50,8 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="hide_read_feeds"> - <input type="checkbox" name="hide_read_feeds" id="hide_read_feeds" value="1"<?php echo FreshRSS_Context::$user_conf->hide_read_feeds ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->hide_read_feeds; ?>"/> - <?php echo _t('conf.reading.hide_read_feeds'); ?> + <input type="checkbox" name="hide_read_feeds" id="hide_read_feeds" value="1"<?= FreshRSS_Context::$user_conf->hide_read_feeds ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->hide_read_feeds ?>"/> + <?= _t('conf.reading.hide_read_feeds') ?> </label> </div> </div> @@ -59,9 +59,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="display_posts"> - <input type="checkbox" name="display_posts" id="display_posts" value="1"<?php echo FreshRSS_Context::$user_conf->display_posts ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->display_posts; ?>"/> - <?php echo _t('conf.reading.display_articles_unfolded'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="display_posts" id="display_posts" value="1"<?= FreshRSS_Context::$user_conf->display_posts ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->display_posts ?>"/> + <?= _t('conf.reading.display_articles_unfolded') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -69,9 +69,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="display_categories"> - <input type="checkbox" name="display_categories" id="display_categories" value="1"<?php echo FreshRSS_Context::$user_conf->display_categories ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->display_categories; ?>"/> - <?php echo _t('conf.reading.display_categories_unfolded'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="display_categories" id="display_categories" value="1"<?= FreshRSS_Context::$user_conf->display_categories ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->display_categories ?>"/> + <?= _t('conf.reading.display_categories_unfolded') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -79,9 +79,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="sticky_post"> - <input type="checkbox" name="sticky_post" id="sticky_post" value="1"<?php echo FreshRSS_Context::$user_conf->sticky_post ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->sticky_post; ?>"/> - <?php echo _t('conf.reading.sticky_post'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="sticky_post" id="sticky_post" value="1"<?= FreshRSS_Context::$user_conf->sticky_post ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->sticky_post ?>"/> + <?= _t('conf.reading.sticky_post') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -89,9 +89,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="auto_load_more"> - <input type="checkbox" name="auto_load_more" id="auto_load_more" value="1"<?php echo FreshRSS_Context::$user_conf->auto_load_more ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->auto_load_more; ?>"/> - <?php echo _t('conf.reading.auto_load_more'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="auto_load_more" id="auto_load_more" value="1"<?= FreshRSS_Context::$user_conf->auto_load_more ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->auto_load_more ?>"/> + <?= _t('conf.reading.auto_load_more') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -99,9 +99,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="lazyload"> - <input type="checkbox" name="lazyload" id="lazyload" value="1"<?php echo FreshRSS_Context::$user_conf->lazyload ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->lazyload; ?>"/> - <?php echo _t('conf.reading.img_with_lazyload'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="lazyload" id="lazyload" value="1"<?= FreshRSS_Context::$user_conf->lazyload ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->lazyload ?>"/> + <?= _t('conf.reading.img_with_lazyload') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -109,9 +109,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="sides_close_article"> - <input type="checkbox" name="sides_close_article" id="sides_close_article" value="1"<?php echo FreshRSS_Context::$user_conf->sides_close_article ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->sides_close_article; ?>"/> - <?php echo _t('conf.reading.sides_close_article'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="sides_close_article" id="sides_close_article" value="1"<?= FreshRSS_Context::$user_conf->sides_close_article ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->sides_close_article ?>"/> + <?= _t('conf.reading.sides_close_article') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -119,9 +119,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="reading_confirm"> - <input type="checkbox" name="reading_confirm" id="reading_confirm" value="1"<?php echo FreshRSS_Context::$user_conf->reading_confirm ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->reading_confirm; ?>"/> - <?php echo _t('conf.reading.confirm_enabled'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="reading_confirm" id="reading_confirm" value="1"<?= FreshRSS_Context::$user_conf->reading_confirm ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->reading_confirm ?>"/> + <?= _t('conf.reading.confirm_enabled') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -129,9 +129,9 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="auto_remove_article"> - <input type="checkbox" name="auto_remove_article" id="auto_remove_article" value="1"<?php echo FreshRSS_Context::$user_conf->auto_remove_article ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->auto_remove_article; ?>"/> - <?php echo _t('conf.reading.auto_remove_article'); ?> - <noscript> — <strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <input type="checkbox" name="auto_remove_article" id="auto_remove_article" value="1"<?= FreshRSS_Context::$user_conf->auto_remove_article ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->auto_remove_article ?>"/> + <?= _t('conf.reading.auto_remove_article') ?> + <noscript> — <strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </label> </div> </div> @@ -139,48 +139,48 @@ <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="mark_updated_article_unread"> - <input type="checkbox" name="mark_updated_article_unread" id="mark_updated_article_unread" value="1"<?php echo FreshRSS_Context::$user_conf->mark_updated_article_unread ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->mark_updated_article_unread; ?>"/> - <?php echo _t('conf.reading.mark_updated_article_unread'); ?> + <input type="checkbox" name="mark_updated_article_unread" id="mark_updated_article_unread" value="1"<?= FreshRSS_Context::$user_conf->mark_updated_article_unread ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_updated_article_unread ?>"/> + <?= _t('conf.reading.mark_updated_article_unread') ?> </label> </div> </div> <div class="form-group"> - <label class="group-name"><?php echo _t('conf.reading.read.when'); ?></label> + <label class="group-name"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <label class="checkbox" for="check_open_article"> - <input type="checkbox" name="mark_open_article" id="check_open_article" value="1"<?php echo FreshRSS_Context::$user_conf->mark_when['article'] ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->mark_when['article']; ?>"/> - <?php echo _t('conf.reading.read.article_viewed'); ?> + <input type="checkbox" name="mark_open_article" id="check_open_article" value="1"<?= FreshRSS_Context::$user_conf->mark_when['article'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['article'] ?>"/> + <?= _t('conf.reading.read.article_viewed') ?> </label> <label class="checkbox" for="check_open_site"> - <input type="checkbox" name="mark_open_site" id="check_open_site" value="1"<?php echo FreshRSS_Context::$user_conf->mark_when['site'] ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->mark_when['site']; ?>"/> - <?php echo _t('conf.reading.read.article_open_on_website'); ?> + <input type="checkbox" name="mark_open_site" id="check_open_site" value="1"<?= FreshRSS_Context::$user_conf->mark_when['site'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['site'] ?>"/> + <?= _t('conf.reading.read.article_open_on_website') ?> </label> <label class="checkbox" for="check_scroll"> - <input type="checkbox" name="mark_scroll" id="check_scroll" value="1"<?php echo FreshRSS_Context::$user_conf->mark_when['scroll'] ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->mark_when['scroll']; ?>"/> - <?php echo _t('conf.reading.read.scroll'); ?> + <input type="checkbox" name="mark_scroll" id="check_scroll" value="1"<?= FreshRSS_Context::$user_conf->mark_when['scroll'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['scroll'] ?>"/> + <?= _t('conf.reading.read.scroll') ?> </label> <label class="checkbox" for="check_reception"> - <input type="checkbox" name="mark_upon_reception" id="check_reception" value="1"<?php echo FreshRSS_Context::$user_conf->mark_when['reception'] ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->mark_when['reception']; ?>"/> - <?php echo _t('conf.reading.read.upon_reception'); ?> + <input type="checkbox" name="mark_upon_reception" id="check_reception" value="1"<?= FreshRSS_Context::$user_conf->mark_when['reception'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->mark_when['reception'] ?>"/> + <?= _t('conf.reading.read.upon_reception') ?> </label> </div> </div> <div class="form-group"> - <label class="group-name"><?php echo _t('conf.reading.after_onread'); ?></label> + <label class="group-name"><?= _t('conf.reading.after_onread') ?></label> <div class="group-controls"> <label class="checkbox" for="onread_jump_next"> - <input type="checkbox" name="onread_jump_next" id="onread_jump_next" value="1"<?php echo FreshRSS_Context::$user_conf->onread_jump_next ? ' checked="checked"' : ''; ?> data-leave-validation="<?php echo FreshRSS_Context::$user_conf->onread_jump_next; ?>"/> - <?php echo _t('conf.reading.jump_next'); ?> + <input type="checkbox" name="onread_jump_next" id="onread_jump_next" value="1"<?= FreshRSS_Context::$user_conf->onread_jump_next ? ' checked="checked"' : '' ?> data-leave-validation="<?= FreshRSS_Context::$user_conf->onread_jump_next ?>"/> + <?= _t('conf.reading.jump_next') ?> </label> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml index 026659007..32ef11716 100644 --- a/app/views/configure/sharing.phtml +++ b/app/views/configure/sharing.phtml @@ -1,48 +1,48 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('configure', 'sharing'); ?>" - data-simple='<div class="form-group" id="group-share-##key##"><label class="group-name">##label##</label><div class="group-controls"><div class="stick"><input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="##label##" placeholder="<?php echo _t('conf.sharing.share_name'); ?>" size="64" /> - <input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?php echo _t('gen.short.not_applicable'); ?>" size="64" disabled /><a href="#" class="remove btn btn-attention" data-remove="group-share-##key##"><?php echo _i('close'); ?></a></div> + <form method="post" action="<?= _url('configure', 'sharing') ?>" + data-simple='<div class="form-group" id="group-share-##key##"><label class="group-name">##label##</label><div class="group-controls"><div class="stick"><input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="##label##" placeholder="<?= _t('conf.sharing.share_name') ?>" size="64" /> + <input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?= _t('gen.short.not_applicable') ?>" size="64" disabled /><a href="#" class="remove btn btn-attention" data-remove="group-share-##key##"><?= _i('close') ?></a></div> <input type="hidden" id="share_##key##_type" name="share[##key##][type]" value="##type##" /></div></div>' data-advanced='<div class="form-group" id="group-share-##key##"><label class="group-name">##label##</label><div class="group-controls"> <input type="hidden" id="share_##key##_type" name="share[##key##][type]" value="##type##" /> <input type="hidden" id="share_##key##_method" name="share[##key##][method]" value="##method##" /> <input type="hidden" id="share_##key##_field" name="share[##key##][field]" value="##field##" /> <div class="stick"> - <input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="" placeholder="<?php echo _t('conf.sharing.share_name'); ?>" size="64" /> - <input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?php echo _t('conf.sharing.share_url'); ?>" size="64" /> - <a href="#" class="remove btn btn-attention" data-remove="group-share-##key##" title="<?php echo _t('conf.sharing.remove'); ?>"><?php echo _i('close'); ?></a></div> - <a target="_blank" rel="noreferrer" class="btn" title="<?php echo _t('conf.sharing.more_information'); ?>" href="##help##"><?php echo _i('help'); ?></a> + <input type="text" id="share_##key##_name" name="share[##key##][name]" class="extend" value="" placeholder="<?= _t('conf.sharing.share_name') ?>" size="64" /> + <input type="url" id="share_##key##_url" name="share[##key##][url]" class="extend" value="" placeholder="<?= _t('conf.sharing.share_url') ?>" size="64" /> + <a href="#" class="remove btn btn-attention" data-remove="group-share-##key##" title="<?= _t('conf.sharing.remove') ?>"><?= _i('close') ?></a></div> + <a target="_blank" rel="noreferrer" class="btn" title="<?= _t('conf.sharing.more_information') ?>" href="##help##"><?= _i('help') ?></a> </div></div>'> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.sharing'); ?></legend> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.sharing') ?></legend> <?php foreach (FreshRSS_Context::$user_conf->sharing as $key => $share_options) { $share = FreshRSS_Share::get($share_options['type']); $share->update($share_options); ?> - <div class="form-group group-share" id="group-share-<?php echo $key; ?>"> + <div class="form-group group-share" id="group-share-<?= $key ?>"> <label class="group-name"> - <?php echo $share->name(true); ?> + <?= $share->name(true) ?> </label> <div class="group-controls"> - <input type='hidden' id='share_<?php echo $key; ?>_type' name="share[<?php echo $key; ?>][type]" value='<?php echo $share->type(); ?>' /> - <input type='hidden' id='share_<?php echo $key; ?>_method' name="share[<?php echo $key; ?>][method]" value='<?php echo $share->method(); ?>' /> - <input type='hidden' id='share_<?php echo $key; ?>_field' name="share[<?php echo $key; ?>][field]" value='<?php echo $share->field(); ?>' /> + <input type='hidden' id='share_<?= $key ?>_type' name="share[<?= $key ?>][type]" value='<?= $share->type() ?>' /> + <input type='hidden' id='share_<?= $key ?>_method' name="share[<?= $key ?>][method]" value='<?= $share->method() ?>' /> + <input type='hidden' id='share_<?= $key ?>_field' name="share[<?= $key ?>][field]" value='<?= $share->field() ?>' /> <div class="stick"> - <input type="text" id="share_<?php echo $key; ?>_name" name="share[<?php echo $key; ?>][name]" class="extend" value="<?php echo $share->name(); ?>" placeholder="<?php echo _t('conf.sharing.share_name'); ?>" size="64" data-leave-validation="<?php echo $share->name(); ?>"/> + <input type="text" id="share_<?= $key ?>_name" name="share[<?= $key ?>][name]" class="extend" value="<?= $share->name() ?>" placeholder="<?= _t('conf.sharing.share_name') ?>" size="64" data-leave-validation="<?= $share->name() ?>"/> <?php if ($share->formType() === 'advanced') { ?> - <input type="url" id="share_<?php echo $key; ?>_url" name="share[<?php echo $key; ?>][url]" class="extend" value="<?php echo $share->baseUrl(); ?>" placeholder="<?php echo _t('conf.sharing.share_url'); ?>" size="64" data-leave-validation="<?php echo $share->baseUrl(); ?>"/> + <input type="url" id="share_<?= $key ?>_url" name="share[<?= $key ?>][url]" class="extend" value="<?= $share->baseUrl() ?>" placeholder="<?= _t('conf.sharing.share_url') ?>" size="64" data-leave-validation="<?= $share->baseUrl() ?>"/> <?php } else { ?> - <input type="url" id="share_<?php echo $key; ?>_url" name="share[<?php echo $key; ?>][url]" class="extend" value="<?php echo $share->baseUrl(); ?>" placeholder="<?php echo _t('gen.short.not_applicable'); ?>" size="64" disabled/> + <input type="url" id="share_<?= $key ?>_url" name="share[<?= $key ?>][url]" class="extend" value="<?= $share->baseUrl() ?>" placeholder="<?= _t('gen.short.not_applicable') ?>" size="64" disabled/> <?php } ?> - <a href='#' class='remove btn btn-attention' data-remove="group-share-<?php echo $key; ?>" title="<?php echo _t('conf.sharing.remove'); ?>"><?php echo _i('close'); ?></a> + <a href='#' class='remove btn btn-attention' data-remove="group-share-<?= $key ?>" title="<?= _t('conf.sharing.remove') ?>"><?= _i('close') ?></a> </div> <?php if ($share->formType() === 'advanced') { ?> - <a target="_blank" rel="noreferrer" class="btn" title="<?php echo _t('conf.sharing.more_information'); ?>" href="<?php echo $share->help(); ?>"><?php echo _i('help'); ?></a> + <a target="_blank" rel="noreferrer" class="btn" title="<?= _t('conf.sharing.more_information') ?>" href="<?= $share->help() ?>"><?= _i('help') ?></a> <?php } ?> </div> </div> @@ -52,19 +52,19 @@ <div class="group-controls"> <select> <?php foreach (FreshRSS_Share::enum() as $share) { ?> - <option value='<?php echo $share->type(); ?>' data-form='<?php echo $share->formType(); ?>' data-help='<?php echo $share->help(); ?>' data-method='<?php echo $share->method(); ?>' data-field='<?php echo $share->field(); ?>'> - <?php echo $share->name(true); ?> + <option value='<?= $share->type() ?>' data-form='<?= $share->formType() ?>' data-help='<?= $share->help() ?>' data-method='<?= $share->method() ?>' data-field='<?= $share->field() ?>'> + <?= $share->name(true) ?> </option> <?php } ?> </select> - <a href='#' class='share add btn' title="<?php echo _t('conf.sharing.add'); ?>"><?php echo _i('add'); ?></a> + <a href='#' class='share add btn' title="<?= _t('conf.sharing.add') ?>"><?= _i('add') ?></a> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index 412ea676d..4412266cc 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -1,181 +1,181 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> <datalist id="keys"> <?php foreach ($this->list_keys as $key) { ?> - <option value="<?php echo $key; ?>"> + <option value="<?= $key ?>"> <?php } ?> </datalist> <?php $s = FreshRSS_Context::$user_conf->shortcuts; ?> - <form method="post" action="<?php echo _url('configure', 'shortcut'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.shortcut'); ?></legend> + <form method="post" action="<?= _url('configure', 'shortcut') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.shortcut') ?></legend> - <noscript><p class="alert alert-error"><?php echo _t('conf.shortcut.javascript'); ?></p></noscript> + <noscript><p class="alert alert-error"><?= _t('conf.shortcut.javascript') ?></p></noscript> - <legend><?php echo _t('conf.shortcut.views'); ?></legend> + <legend><?= _t('conf.shortcut.views') ?></legend> <div class="form-group"> - <label class="group-name" for="normal_view_shortcut"><?php echo _t('conf.shortcut.normal_view'); ?></label> + <label class="group-name" for="normal_view_shortcut"><?= _t('conf.shortcut.normal_view') ?></label> <div class="group-controls"> - <input type="text" id="normal_view_shortcut" name="shortcuts[normal_view]" list="keys" value="<?php echo $s['normal_view']; ?>" data-leave-validation="<?php echo $s['normal_view']; ?>"/> + <input type="text" id="normal_view_shortcut" name="shortcuts[normal_view]" list="keys" value="<?= $s['normal_view'] ?>" data-leave-validation="<?= $s['normal_view'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="global_view_shortcut"><?php echo _t('conf.shortcut.global_view'); ?></label> + <label class="group-name" for="global_view_shortcut"><?= _t('conf.shortcut.global_view') ?></label> <div class="group-controls"> - <input type="text" id="global_view_shortcut" name="shortcuts[global_view]" list="keys" value="<?php echo $s['global_view']; ?>" data-leave-validation="<?php echo $s['global_view']; ?>"/> + <input type="text" id="global_view_shortcut" name="shortcuts[global_view]" list="keys" value="<?= $s['global_view'] ?>" data-leave-validation="<?= $s['global_view'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="reading_view_shortcut"><?php echo _t('conf.shortcut.reading_view'); ?></label> + <label class="group-name" for="reading_view_shortcut"><?= _t('conf.shortcut.reading_view') ?></label> <div class="group-controls"> - <input type="text" id="reading_view_shortcut" name="shortcuts[reading_view]" list="keys" value="<?php echo $s['reading_view']; ?>" data-leave-validation="<?php echo $s['reading_view']; ?>"/> + <input type="text" id="reading_view_shortcut" name="shortcuts[reading_view]" list="keys" value="<?= $s['reading_view'] ?>" data-leave-validation="<?= $s['reading_view'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="rss_view_shortcut"><?php echo _t('conf.shortcut.rss_view'); ?></label> + <label class="group-name" for="rss_view_shortcut"><?= _t('conf.shortcut.rss_view') ?></label> <div class="group-controls"> - <input type="text" id="rss_view_shortcut" name="shortcuts[rss_view]" list="keys" value="<?php echo $s['rss_view']; ?>" data-leave-validation="<?php echo $s['rss_view']; ?>"/> + <input type="text" id="rss_view_shortcut" name="shortcuts[rss_view]" list="keys" value="<?= $s['rss_view'] ?>" data-leave-validation="<?= $s['rss_view'] ?>"/> </div> </div> - <legend><?php echo _t('conf.shortcut.navigation'); ?></legend> + <legend><?= _t('conf.shortcut.navigation') ?></legend> - <p class="alert alert-warn"><?php echo _t('conf.shortcut.navigation_help');?></p> + <p class="alert alert-warn"><?= _t('conf.shortcut.navigation_help') ?></p> <div class="form-group"> - <label class="group-name" for="next_entry"><?php echo _t('conf.shortcut.next_article'); ?></label> + <label class="group-name" for="next_entry"><?= _t('conf.shortcut.next_article') ?></label> <div class="group-controls"> - <input type="text" id="next_entry" name="shortcuts[next_entry]" list="keys" value="<?php echo $s['next_entry']; ?>" data-leave-validation="<?php echo $s['next_entry']; ?>"/> + <input type="text" id="next_entry" name="shortcuts[next_entry]" list="keys" value="<?= $s['next_entry'] ?>" data-leave-validation="<?= $s['next_entry'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="prev_entry"><?php echo _t('conf.shortcut.previous_article'); ?></label> + <label class="group-name" for="prev_entry"><?= _t('conf.shortcut.previous_article') ?></label> <div class="group-controls"> - <input type="text" id="prev_entry" name="shortcuts[prev_entry]" list="keys" value="<?php echo $s['prev_entry']; ?>" data-leave-validation="<?php echo $s['prev_entry']; ?>"/> + <input type="text" id="prev_entry" name="shortcuts[prev_entry]" list="keys" value="<?= $s['prev_entry'] ?>" data-leave-validation="<?= $s['prev_entry'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="first_entry"><?php echo _t('conf.shortcut.first_article'); ?></label> + <label class="group-name" for="first_entry"><?= _t('conf.shortcut.first_article') ?></label> <div class="group-controls"> - <input type="text" id="first_entry" name="shortcuts[first_entry]" list="keys" value="<?php echo $s['first_entry']; ?>" data-leave-validation="<?php echo $s['first_entry']; ?>"/> + <input type="text" id="first_entry" name="shortcuts[first_entry]" list="keys" value="<?= $s['first_entry'] ?>" data-leave-validation="<?= $s['first_entry'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="last_entry"><?php echo _t('conf.shortcut.last_article'); ?></label> + <label class="group-name" for="last_entry"><?= _t('conf.shortcut.last_article') ?></label> <div class="group-controls"> - <input type="text" id="last_entry" name="shortcuts[last_entry]" list="keys" value="<?php echo $s['last_entry']; ?>" data-leave-validation="<?php echo $s['last_entry']; ?>"/> + <input type="text" id="last_entry" name="shortcuts[last_entry]" list="keys" value="<?= $s['last_entry'] ?>" data-leave-validation="<?= $s['last_entry'] ?>"/> </div> </div> - <p class="alert alert-warn"><?php echo _t('conf.shortcut.navigation_no_mod_help');?></p> + <p class="alert alert-warn"><?= _t('conf.shortcut.navigation_no_mod_help') ?></p> <div class="form-group"> - <label class="group-name" for="skip_next_entry"><?php echo _t('conf.shortcut.skip_next_article'); ?></label> + <label class="group-name" for="skip_next_entry"><?= _t('conf.shortcut.skip_next_article') ?></label> <div class="group-controls"> - <input type="text" id="skip_next_entry" name="shortcuts[skip_next_entry]" list="keys" value="<?php echo $s['skip_next_entry']; ?>" data-leave-validation="<?php echo $s['skip_next_entry']; ?>"/> + <input type="text" id="skip_next_entry" name="shortcuts[skip_next_entry]" list="keys" value="<?= $s['skip_next_entry'] ?>" data-leave-validation="<?= $s['skip_next_entry'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="skip_prev_entry"><?php echo _t('conf.shortcut.skip_previous_article'); ?></label> + <label class="group-name" for="skip_prev_entry"><?= _t('conf.shortcut.skip_previous_article') ?></label> <div class="group-controls"> - <input type="text" id="skip_prev_entry" name="shortcuts[skip_prev_entry]" list="keys" value="<?php echo $s['skip_prev_entry']; ?>" data-leave-validation="<?php echo $s['skip_prev_entry']; ?>"/> + <input type="text" id="skip_prev_entry" name="shortcuts[skip_prev_entry]" list="keys" value="<?= $s['skip_prev_entry'] ?>" data-leave-validation="<?= $s['skip_prev_entry'] ?>"/> </div> </div> - <legend><?php echo _t('conf.shortcut.article_action');?></legend> + <legend><?= _t('conf.shortcut.article_action') ?></legend> <div class="form-group"> - <label class="group-name" for="mark_read"><?php echo _t('conf.shortcut.mark_read'); ?></label> + <label class="group-name" for="mark_read"><?= _t('conf.shortcut.mark_read') ?></label> <div class="group-controls"> - <input type="text" id="mark_read" name="shortcuts[mark_read]" list="keys" value="<?php echo $s['mark_read']; ?>" data-leave-validation="<?php echo $s['mark_read']; ?>"/> - <?php echo _t('conf.shortcut.shift_for_all_read'); ?> + <input type="text" id="mark_read" name="shortcuts[mark_read]" list="keys" value="<?= $s['mark_read'] ?>" data-leave-validation="<?= $s['mark_read'] ?>"/> + <?= _t('conf.shortcut.shift_for_all_read') ?> </div> </div> <div class="form-group"> - <label class="group-name" for="mark_favorite"><?php echo _t('conf.shortcut.mark_favorite'); ?></label> + <label class="group-name" for="mark_favorite"><?= _t('conf.shortcut.mark_favorite') ?></label> <div class="group-controls"> - <input type="text" id="mark_favorite" name="shortcuts[mark_favorite]" list="keys" value="<?php echo $s['mark_favorite']; ?>" data-leave-validation="<?php echo $s['mark_favorite']; ?>"/> + <input type="text" id="mark_favorite" name="shortcuts[mark_favorite]" list="keys" value="<?= $s['mark_favorite'] ?>" data-leave-validation="<?= $s['mark_favorite'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="go_website"><?php echo _t('conf.shortcut.see_on_website'); ?></label> + <label class="group-name" for="go_website"><?= _t('conf.shortcut.see_on_website') ?></label> <div class="group-controls"> - <input type="text" id="go_website" name="shortcuts[go_website]" list="keys" value="<?php echo $s['go_website']; ?>" data-leave-validation="<?php echo $s['go_website']; ?>"/> + <input type="text" id="go_website" name="shortcuts[go_website]" list="keys" value="<?= $s['go_website'] ?>" data-leave-validation="<?= $s['go_website'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="auto_share_shortcut"><?php echo _t('conf.shortcut.auto_share'); ?></label> + <label class="group-name" for="auto_share_shortcut"><?= _t('conf.shortcut.auto_share') ?></label> <div class="group-controls"> - <input type="text" id="auto_share_shortcut" name="shortcuts[auto_share]" list="keys" value="<?php echo $s['auto_share']; ?>" data-leave-validation="<?php echo $s['auto_share']; ?>"/> - <?php echo _t('conf.shortcut.auto_share_help'); ?> + <input type="text" id="auto_share_shortcut" name="shortcuts[auto_share]" list="keys" value="<?= $s['auto_share'] ?>" data-leave-validation="<?= $s['auto_share'] ?>"/> + <?= _t('conf.shortcut.auto_share_help') ?> </div> </div> <div class="form-group"> - <label class="group-name" for="collapse_entry"><?php echo _t('conf.shortcut.collapse_article'); ?></label> + <label class="group-name" for="collapse_entry"><?= _t('conf.shortcut.collapse_article') ?></label> <div class="group-controls"> - <input type="text" id="collapse_entry" name="shortcuts[collapse_entry]" list="keys" value="<?php echo $s['collapse_entry']; ?>" data-leave-validation="<?php echo $s['collapse_entry']; ?>"/> + <input type="text" id="collapse_entry" name="shortcuts[collapse_entry]" list="keys" value="<?= $s['collapse_entry'] ?>" data-leave-validation="<?= $s['collapse_entry'] ?>"/> </div> </div> - <legend><?php echo _t('conf.shortcut.other_action');?></legend> + <legend><?= _t('conf.shortcut.other_action') ?></legend> <div class="form-group"> - <label class="group-name" for="load_more_shortcut"><?php echo _t('conf.shortcut.load_more'); ?></label> + <label class="group-name" for="load_more_shortcut"><?= _t('conf.shortcut.load_more') ?></label> <div class="group-controls"> - <input type="text" id="load_more_shortcut" name="shortcuts[load_more]" list="keys" value="<?php echo $s['load_more']; ?>" data-leave-validation="<?php echo $s['load_more']; ?>"/> + <input type="text" id="load_more_shortcut" name="shortcuts[load_more]" list="keys" value="<?= $s['load_more'] ?>" data-leave-validation="<?= $s['load_more'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="focus_search_shortcut"><?php echo _t('conf.shortcut.focus_search'); ?></label> + <label class="group-name" for="focus_search_shortcut"><?= _t('conf.shortcut.focus_search') ?></label> <div class="group-controls"> - <input type="text" id="focus_search_shortcut" name="shortcuts[focus_search]" list="keys" value="<?php echo $s['focus_search']; ?>" data-leave-validation="<?php echo $s['focus_search']; ?>"/> + <input type="text" id="focus_search_shortcut" name="shortcuts[focus_search]" list="keys" value="<?= $s['focus_search'] ?>" data-leave-validation="<?= $s['focus_search'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="user_filter_shortcut"><?php echo _t('conf.shortcut.user_filter'); ?></label> + <label class="group-name" for="user_filter_shortcut"><?= _t('conf.shortcut.user_filter') ?></label> <div class="group-controls"> - <input type="text" id="user_filter_shortcut" name="shortcuts[user_filter]" list="keys" value="<?php echo $s['user_filter']; ?>" data-leave-validation="<?php echo $s['user_filter']; ?>"/> - <?php echo _t('conf.shortcut.user_filter_help'); ?> + <input type="text" id="user_filter_shortcut" name="shortcuts[user_filter]" list="keys" value="<?= $s['user_filter'] ?>" data-leave-validation="<?= $s['user_filter'] ?>"/> + <?= _t('conf.shortcut.user_filter_help') ?> </div> </div> <div class="form-group"> - <label class="group-name" for="close_dropdown_shortcut"><?php echo _t('conf.shortcut.close_dropdown'); ?></label> + <label class="group-name" for="close_dropdown_shortcut"><?= _t('conf.shortcut.close_dropdown') ?></label> <div class="group-controls"> - <input type="text" id="close_dropdown" name="shortcuts[close_dropdown]" list="keys" value="<?php echo $s['close_dropdown']; ?>" data-leave-validation="<?php echo $s['close_dropdown']; ?>"/> + <input type="text" id="close_dropdown" name="shortcuts[close_dropdown]" list="keys" value="<?= $s['close_dropdown'] ?>" data-leave-validation="<?= $s['close_dropdown'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="help_shortcut"><?php echo _t('conf.shortcut.help'); ?></label> + <label class="group-name" for="help_shortcut"><?= _t('conf.shortcut.help') ?></label> <div class="group-controls"> - <input type="text" id="help_shortcut" name="shortcuts[help]" list="keys" value="<?php echo $s['help']; ?>" data-leave-validation="<?php echo $s['help']; ?>"/> + <input type="text" id="help_shortcut" name="shortcuts[help]" list="keys" value="<?= $s['help'] ?>" data-leave-validation="<?= $s['help'] ?>"/> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/configure/system.phtml b/app/views/configure/system.phtml index 9af4cc2c9..1a718e20f 100644 --- a/app/views/configure/system.phtml +++ b/app/views/configure/system.phtml @@ -1,31 +1,31 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('configure', 'system'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('admin.system'); ?></legend> + <form method="post" action="<?= _url('configure', 'system') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('admin.system') ?></legend> <div class="form-group"> - <label class="group-name" for="instance-name"><?php echo _t('admin.system.instance-name'); ?></label> + <label class="group-name" for="instance-name"><?= _t('admin.system.instance-name') ?></label> <div class="group-controls"> - <input type="text" class="extend" id="instance-name" name="instance-name" value="<?php echo FreshRSS_Context::$system_conf->title; ?>" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->title; ?>"/> + <input type="text" class="extend" id="instance-name" name="instance-name" value="<?= FreshRSS_Context::$system_conf->title ?>" data-leave-validation="<?= FreshRSS_Context::$system_conf->title ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="auto-update-url"><?php echo _t('admin.system.auto-update-url'); ?></label> + <label class="group-name" for="auto-update-url"><?= _t('admin.system.auto-update-url') ?></label> <div class="group-controls"> - <input type="text" class="extend" id="auto-update-url" name="auto-update-url" value="<?php echo FreshRSS_Context::$system_conf->auto_update_url; ?>" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->auto_update_url; ?>"/> + <input type="text" class="extend" id="auto-update-url" name="auto-update-url" value="<?= FreshRSS_Context::$system_conf->auto_update_url ?>" data-leave-validation="<?= FreshRSS_Context::$system_conf->auto_update_url ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="max-registrations"><?php echo _t('admin.system.registration.number'); ?></label> + <label class="group-name" for="max-registrations"><?= _t('admin.system.registration.number') ?></label> <div class="group-controls"> - <input type="number" id="max-registrations" name="max-registrations" value="<?php echo FreshRSS_Context::$system_conf->limits['max_registrations']; ?>" min="0" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->limits['max_registrations']; ?>"/> - <?php echo _i('help'); ?> <?php echo _t('admin.system.registration.help'); ?> + <input type="number" id="max-registrations" name="max-registrations" value="<?= FreshRSS_Context::$system_conf->limits['max_registrations'] ?>" min="0" data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['max_registrations'] ?>"/> + <?= _i('help') ?> <?= _t('admin.system.registration.help') ?> </div> </div> @@ -38,32 +38,50 @@ </div> </div> + <?php if ($this->can_enable_email_validation) { ?> + <div class="form-group"> + <div class="group-controls"> + <label class="checkbox" for="force-email-validation"> + <input + type="checkbox" + name="force-email-validation" + id="force-email-validation" + value="1" + <?= FreshRSS_Context::$system_conf->force_email_validation ? 'checked="checked"' : '' ?> + data-leave-validation="<?= FreshRSS_Context::$system_conf->force_email_validation ?>" + /> + <?= _t('admin.system.force_email_validation') ?> + </label> + </div> + </div> + <?php } ?> + <div class="form-group"> - <label class="group-name" for="max-feeds"><?php echo _t('admin.system.max-feeds'); ?></label> + <label class="group-name" for="max-feeds"><?= _t('admin.system.max-feeds') ?></label> <div class="group-controls"> - <input type="number" id="max-feeds" name="max-feeds" value="<?php echo FreshRSS_Context::$system_conf->limits['max_feeds']; ?>" min="1" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->limits['max_feeds']; ?>"/> + <input type="number" id="max-feeds" name="max-feeds" value="<?= FreshRSS_Context::$system_conf->limits['max_feeds'] ?>" min="1" data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['max_feeds'] ?>"/> </div> </div> <div class="form-group"> - <label class="group-name" for="max-categories"><?php echo _t('admin.system.max-categories'); ?></label> + <label class="group-name" for="max-categories"><?= _t('admin.system.max-categories') ?></label> <div class="group-controls"> - <input type="number" id="max-categories" name="max-categories" value="<?php echo FreshRSS_Context::$system_conf->limits['max_categories']; ?>" min="1" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->limits['max_categories']; ?>"/> + <input type="number" id="max-categories" name="max-categories" value="<?= FreshRSS_Context::$system_conf->limits['max_categories'] ?>" min="1" data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['max_categories'] ?>"/> </div> </div> - + <div class="form-group"> - <label class="group-name" for="cookie-duration"><?php echo _t('admin.system.cookie-duration.number'); ?></label> + <label class="group-name" for="cookie-duration"><?= _t('admin.system.cookie-duration.number') ?></label> <div class="group-controls"> - <input type="number" id="cookie-duration" name="cookie-duration" value="<?php echo FreshRSS_Context::$system_conf->limits['cookie_duration']; ?>" min="0" data-leave-validation="<?php echo FreshRSS_Context::$system_conf->limits['cookie_duration']; ?>"/> - <?php echo _i('help'); ?> <?php echo _t('admin.system.cookie-duration.help'); ?> + <input type="number" id="cookie-duration" name="cookie-duration" value="<?= FreshRSS_Context::$system_conf->limits['cookie_duration'] ?>" min="0" data-leave-validation="<?= FreshRSS_Context::$system_conf->limits['cookie_duration'] ?>"/> + <?= _i('help') ?> <?= _t('admin.system.cookie-duration.help') ?> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/error/index.phtml b/app/views/error/index.phtml index 8fd74e8bf..d5618d54c 100644 --- a/app/views/error/index.phtml +++ b/app/views/error/index.phtml @@ -1,9 +1,9 @@ <div class="post"> <div class="alert alert-error"> - <h1 class="alert-head"><?php echo $this->code; ?></h1> + <h1 class="alert-head"><?= $this->code ?></h1> <p> - <?php echo htmlspecialchars($this->errorMessage, ENT_NOQUOTES, 'UTF-8'); ?><br /> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <?= htmlspecialchars($this->errorMessage, ENT_NOQUOTES, 'UTF-8') ?><br /> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> </p> </div> </div> diff --git a/app/views/extension/index.phtml b/app/views/extension/index.phtml index 6439a0333..f5c5bf032 100644 --- a/app/views/extension/index.phtml +++ b/app/views/extension/index.phtml @@ -1,14 +1,14 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('admin.extensions.title'); ?></h1> + <h1><?= _t('admin.extensions.title') ?></h1> <form id="form-extension" method="post"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <?php if (!empty($this->extension_list['system'])) { ?> - <h2><?php echo _t('admin.extensions.system'); ?></h2> + <h2><?= _t('admin.extensions.system') ?></h2> <?php foreach ($this->extension_list['system'] as $ext) { $this->ext_details = $ext; @@ -18,7 +18,7 @@ <?php } ?> <?php if (!empty($this->extension_list['user'])) { ?> - <h2><?php echo _t('admin.extensions.user'); ?></h2> + <h2><?= _t('admin.extensions.user') ?></h2> <?php foreach ($this->extension_list['user'] as $ext) { $this->ext_details = $ext; @@ -30,34 +30,34 @@ if (empty($this->extension_list['system']) && empty($this->extension_list['user'])) { ?> - <p class="alert alert-warn"><?php echo _t('admin.extensions.empty_list'); ?></p> + <p class="alert alert-warn"><?= _t('admin.extensions.empty_list') ?></p> <?php } ?> </form> <?php if (!empty($this->available_extensions)) { ?> - <h2><?php echo _t('admin.extensions.community'); ?></h2> + <h2><?= _t('admin.extensions.community') ?></h2> <table> <tr> - <th><?php echo _t('admin.extensions.name'); ?></th> - <th><?php echo _t('admin.extensions.version'); ?></th> - <th><?php echo _t('admin.extensions.author'); ?></th> - <th><?php echo _t('admin.extensions.description'); ?></th> + <th><?= _t('admin.extensions.name') ?></th> + <th><?= _t('admin.extensions.version') ?></th> + <th><?= _t('admin.extensions.author') ?></th> + <th><?= _t('admin.extensions.description') ?></th> </tr> <?php foreach ($this->available_extensions as $ext) { ?> <tr> - <td><a href="<?php echo $ext['url']; ?>" target="_blank"><?php echo $ext['name']; ?></a></td> - <td><?php echo $ext['version']; ?></td> - <td><?php echo $ext['author']; ?></td> + <td><a href="<?= $ext['url'] ?>" target="_blank"><?= $ext['name'] ?></a></td> + <td><?= $ext['version'] ?></td> + <td><?= $ext['author'] ?></td> <td> - <?php echo $ext['description']; ?> + <?= $ext['description'] ?> <?php if (isset($this->extensions_installed[$ext['name']])) { ?> <?php if (version_compare($this->extensions_installed[$ext['name']], $ext['version']) >= 0) { ?> <span class="alert alert-success"> - <?php echo _t('admin.extensions.latest'); ?> + <?= _t('admin.extensions.latest') ?> </span> <?php } else if ($this->extensions_installed[$ext['name']] != $ext['version']) { ?> <span class="alert alert-warn"> - <?php echo _t('admin.extensions.update'); ?> + <?= _t('admin.extensions.update') ?> </span> <?php } ?> <?php } ?> @@ -69,8 +69,8 @@ </div> <?php $class = isset($this->extension) ? ' class="active"' : ''; ?> -<a href="#" id="close-slider"<?php echo $class; ?>></a> -<div id="slider"<?php echo $class; ?>> +<a href="#" id="close-slider"<?= $class ?>></a> +<div id="slider"<?= $class ?>> <?php if (isset($this->extension)) { $this->renderHelper('extension/configure'); diff --git a/app/views/feed/add.phtml b/app/views/feed/add.phtml index 340970b25..e39f45a86 100644 --- a/app/views/feed/add.phtml +++ b/app/views/feed/add.phtml @@ -1,90 +1,90 @@ <?php if ($this->feed) { ?> <div class="post"> - <h1><?php echo _t('sub.feed.add'); ?></h1> + <h1><?= _t('sub.feed.add') ?></h1> <?php if (!$this->load_ok) { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('feedback.sub.feed.internal_problem', _url('index', 'logs')); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('feedback.sub.feed.internal_problem', _url('index', 'logs')) ?></p> <?php } ?> - <form method="post" action="<?php echo _url('feed', 'add'); ?>" autocomplete="off"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('sub.feed.information'); ?></legend> + <form method="post" action="<?= _url('feed', 'add') ?>" autocomplete="off"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('sub.feed.information') ?></legend> <?php if ($this->load_ok) { ?> <div class="form-group"> - <label class="group-name"><?php echo _t('sub.feed.title'); ?></label> + <label class="group-name"><?= _t('sub.feed.title') ?></label> <div class="group-controls"> - <label><?php echo $this->feed->name() ; ?></label> + <label><?= $this->feed->name() ?></label> </div> </div> <?php $desc = $this->feed->description(); if ($desc != '') { ?> <div class="form-group"> - <label class="group-name"><?php echo _t('sub.feed.description'); ?></label> + <label class="group-name"><?= _t('sub.feed.description') ?></label> <div class="group-controls"> - <label><?php echo htmlspecialchars($desc, ENT_NOQUOTES, 'UTF-8'); ?></label> + <label><?= htmlspecialchars($desc, ENT_NOQUOTES, 'UTF-8') ?></label> </div> </div> <?php } ?> <div class="form-group"> - <label class="group-name"><?php echo _t('sub.feed.website'); ?></label> + <label class="group-name"><?= _t('sub.feed.website') ?></label> <div class="group-controls"> - <?php echo $this->feed->website(); ?> - <a class="btn" target="_blank" rel="noreferrer" href="<?php echo $this->feed->website(); ?>"><?php echo _i('link'); ?></a> + <?= $this->feed->website() ?> + <a class="btn" target="_blank" rel="noreferrer" href="<?= $this->feed->website() ?>"><?= _i('link') ?></a> </div> </div> <?php } ?> <div class="form-group"> - <label class="group-name" for="url"><?php echo _t('sub.feed.url'); ?></label> + <label class="group-name" for="url"><?= _t('sub.feed.url') ?></label> <div class="group-controls"> <div class="stick"> - <input type="text" name="url_rss" id="url" class="extend" value="<?php echo $this->feed->url(); ?>" /> - <a class="btn" target="_blank" rel="noreferrer" href="<?php echo $this->feed->url(); ?>"><?php echo _i('link'); ?></a> + <input type="text" name="url_rss" id="url" class="extend" value="<?= $this->feed->url() ?>" /> + <a class="btn" target="_blank" rel="noreferrer" href="<?= $this->feed->url() ?>"><?= _i('link') ?></a> </div> - <a class="btn" target="_blank" rel="noreferrer" href="http://validator.w3.org/feed/check.cgi?url=<?php echo $this->feed->url(); ?>"><?php echo _t('sub.feed.validator'); ?></a> + <a class="btn" target="_blank" rel="noreferrer" href="http://validator.w3.org/feed/check.cgi?url=<?= $this->feed->url() ?>"><?= _t('sub.feed.validator') ?></a> </div> </div> <div class="form-group"> - <label class="group-name" for="category"><?php echo _t('sub.category'); ?></label> + <label class="group-name" for="category"><?= _t('sub.category') ?></label> <div class="group-controls"> <select name="category" id="category"> <?php foreach ($this->categories as $cat) { ?> - <option value="<?php echo $cat->id(); ?>"<?php echo $cat->id() == 1 ? ' selected="selected"' : ''; ?>> - <?php echo $cat->name(); ?> + <option value="<?= $cat->id() ?>"<?= $cat->id() == 1 ? ' selected="selected"' : '' ?>> + <?= $cat->name() ?> </option> <?php } ?> - <option value="nc"><?php echo _t('sub.category.new'); ?></option> + <option value="nc"><?= _t('sub.category.new') ?></option> </select> <span aria-hidden="true"> - <input type="text" name="new_category[name]" id="new_category_name" autocomplete="off" placeholder="<?php echo _t('sub.category.new'); ?>" /> + <input type="text" name="new_category[name]" id="new_category_name" autocomplete="off" placeholder="<?= _t('sub.category.new') ?>" /> </span> </div> </div> - <legend><?php echo _t('sub.feed.auth.http'); ?></legend> + <legend><?= _t('sub.feed.auth.http') ?></legend> <?php $auth = $this->feed->httpAuth(false); ?> <div class="form-group"> - <label class="group-name" for="http_user"><?php echo _t('sub.feed.auth.username'); ?></label> + <label class="group-name" for="http_user"><?= _t('sub.feed.auth.username') ?></label> <div class="group-controls"> - <input type="text" name="http_user" id="http_user" class="extend" value="<?php echo empty($auth['username']) ? ' ' : $auth['username']; ?>" autocomplete="off" /> + <input type="text" name="http_user" id="http_user" class="extend" value="<?= empty($auth['username']) ? ' ' : $auth['username'] ?>" autocomplete="off" /> </div> - <label class="group-name" for="http_pass"><?php echo _t('sub.feed.auth.password'); ?></label> + <label class="group-name" for="http_pass"><?= _t('sub.feed.auth.password') ?></label> <div class="group-controls"> - <input type="password" name="http_pass" id="http_pass" class="extend" value="<?php echo $auth['password']; ?>" autocomplete="new-password" /> + <input type="password" name="http_pass" id="http_pass" class="extend" value="<?= $auth['password'] ?>" autocomplete="new-password" /> </div> <div class="group-controls"> - <?php echo _i('help'); ?> <?php echo _t('sub.feed.auth.help'); ?> + <?= _i('help') ?> <?= _t('sub.feed.auth.help') ?> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/helpers/category/update.phtml b/app/views/helpers/category/update.phtml index a2ee3e2ef..9e55e613d 100644 --- a/app/views/helpers/category/update.phtml +++ b/app/views/helpers/category/update.phtml @@ -1,34 +1,160 @@ <div class="post"> - <h1><?php echo $this->category->name(); ?></h1> + <h1><?= $this->category->name() ?></h1> <div> - <a href="<?php echo _url('index', 'index', 'get', 'c_' . $this->category->id()); ?>"><?php echo _i('link'); ?> <?php echo _t('gen.action.filter'); ?></a> + <a href="<?= _url('index', 'index', 'get', 'c_' . $this->category->id()) ?>"><?= _i('link') ?> <?= _t('gen.action.filter') ?></a> </div> - <form method="post" action="<?php echo _url('subscription', 'category', 'id', $this->category->id()); ?>" autocomplete="off"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('sub.category.information'); ?></legend> + <form method="post" action="<?= _url('subscription', 'category', 'id', $this->category->id()) ?>" autocomplete="off"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('sub.category.information') ?></legend> <div class="form-group"> - <label class="group-name" for="name"><?php echo _t('sub.category.title'); ?></label> + <label class="group-name" for="name"><?= _t('sub.category.title') ?></label> <div class="group-controls"> - <input type="text" name="name" id="name" class="extend" value="<?php echo $this->category->name() ; ?>" /> + <input type="text" name="name" id="name" class="extend" value="<?= $this->category->name() ?>" <?php + //Disallow changing the name of the default category + echo $this->category->id() == FreshRSS_CategoryDAO::DEFAULTCATEGORYID ? 'disabled="disabled"' : ''; + ?> /> + </div> + </div> + <div class="form-group"> + <label class="group-name" for="position"><?= _t('sub.category.position') ?></label> + <div class="group-controls"> + <input type="number" name="position" id="position" min="1" value="<?= $this->category->attributes('position') ?>" /> + <?= _i('help') ?> <?= _t('sub.category.position_help') ?> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> + <button class="btn btn-important"><?= _t('gen.action.submit') ?></button> <button class="btn btn-attention confirm" - data-str-confirm="<?php echo _t('gen.js.confirm_action_feed_cat'); ?>" - formaction="<?php echo _url('category', 'empty', 'id', $this->category->id()); ?>" - formmethod="post"><?php echo _t('gen.action.empty'); ?></button> + data-str-confirm="<?= _t('gen.js.confirm_action_feed_cat') ?>" + formaction="<?= _url('category', 'empty', 'id', $this->category->id()) ?>" + formmethod="post"><?= _t('gen.action.empty') ?></button> <?php if (!$this->category->isDefault()): ?> <button class="btn btn-attention confirm" - data-str-confirm="<?php echo _t('gen.js.confirm_action_feed_cat'); ?>" - formaction="<?php echo _url('category', 'delete', 'id', $this->category->id()); ?>" - formmethod="post"><?php echo _t('gen.action.remove'); ?></button> + data-str-confirm="<?= _t('gen.js.confirm_action_feed_cat') ?>" + formaction="<?= _url('category', 'delete', 'id', $this->category->id()) ?>" + formmethod="post"><?= _t('gen.action.remove') ?></button> <?php endif;?> </div> </div> + + <legend><?= _t('sub.category.archiving') ?></legend> + <?php + $archiving = $this->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<count>\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; + } + ?> + + <p class="alert alert-warn"> + <?= _t('conf.archiving.policy_warning') ?> + </p> + + <div class="form-group"> + <label class="group-name"><?= _t('conf.archiving.policy') ?></label> + <div class="group-controls"> + <label class="checkbox"> + <input type="checkbox" name="use_default_purge_options" id="use_default_purge_options" value="1"<?= $archiving['default'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['default'] ? 1 : 0 ?>" /> + <?= _t('gen.short.by_default') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="enable_keep_max"> + <input type="checkbox" name="enable_keep_max" id="enable_keep_max" value="1"<?= empty($archiving['keep_max']) ? '' : ' checked="checked"' ?> data-leave-validation="<?= empty($archiving['keep_max']) ? 0 : 1 ?>"/> + <?= _t('conf.archiving.keep_max') ?> + <input type="number" id="keep_max" name="keep_max" min="0" value="<?= empty($archiving['keep_max']) ? 200 : $archiving['keep_max'] ?>" data-leave-validation="<?= empty($archiving['keep_max']) ? 200 : $archiving['keep_max'] ?>"/> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="enable_keep_period"> + <input type="checkbox" name="enable_keep_period" id="enable_keep_period" value="1"<?= $volatile['enable_keep_period'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $volatile['enable_keep_period'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_period') ?> + <input type="number" id="keep_period_count" name="keep_period_count" min="0" value="<?= $volatile['keep_period_count'] ?>" data-leave-validation="<?= $volatile['keep_period_count'] ?>"/> + <select class="number" name="keep_period_unit" id="keep_period_unit" data-leave-validation="<?= $volatile['keep_period_unit'] ?>"> + <option></option> + <option value="P1Y" <?= 'P1Y' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.years') ?></option> + <option value="P1M" <?= 'P1M' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.months') ?></option> + <option value="P1W" <?= 'P1W' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.weeks') ?></option> + <option value="P1D" <?= 'P1D' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.days') ?></option> + <option value="PT1H" <?= 'PT1H' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.hours') ?></option> + </select> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <label class="group-name"><?= _t('conf.archiving.exception') ?></label> + <div class="group-controls"> + <label class="checkbox" for="keep_favourites"> + <input type="checkbox" name="keep_favourites" id="keep_favourites" value="1"<?= $archiving['keep_favourites'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['keep_favourites'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_favourites') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="keep_labels"> + <input type="checkbox" name="keep_labels" id="keep_labels" value="1"<?= $archiving['keep_labels'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['keep_labels'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_labels') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="keep_unreads"> + <input type="checkbox" name="keep_unreads" id="keep_unreads" value="1"<?= $archiving['keep_unreads'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['keep_unreads'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_unreads') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label for="keep_min"><?= _t('sub.feed.keep_min') ?> + <input type="number" id="keep_min" name="keep_min" min="0" value="<?= $archiving['keep_min'] ?>" data-leave-validation="<?= $archiving['keep_min'] ?>"> + </label> + </div> + </div> + <div class="form-group form-actions"> + <div class="group-controls"> + <button class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> + </div> + </div> </form> </div> diff --git a/app/views/helpers/export/articles.phtml b/app/views/helpers/export/articles.phtml index 2d1fcd133..0bbfb86ec 100644 --- a/app/views/helpers/export/articles.phtml +++ b/app/views/helpers/export/articles.phtml @@ -1,10 +1,7 @@ <?php $username = Minz_Session::param('currentUser', '_'); -$options = 0; -if (version_compare(PHP_VERSION, '5.4.0') >= 0) { - $options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; -} +$options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; $articles = array( 'id' => 'user/' . str_replace('/', '', $username) . '/state/org.freshrss/' . $this->type, diff --git a/app/views/helpers/extension/configure.phtml b/app/views/helpers/extension/configure.phtml index cde872aa0..cb6ebd6bb 100644 --- a/app/views/helpers/extension/configure.phtml +++ b/app/views/helpers/extension/configure.phtml @@ -1,19 +1,19 @@ <div class="post"> <h1> - <?php echo $this->extension->getName(); ?> (<?php echo $this->extension->getVersion(); ?>) — + <?= $this->extension->getName() ?> (<?= $this->extension->getVersion() ?>) — <?php echo $this->extension->isEnabled() ? _t('admin.extensions.enabled') : _t('admin.extensions.disabled'); ?> </h1> - <p class="alert alert-warn"><?php echo $this->extension->getDescription(); ?> — <?php echo _t('gen.short.by_author'), ' ', $this->extension->getAuthor(); ?></p> + <p class="alert alert-warn"><?= $this->extension->getDescription() ?> — <?= _t('gen.short.by_author'), ' ', $this->extension->getAuthor() ?></p> - <h2><?php echo _t('gen.action.manage'); ?></h2> + <h2><?= _t('gen.action.manage') ?></h2> <?php $configure_view = $this->extension->getConfigureView(); if ($configure_view !== false) { echo $configure_view; } else { ?> - <p class="alert alert-warn"><?php echo _t('admin.extensions.no_configure_view'); ?></p> + <p class="alert alert-warn"><?= _t('admin.extensions.no_configure_view') ?></p> <?php } ?> </div> diff --git a/app/views/helpers/extension/details.phtml b/app/views/helpers/extension/details.phtml index acba4e816..ed9674e3d 100644 --- a/app/views/helpers/extension/details.phtml +++ b/app/views/helpers/extension/details.phtml @@ -3,19 +3,19 @@ <?php if ($this->ext_details->getType() === 'user' || FreshRSS_Auth::hasAccess('admin')) { ?> <?php $name_encoded = urlencode($this->ext_details->getName()); ?> <div class="stick"> - <a class="btn open-slider" href="<?php echo _url('extension', 'configure', 'e', $name_encoded); ?>"><?php echo _i('configure'); ?> <?php echo _t('gen.action.manage'); ?></a> + <a class="btn open-slider" href="<?= _url('extension', 'configure', 'e', $name_encoded) ?>"><?= _i('configure') ?> <?= _t('gen.action.manage') ?></a> <?php if ($this->ext_details->isEnabled()) { ?> - <button class="btn active" form="form-extension" formaction="<?php echo _url('extension', 'disable', 'e', $name_encoded); ?>"><?php echo _t('gen.action.disable'); ?></button> + <button class="btn active" form="form-extension" formaction="<?= _url('extension', 'disable', 'e', $name_encoded) ?>"><?= _t('gen.action.disable') ?></button> <?php } else { ?> - <button class="btn" form="form-extension" formaction="<?php echo _url('extension', 'enable', 'e', $name_encoded); ?>"><?php echo _t('gen.action.enable'); ?></button> + <button class="btn" form="form-extension" formaction="<?= _url('extension', 'enable', 'e', $name_encoded) ?>"><?= _t('gen.action.enable') ?></button> <?php } ?> <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> - <button class="btn btn-attention confirm" form="form-extension" formaction="<?php echo _url('extension', 'remove', 'e', $name_encoded); ?>"><?php echo _t('gen.action.remove'); ?></button> + <button class="btn btn-attention confirm" form="form-extension" formaction="<?= _url('extension', 'remove', 'e', $name_encoded) ?>"><?= _t('gen.action.remove') ?></button> <?php } ?> </div> <?php } else { ?> - <?php echo _t('admin.extensions.system.no_rights'); ?> + <?= _t('admin.extensions.system.no_rights') ?> <?php } ?> </li> - <li class="item"><?php echo $this->ext_details->getName(); ?></li> + <li class="item"><?= $this->ext_details->getName() ?></li> </ul> diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 620806d7b..e5f186956 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -1,114 +1,214 @@ <div class="post"> - <h1><?php echo $this->feed->name(); ?></h1> + <h1><?= $this->feed->name() ?></h1> <div> - <a href="<?php echo _url('index', 'index', 'get', 'f_' . $this->feed->id()); ?>"><?php echo _i('link'); ?> <?php echo _t('gen.action.filter'); ?></a> - <?php echo _t('gen.short.or'); ?> - <a href="<?php echo _url('stats', 'repartition', 'id', $this->feed->id()); ?>"><?php echo _i('stats'); ?> <?php echo _t('sub.feed.stats'); ?></a> + <a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>"><?= _i('link') ?> <?= _t('gen.action.filter') ?></a> + <?= _t('gen.short.or') ?> + <a href="<?= _url('stats', 'repartition', 'id', $this->feed->id()) ?>"><?= _i('stats') ?> <?= _t('sub.feed.stats') ?></a> </div> - <p><?php echo $this->feed->description(); ?></p> + <p><?= $this->feed->description() ?></p> <?php $nbEntries = $this->feed->nbEntries(); ?> <?php if ($this->feed->inError()) { ?> - <p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('sub.feed.error'); ?></p> + <p class="alert alert-error"><span class="alert-head"><?= _t('gen.short.damn') ?></span> <?= _t('sub.feed.error') ?></p> <?php } elseif ($nbEntries === 0) { ?> - <p class="alert alert-warn"><?php echo _t('sub.feed.empty'); ?></p> + <p class="alert alert-warn"><?= _t('sub.feed.empty') ?></p> <?php } ?> - <form method="post" action="<?php echo _url('subscription', 'feed', 'id', $this->feed->id()); ?>" autocomplete="off"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('sub.feed.information'); ?></legend> + <form method="post" action="<?= _url('subscription', 'feed', 'id', $this->feed->id()) ?>" autocomplete="off"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('sub.feed.information') ?></legend> <div class="form-group"> - <label class="group-name" for="name"><?php echo _t('sub.feed.title'); ?></label> + <label class="group-name" for="name"><?= _t('sub.feed.title') ?></label> <div class="group-controls"> - <input type="text" name="name" id="name" class="extend" value="<?php echo $this->feed->name() ; ?>" /> + <input type="text" name="name" id="name" class="extend" value="<?= $this->feed->name() ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="description"><?php echo _t('sub.feed.description'); ?></label> + <label class="group-name" for="description"><?= _t('sub.feed.description') ?></label> <div class="group-controls"> - <textarea name="description" id="description"><?php echo htmlspecialchars($this->feed->description(), ENT_NOQUOTES, 'UTF-8'); ?></textarea> + <textarea name="description" id="description"><?= htmlspecialchars($this->feed->description(), ENT_NOQUOTES, 'UTF-8') ?></textarea> </div> </div> <div class="form-group"> - <label class="group-name" for="website"><?php echo _t('sub.feed.website'); ?></label> + <label class="group-name" for="website"><?= _t('sub.feed.website') ?></label> <div class="group-controls"> <div class="stick"> - <input type="text" name="website" id="website" class="extend" value="<?php echo $this->feed->website(); ?>" /> - <a class="btn" target="_blank" rel="noreferrer" href="<?php echo $this->feed->website(); ?>"><?php echo _i('link'); ?></a> + <input type="text" name="website" id="website" class="extend" value="<?= $this->feed->website() ?>" /> + <a class="btn" target="_blank" rel="noreferrer" href="<?= $this->feed->website() ?>"><?= _i('link') ?></a> </div> </div> </div> <div class="form-group"> - <label class="group-name" for="url"><?php echo _t('sub.feed.url'); ?></label> + <label class="group-name" for="url"><?= _t('sub.feed.url') ?></label> <div class="group-controls"> <div class="stick"> - <input type="text" name="url" id="url" class="extend" value="<?php echo $this->feed->url(); ?>" /> - <a class="btn" target="_blank" rel="noreferrer" href="<?php echo $this->feed->url(); ?>"><?php echo _i('link'); ?></a> + <input type="text" name="url" id="url" class="extend" value="<?= $this->feed->url() ?>" /> + <a class="btn" target="_blank" rel="noreferrer" href="<?= $this->feed->url() ?>"><?= _i('link') ?></a> </div> - <a class="btn" target="_blank" rel="noreferrer" href="http://validator.w3.org/feed/check.cgi?url=<?php echo rawurlencode(htmlspecialchars_decode($this->feed->url(), ENT_QUOTES)); ?>"><?php echo _t('sub.feed.validator'); ?></a> + <a class="btn" target="_blank" rel="noreferrer" href="http://validator.w3.org/feed/check.cgi?url=<?= rawurlencode(htmlspecialchars_decode($this->feed->url(), ENT_QUOTES)) ?>"><?= _t('sub.feed.validator') ?></a> </div> </div> <div class="form-group"> - <label class="group-name" for="category"><?php echo _t('sub.category'); ?></label> + <label class="group-name" for="category"><?= _t('sub.category') ?></label> <div class="group-controls"> <select name="category" id="category"> <?php foreach ($this->categories as $cat) { ?> - <option value="<?php echo $cat->id(); ?>"<?php echo $cat->id()== $this->feed->category() ? ' selected="selected"' : ''; ?>> - <?php echo $cat->name(); ?> + <option value="<?= $cat->id() ?>"<?= $cat->id()== $this->feed->category() ? ' selected="selected"' : '' ?>> + <?= $cat->name() ?> </option> <?php } ?> </select> </div> </div> <div class="form-group"> - <label class="group-name" for="priority"><?php echo _t('sub.feed.priority'); ?></label> + <label class="group-name" for="priority"><?= _t('sub.feed.priority') ?></label> <div class="group-controls"> <select name="priority" id="priority"> - <option value='<?php echo FreshRSS_Feed::PRIORITY_MAIN_STREAM;?>' <?php if (FreshRSS_Feed::PRIORITY_MAIN_STREAM === $this->feed->priority()) {echo 'selected="selected"';}?>><?php echo _t('sub.feed.priority.main_stream'); ?></option> - <option value='<?php echo FreshRSS_Feed::PRIORITY_NORMAL;?>' <?php if (FreshRSS_Feed::PRIORITY_NORMAL === $this->feed->priority()) {echo 'selected="selected"';}?>><?php echo _t('sub.feed.priority.normal'); ?></option> - <option value='<?php echo FreshRSS_Feed::PRIORITY_ARCHIVED;?>' <?php if (FreshRSS_Feed::PRIORITY_ARCHIVED === $this->feed->priority()) {echo 'selected="selected"';}?>><?php echo _t('sub.feed.priority.archived'); ?></option> + <option value='<?= FreshRSS_Feed::PRIORITY_MAIN_STREAM ?>' <?php if (FreshRSS_Feed::PRIORITY_MAIN_STREAM === $this->feed->priority()) {echo 'selected="selected"';}?>><?= _t('sub.feed.priority.main_stream') ?></option> + <option value='<?= FreshRSS_Feed::PRIORITY_NORMAL ?>' <?php if (FreshRSS_Feed::PRIORITY_NORMAL === $this->feed->priority()) {echo 'selected="selected"';}?>><?= _t('sub.feed.priority.normal') ?></option> + <option value='<?= FreshRSS_Feed::PRIORITY_ARCHIVED ?>' <?php if (FreshRSS_Feed::PRIORITY_ARCHIVED === $this->feed->priority()) {echo 'selected="selected"';}?>><?= _t('sub.feed.priority.archived') ?></option> </select> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> + <button class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> <button class="btn btn-attention confirm" - data-str-confirm="<?php echo _t('gen.js.confirm_action_feed_cat'); ?>" - formaction="<?php echo _url('feed', 'delete', 'id', $this->feed->id()); ?>" - formmethod="post"><?php echo _t('gen.action.remove'); ?></button> + data-str-confirm="<?= _t('gen.js.confirm_action_feed_cat') ?>" + formaction="<?= _url('feed', 'delete', 'id', $this->feed->id()) ?>" + formmethod="post"><?= _t('gen.action.remove') ?></button> </div> </div> - <legend><?php echo _t('sub.feed.archiving'); ?></legend> + <legend><?= _t('sub.feed.archiving') ?></legend> <div class="form-group"> <div class="group-controls"> <div class="stick"> - <input type="text" value="<?php echo _t('sub.feed.number_entries', $nbEntries); ?>" disabled="disabled" /> - <a class="btn" href="<?php echo _url('feed', 'actualize', 'id', $this->feed->id()); ?>"> - <?php echo _i('refresh'); ?> <?php echo _t('gen.action.actualize'); ?> + <input type="text" value="<?= _t('sub.feed.number_entries', $nbEntries) ?>" disabled="disabled" /> + <a class="btn" href="<?= _url('feed', 'actualize', 'id', $this->feed->id()) ?>"> + <?= _i('refresh') ?> <?= _t('gen.action.actualize') ?> </a> </div> </div> </div> + <?php + $archiving = $this->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<count>\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; + } + ?> + + <p class="alert alert-warn"> + <?= _t('conf.archiving.policy_warning') ?> + </p> + <div class="form-group"> - <label class="group-name" for="keep_history"><?php echo _t('sub.feed.keep_history'); ?></label> + <label class="group-name"><?= _t('conf.archiving.policy') ?></label> <div class="group-controls"> - <select class="number" name="keep_history" id="keep_history" required="required"><?php - foreach (array('' => '', FreshRSS_Feed::KEEP_HISTORY_DEFAULT => _t('gen.short.by_default'), 0 => '0', 10 => '10', 50 => '50', 100 => '100', 500 => '500', 1000 => '1 000', 5000 => '5 000', 10000 => '10 000', FreshRSS_Feed::KEEP_HISTORY_INFINITE => '∞') as $v => $t) { - echo '<option value="' . $v . ($this->feed->keepHistory() === $v ? '" selected="selected' : '') . '">' . $t . '</option>'; - } - ?></select> + <label class="checkbox"> + <input type="checkbox" name="use_default_purge_options" id="use_default_purge_options" value="1"<?= $archiving['default'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['default'] ? 1 : 0 ?>" /> + <?= _t('gen.short.by_default') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="enable_keep_max"> + <input type="checkbox" name="enable_keep_max" id="enable_keep_max" value="1"<?= empty($archiving['keep_max']) ? '' : ' checked="checked"' ?> data-leave-validation="<?= empty($archiving['keep_max']) ? 0 : 1 ?>"/> + <?= _t('conf.archiving.keep_max') ?> + <input type="number" id="keep_max" name="keep_max" min="0" value="<?= empty($archiving['keep_max']) ? 200 : $archiving['keep_max'] ?>" data-leave-validation="<?= empty($archiving['keep_max']) ? 200 : $archiving['keep_max'] ?>"/> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="enable_keep_period"> + <input type="checkbox" name="enable_keep_period" id="enable_keep_period" value="1"<?= $volatile['enable_keep_period'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $volatile['enable_keep_period'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_period') ?> + <input type="number" id="keep_period_count" name="keep_period_count" min="0" value="<?= $volatile['keep_period_count'] ?>" data-leave-validation="<?= $volatile['keep_period_count'] ?>"/> + <select class="number" name="keep_period_unit" id="keep_period_unit" data-leave-validation="<?= $volatile['keep_period_unit'] ?>"> + <option></option> + <option value="P1Y" <?= 'P1Y' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.years') ?></option> + <option value="P1M" <?= 'P1M' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.months') ?></option> + <option value="P1W" <?= 'P1W' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.weeks') ?></option> + <option value="P1D" <?= 'P1D' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.days') ?></option> + <option value="PT1H" <?= 'PT1H' === $volatile['keep_period_unit'] ? 'selected="selected"' : '' ?>><?= _t('gen.period.hours') ?></option> + </select> + </label> </div> </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <label class="group-name"><?= _t('conf.archiving.exception') ?></label> + <div class="group-controls"> + <label class="checkbox" for="keep_favourites"> + <input type="checkbox" name="keep_favourites" id="keep_favourites" value="1"<?= $archiving['keep_favourites'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['keep_favourites'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_favourites') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="keep_labels"> + <input type="checkbox" name="keep_labels" id="keep_labels" value="1"<?= $archiving['keep_labels'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['keep_labels'] ? 1 : 0 ?>"/> + <?= _t('conf.archiving.keep_labels') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label class="checkbox" for="keep_unreads"> + <input type="checkbox" name="keep_unreads" id="keep_unreads" value="1"<?= $archiving['keep_unreads'] ? ' checked="checked"' : '' ?> data-leave-validation="<?= $archiving['keep_unreads'] ?>"/> + <?= _t('conf.archiving.keep_unreads') ?> + </label> + </div> + </div> + <div class="form-group archiving"<?= $archiving['default'] ? ' hidden="hidden"' : '' ?>> + <div class="group-controls"> + <label for="keep_min"><?= _t('sub.feed.keep_min') ?> + <input type="number" id="keep_min" name="keep_min" min="0" value="<?= $archiving['keep_min'] ?>" data-leave-validation="<?= $archiving['keep_min'] ?>"> + </label> + </div> + </div> + <div class="form-group"> - <label class="group-name" for="ttl"><?php echo _t('sub.feed.ttl'); ?></label> + <label class="group-name" for="ttl"><?= _t('sub.feed.ttl') ?></label> <div class="group-controls"> <select class="number" name="ttl" id="ttl" required="required"><?php $found = false; @@ -127,130 +227,131 @@ } ?></select> <label for="mute"> - <input type="checkbox" name="mute" id="mute" value="1"<?php echo $this->feed->mute() ? ' checked="checked"' : ''; ?> /> - <?php echo _t('sub.feed.mute'); ?> + <input type="checkbox" name="mute" id="mute" value="1"<?= $this->feed->mute() ? ' checked="checked"' : '' ?> /> + <?= _t('sub.feed.mute') ?> </label> </div> </div> <div class="form-group"> - <label class="group-name" for="pubsubhubbub"><?php echo _t('sub.feed.websub'); ?></label> + <label class="group-name" for="pubsubhubbub"><?= _t('sub.feed.websub') ?></label> <div class="group-controls"> <label class="checkbox" for="pubsubhubbub"> - <input type="checkbox" name="pubsubhubbub" id="pubsubhubbub" disabled="disabled" value="1"<?php echo $this->feed->pubSubHubbubEnabled() ? ' checked="checked"' : ''; ?> /> + <input type="checkbox" name="pubsubhubbub" id="pubsubhubbub" disabled="disabled" value="1"<?= $this->feed->pubSubHubbubEnabled() ? ' checked="checked"' : '' ?> /> </label> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button class="btn btn-attention confirm" formmethod="post" formaction="<?php echo _url('feed', 'truncate', 'id', $this->feed->id()); ?>"><?php echo _t('gen.action.truncate'); ?></button> + <button class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> + <button class="btn btn-attention confirm" formmethod="post" formaction="<?= _url('feed', 'truncate', 'id', $this->feed->id()) ?>"><?= _t('gen.action.truncate') ?></button> </div> </div> - <legend><?php echo _t('sub.feed.auth.configuration'); ?></legend> + <legend><?= _t('sub.feed.auth.configuration') ?></legend> <?php $auth = $this->feed->httpAuth(false); ?> <div class="form-group"> - <label class="group-name" for="http_user_feed<?php echo $this->feed->id(); ?>"><?php echo _t('sub.feed.auth.username'); ?></label> + <label class="group-name" for="http_user_feed<?= $this->feed->id() ?>"><?= _t('sub.feed.auth.username') ?></label> <div class="group-controls"> - <input type="text" name="http_user_feed<?php echo $this->feed->id(); ?>" id="http_user_feed<?php echo $this->feed->id(); ?>" class="extend" value="<?php echo empty($auth['username']) ? ' ' : $auth['username']; ?>" autocomplete="off" /> - <?php echo _i('help'); ?> <?php echo _t('sub.feed.auth.help'); ?> + <input type="text" name="http_user_feed<?= $this->feed->id() ?>" id="http_user_feed<?= $this->feed->id() ?>" class="extend" value="<?= empty($auth['username']) ? ' ' : $auth['username'] ?>" autocomplete="off" /> + <?= _i('help') ?> <?= _t('sub.feed.auth.help') ?> </div> - <label class="group-name" for="http_pass_feed<?php echo $this->feed->id(); ?>"><?php echo _t('sub.feed.auth.password'); ?></label> + <label class="group-name" for="http_pass_feed<?= $this->feed->id() ?>"><?= _t('sub.feed.auth.password') ?></label> <div class="group-controls"> - <input type="password" name="http_pass_feed<?php echo $this->feed->id(); ?>" id="http_pass_feed<?php echo $this->feed->id(); ?>" class="extend" value="<?php echo $auth['password']; ?>" autocomplete="new-password" /> + <input type="password" name="http_pass_feed<?= $this->feed->id() ?>" id="http_pass_feed<?= $this->feed->id() ?>" class="extend" value="<?= $auth['password'] ?>" autocomplete="new-password" /> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> - <legend><?php echo _t('sub.feed.advanced'); ?></legend> + <legend><?= _t('sub.feed.advanced') ?></legend> <div class="form-group"> - <label class="group-name" for="path_entries"><?php echo _t('sub.feed.css_path'); ?></label> + <label class="group-name" for="path_entries"><?= _t('sub.feed.css_path') ?></label> <div class="group-controls"> - <input type="text" name="path_entries" id="path_entries" class="extend" value="<?php echo $this->feed->pathEntries(); ?>" placeholder="<?php echo _t('gen.short.blank_to_disable'); ?>" /> - <?php echo _i('help'); ?> <?php echo _t('sub.feed.css_help'); ?> + <input type="text" name="path_entries" id="path_entries" class="extend" value="<?= $this->feed->pathEntries() ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" /> + <?= _i('help') ?> <?= _t('sub.feed.css_help') ?> </div> </div> <div class="form-group"> - <label class="group-name" for="mark_updated_article_unread"><?php echo _t('conf.reading.mark_updated_article_unread'); ?></label> + <label class="group-name" for="mark_updated_article_unread"><?= _t('conf.reading.mark_updated_article_unread') ?></label> <div class="group-controls"> <label class="checkbox" for="mark_updated_article_unread"> <select name="mark_updated_article_unread" id="mark_updated_article_unread"> - <option value=""<?php echo $this->feed->attributes('mark_updated_article_unread') === null ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.by_default'); ?></option> - <option value="0"<?php echo $this->feed->attributes('mark_updated_article_unread') === false ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.no'); ?></option> - <option value="1"<?php echo $this->feed->attributes('mark_updated_article_unread') === true ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.yes'); ?></option> + <option value=""<?= $this->feed->attributes('mark_updated_article_unread') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributes('mark_updated_article_unread') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributes('mark_updated_article_unread') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> </label> </div> </div> <div class="form-group"> - <label class="group-name" for="read_upon_reception"><?php echo _t('conf.reading.read.when'); ?></label> + <label class="group-name" for="read_upon_reception"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <label class="checkbox" for="read_upon_reception"> <select name="read_upon_reception" id="read_upon_reception"> - <option value=""<?php echo $this->feed->attributes('read_upon_reception') === null ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.by_default'); ?></option> - <option value="0"<?php echo $this->feed->attributes('read_upon_reception') === false ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.no'); ?></option> - <option value="1"<?php echo $this->feed->attributes('read_upon_reception') === true ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.yes'); ?></option> + <option value=""<?= $this->feed->attributes('read_upon_reception') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributes('read_upon_reception') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributes('read_upon_reception') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> - <?php echo _t('conf.reading.read.upon_reception'); ?> + <?= _t('conf.reading.read.upon_reception') ?> </label> </div> </div> <div class="form-group"> - <label class="group-name" for="clear_cache"><?php echo _t('sub.feed.clear_cache'); ?></label> + <label class="group-name" for="clear_cache"><?= _t('sub.feed.clear_cache') ?></label> <div class="group-controls"> - <input type="checkbox" name="clear_cache" id="clear_cache" value="1"<?php echo $this->feed->attributes('clear_cache') ? ' checked="checked"' : ''; ?> /> + <input type="checkbox" name="clear_cache" id="clear_cache" value="1"<?= $this->feed->attributes('clear_cache') ? ' checked="checked"' : '' ?> /> </div> </div> <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> <div class="form-group"> - <label class="group-name" for="timeout"><?php echo _t('sub.feed.timeout'); ?></label> + <label class="group-name" for="timeout"><?= _t('sub.feed.timeout') ?></label> <div class="group-controls"> - <input type="number" name="timeout" id="timeout" min="3" max="120" value="<?php echo $this->feed->attributes('timeout'); ?>" placeholder="<?php echo _t('gen.short.by_default'); ?>" /> + <input type="number" name="timeout" id="timeout" min="3" max="120" value="<?= $this->feed->attributes('timeout') ?>" placeholder="<?= _t('gen.short.by_default') ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="ssl_verify"><?php echo _t('sub.feed.ssl_verify'); ?></label> + <label class="group-name" for="ssl_verify"><?= _t('sub.feed.ssl_verify') ?></label> <div class="group-controls"> <label class="checkbox" for="ssl_verify"> <select name="ssl_verify" id="ssl_verify"> - <option value=""<?php echo $this->feed->attributes('ssl_verify') === null ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.by_default'); ?></option> - <option value="0"<?php echo $this->feed->attributes('ssl_verify') === false ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.no'); ?></option> - <option value="1"<?php echo $this->feed->attributes('ssl_verify') === true ? ' selected="selected"' : ''; ?>><?php echo _t('gen.short.yes'); ?></option> + <option value=""<?= $this->feed->attributes('ssl_verify') === null ? ' selected="selected"' : '' ?>><?= _t('gen.short.by_default') ?></option> + <option value="0"<?= $this->feed->attributes('ssl_verify') === false ? ' selected="selected"' : '' ?>><?= _t('gen.short.no') ?></option> + <option value="1"<?= $this->feed->attributes('ssl_verify') === true ? ' selected="selected"' : '' ?>><?= _t('gen.short.yes') ?></option> </select> </label> </div> </div> <?php } ?> - <legend><?php echo _t('sub.feed.filteractions'); ?></legend> + <legend><?= _t('sub.feed.filteractions') ?></legend> <div class="form-group"> - <label class="group-name" for="filteractions_read"><?php echo _t('conf.reading.read.when'); ?></label> + <label class="group-name" for="filteractions_read"><?= _t('conf.reading.read.when') ?></label> <div class="group-controls"> <textarea name="filteractions_read" id="filteractions_read"><?php foreach ($this->feed->filtersAction('read') as $filterRead) { echo htmlspecialchars($filterRead->getRawInput(), ENT_NOQUOTES, 'UTF-8'), PHP_EOL; } ?></textarea> - <?php echo _i('help'); ?> <?php echo _t('sub.feed.filteractions.help'); ?> + <?= _i('help') ?> <?= _t('sub.feed.filteractions.help') ?> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> diff --git a/app/views/helpers/index/normal/entry_bottom.phtml b/app/views/helpers/index/normal/entry_bottom.phtml index c0edbdf7d..dd84ca346 100644 --- a/app/views/helpers/index/normal/entry_bottom.phtml +++ b/app/views/helpers/index/normal/entry_bottom.phtml @@ -19,7 +19,7 @@ if ($this->entry->isRead()) { $arUrl['params']['is_read'] = 0; } - ?><a class="read" href="<?php echo Minz_Url::display($arUrl); ?>"><?php + ?><a class="read" href="<?= Minz_Url::display($arUrl) ?>"><?php echo _i($this->entry->isRead() ? 'read' : 'unread'); ?></a><?php ?></li><?php } @@ -29,7 +29,7 @@ if ($this->entry->isFavorite()) { $arUrl['params']['is_favorite'] = 0; } - ?><a class="bookmark" href="<?php echo Minz_Url::display($arUrl); ?>"><?php + ?><a class="bookmark" href="<?= Minz_Url::display($arUrl) ?>"><?php echo _i($this->entry->isFavorite() ? 'starred' : 'non-starred'); ?></a><?php ?></li><?php } @@ -37,9 +37,9 @@ if ($bottomline_labels) { ?><li class="item"> <div class="dropdown dynamictags"> - <div id="dropdown-labels-<?php echo $this->entry->id();?>" class="dropdown-target"></div> - <?php echo FreshRSS_Themes::alt('label'); ?> - <a class="dropdown-toggle" href="#dropdown-labels-<?php echo $this->entry->id();?>"><?php + <div id="dropdown-labels-<?= $this->entry->id() ?>" class="dropdown-target"></div> + <?= FreshRSS_Themes::alt('label') ?> + <a class="dropdown-toggle" href="#dropdown-labels-<?= $this->entry->id() ?>"><?php echo _t('index.menu.tags'); ?></a> <ul class="dropdown-menu dropdown-menu-scrollable"> @@ -53,15 +53,15 @@ if (!empty($tags)) { ?><li class="item"> <div class="dropdown"> - <div id="dropdown-tags-<?php echo $this->entry->id();?>" class="dropdown-target"></div> - <?php echo _i('tag'); ?> - <a class="dropdown-toggle" href="#dropdown-tags-<?php echo $this->entry->id();?>"><?php + <div id="dropdown-tags-<?= $this->entry->id() ?>" class="dropdown-target"></div> + <?= _i('tag') ?> + <a class="dropdown-toggle" href="#dropdown-tags-<?= $this->entry->id() ?>"><?php echo _t('index.tag.related'); ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li><?php foreach ($tags as $tag) { - ?><li class="item"><a href="<?php echo _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))); ?>"><?php echo $tag; ?></a></li><?php + ?><li class="item"><a href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>"><?= $tag ?></a></li><?php } ?> </ul> </div> @@ -70,10 +70,10 @@ ?><li class="item"><?php if ($bottomline_sharing) { ?><div class="dropdown"> - <div id="dropdown-share-<?php echo $this->entry->id();?>" class="dropdown-target"></div> - <a class="dropdown-toggle" href="#dropdown-share-<?php echo $this->entry->id();?>"> - <?php echo _i('share'); ?> - <?php echo _t('index.share'); ?> + <div id="dropdown-share-<?= $this->entry->id() ?>" class="dropdown-target"></div> + <a class="dropdown-toggle" href="#dropdown-share-<?= $this->entry->id() ?>"> + <?= _i('share') ?> + <?= _t('index.share') ?> </a> <ul class="dropdown-menu"> @@ -90,11 +90,11 @@ $share->update($share_options); ?><li class="item share"> <?php if ('GET' === $share->method()) {?> - <a target="_blank" rel="noreferrer" href="<?php echo $share->url(); ?>"><?php echo $share->name(); ?></a> + <a target="_blank" rel="noreferrer" href="<?= $share->url() ?>"><?= $share->name() ?></a> <?php } else {?> - <a href="POST"><?php echo $share->name(); ?></a> - <form method="POST" action="<?php echo $share->url(); ?>" disabled="disabled"> - <input type="hidden" value="<?php echo $link; ?>" name="<?php echo $share->field(); ?>"/> + <a href="POST"><?= $share->name() ?></a> + <form method="POST" action="<?= $share->url() ?>" disabled="disabled"> + <input type="hidden" value="<?= $link ?>" name="<?= $share->field() ?>"/> </form> <?php } ?> </li><?php @@ -104,9 +104,9 @@ <?php } ?> </li><?php if ($bottomline_date) { - ?><li class="item date"><?php echo $this->entry->date(); ?></li><?php + ?><li class="item date"><?= $this->entry->date() ?></li><?php } if ($bottomline_link) { - ?><li class="item link"><a target="_blank" rel="noreferrer" href="<?php echo $this->entry->link(); ?>"><?php echo _i('link'); ?></a></li><?php + ?><li class="item link"><a target="_blank" rel="noreferrer" href="<?= $this->entry->link() ?>"><?= _i('link') ?></a></li><?php } ?> </ul> diff --git a/app/views/helpers/index/normal/entry_header.phtml b/app/views/helpers/index/normal/entry_header.phtml index 86298e59f..d22cf5036 100644 --- a/app/views/helpers/index/normal/entry_header.phtml +++ b/app/views/helpers/index/normal/entry_header.phtml @@ -1,6 +1,7 @@ <?php $topline_read = FreshRSS_Context::$user_conf->topline_read; $topline_favorite = FreshRSS_Context::$user_conf->topline_favorite; + $topline_display_authors = FreshRSS_Context::$user_conf->topline_display_authors; $topline_date = FreshRSS_Context::$user_conf->topline_date; $topline_link = FreshRSS_Context::$user_conf->topline_link; ?><ul class="horizontal-list flux_header"><?php @@ -11,7 +12,7 @@ if ($this->entry->isRead()) { $arUrl['params']['is_read'] = 0; } - ?><a class="read" href="<?php echo Minz_Url::display($arUrl); ?>"><?php + ?><a class="read" href="<?= Minz_Url::display($arUrl) ?>"><?php echo _i($this->entry->isRead() ? 'read' : 'unread'); ?></a><?php ?></li><?php } @@ -21,13 +22,26 @@ if ($this->entry->isFavorite()) { $arUrl['params']['is_favorite'] = 0; } - ?><a class="bookmark" href="<?php echo Minz_Url::display($arUrl); ?>"><?php + ?><a class="bookmark" href="<?= Minz_Url::display($arUrl) ?>"><?php echo _i($this->entry->isFavorite() ? 'starred' : 'non-starred'); ?></a><?php ?></li><?php } } - ?><li class="item website"><a href="<?php echo _url('index', 'index', 'get', 'f_' . $this->feed->id()); ?>"><img class="favicon" src="<?php echo $this->feed->favicon(); ?>" alt="✇" /> <span><?php echo $this->feed->name(); ?></span></a></li> - <li class="item title"><a target="_blank" rel="noreferrer" href="<?php echo $this->entry->link(); ?>"><?php echo $this->entry->title(); ?></a></li> - <?php if ($topline_date) { ?><li class="item date"><?php echo $this->entry->date(); ?> </li><?php } ?> - <?php if ($topline_link) { ?><li class="item link"><a target="_blank" rel="noreferrer" href="<?php echo $this->entry->link(); ?>"><?php echo _i('link'); ?></a></li><?php } ?> + ?><li class="item website"><a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>"><img class="favicon" src="<?= $this->feed->favicon() ?>" alt="✇" /> <span><?= $this->feed->name() ?></span></a></li> + <li class="item title"><a target="_blank" rel="noreferrer" href="<?= $this->entry->link() ?>"><?= $this->entry->title() ?></a><?php + if ($topline_display_authors): + ?><div class="item author"><?php + $authors = $this->entry->authors(); + if (is_array($authors)) { + $first = true; + foreach ($authors as $author) { + echo $first ? $author : ', ' . $author; + $first = false; + } + } + ?></div><?php + endif; + ?></li> + <?php if ($topline_date) { ?><li class="item date"><?= $this->entry->date() ?> </li><?php } ?> + <?php if ($topline_link) { ?><li class="item link"><a target="_blank" rel="noreferrer" href="<?= $this->entry->link() ?>"><?= _i('link') ?></a></li><?php } ?> </ul> diff --git a/app/views/helpers/logs_pagination.phtml b/app/views/helpers/logs_pagination.phtml index bf9d91f04..e74074173 100755 --- a/app/views/helpers/logs_pagination.phtml +++ b/app/views/helpers/logs_pagination.phtml @@ -9,14 +9,14 @@ <?php $params[$getteur] = 1; ?> <li class="item pager-first"> <?php if ($this->currentPage > 1) { ?> - <a href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>">« <?php echo _t('gen.pagination.first'); ?></a> + <a href="<?= Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)) ?>">« <?= _t('gen.pagination.first') ?></a> <?php } ?> </li> <?php $params[$getteur] = $this->currentPage - 1; ?> <li class="item pager-previous"> <?php if ($this->currentPage > 1) { ?> - <a href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>">‹ <?php echo _t('gen.pagination.previous'); ?></a> + <a href="<?= Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)) ?>">‹ <?= _t('gen.pagination.previous') ?></a> <?php } ?> </li> @@ -24,9 +24,9 @@ <?php if($i > 0 && $i <= $this->nbPage) { ?> <?php if ($i != $this->currentPage) { ?> <?php $params[$getteur] = $i; ?> - <li class="item pager-item"><a href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>"><?php echo $i; ?></a></li> + <li class="item pager-item"><a href="<?= Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)) ?>"><?= $i ?></a></li> <?php } else { ?> - <li class="item pager-current"><?php echo $i; ?></li> + <li class="item pager-current"><?= $i ?></li> <?php } ?> <?php } ?> <?php } ?> @@ -34,13 +34,13 @@ <?php $params[$getteur] = $this->currentPage + 1; ?> <li class="item pager-next"> <?php if ($this->currentPage < $this->nbPage) { ?> - <a href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>"><?php echo _t('gen.pagination.next'); ?> ›</a> + <a href="<?= Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)) ?>"><?= _t('gen.pagination.next') ?> ›</a> <?php } ?> </li> <?php $params[$getteur] = $this->nbPage; ?> <li class="item pager-last"> <?php if ($this->currentPage < $this->nbPage) { ?> - <a href="<?php echo Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)); ?>"><?php echo _t('gen.pagination.last'); ?> »</a> + <a href="<?= Minz_Url::display(array('c' => $c, 'a' => $a, 'params' => $params)) ?>"><?= _t('gen.pagination.last') ?> »</a> <?php } ?> </li> </ul> diff --git a/app/views/helpers/pagination.phtml b/app/views/helpers/pagination.phtml index fc37ce3f5..b7d62ceab 100755 --- a/app/views/helpers/pagination.phtml +++ b/app/views/helpers/pagination.phtml @@ -18,26 +18,26 @@ ?> <form id="mark-read-pagination" method="post"> -<input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> +<input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <ul class="pagination"> <li class="item pager-next"> <?php if (FreshRSS_Context::$next_id) { ?> - <a id="load_more" href="<?php echo Minz_Url::display($url_next); ?>"> - <?php echo _t('gen.pagination.load_more'); ?> + <a id="load_more" href="<?= Minz_Url::display($url_next) ?>"> + <?= _t('gen.pagination.load_more') ?> </a> <?php } elseif ($url_mark_read) { ?> <button id="bigMarkAsRead" - class="as-link <?php echo FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : ''; ?>" + class="as-link <?= FreshRSS_Context::$user_conf->reading_confirm ? 'confirm" disabled="disabled' : '' ?>" form="mark-read-pagination" - formaction="<?php echo Minz_Url::display($url_mark_read); ?>" + formaction="<?= Minz_Url::display($url_mark_read) ?>" type="submit"> - <?php echo _t('gen.pagination.nothing_to_load'); ?><br /> + <?= _t('gen.pagination.nothing_to_load') ?><br /> <span class="bigTick">✓</span><br /> - <?php echo _t('gen.pagination.mark_all_read'); ?> + <?= _t('gen.pagination.mark_all_read') ?> </button> <?php } else { ?> <a id="bigMarkAsRead" href="."> - <?php echo _t('gen.pagination.nothing_to_load'); ?><br /> + <?= _t('gen.pagination.nothing_to_load') ?><br /> </a> <?php } ?> </li> diff --git a/app/views/importExport/index.phtml b/app/views/importExport/index.phtml index 139e715c5..e5a24dbc4 100644 --- a/app/views/importExport/index.phtml +++ b/app/views/importExport/index.phtml @@ -1,14 +1,14 @@ <?php $this->partial('aside_subscription'); ?> <div class="post "> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('importExport', 'import'); ?>" enctype="multipart/form-data"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('sub.import_export.import'); ?></legend> + <form method="post" action="<?= _url('importExport', 'import') ?>" enctype="multipart/form-data"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('sub.import_export.import') ?></legend> <div class="form-group"> <label class="group-name" for="file"> - <?php echo extension_loaded('zip') ? _t('sub.import_export.file_to_import') : _t('sub.import_export.file_to_import_no_zip'); ?> + <?= extension_loaded('zip') ? _t('sub.import_export.file_to_import') : _t('sub.import_export.file_to_import_no_zip') ?> </label> <div class="group-controls"> <input type="file" name="file" id="file" /> @@ -17,30 +17,30 @@ <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.import'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.import') ?></button> </div> </div> </form> <?php if (count($this->feeds) > 0) { ?> - <form method="post" action="<?php echo _url('importExport', 'export'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('sub.import_export.export'); ?></legend> + <form method="post" action="<?= _url('importExport', 'export') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('sub.import_export.export') ?></legend> <div class="form-group"> <div class="group-controls"> <label class="checkbox" for="export_opml"> <input type="checkbox" name="export_opml" id="export_opml" value="1" checked="checked" /> - <?php echo _t('sub.import_export.export_opml'); ?> + <?= _t('sub.import_export.export_opml') ?> </label> <label class="checkbox" for="export_labelled"> - <input type="checkbox" name="export_labelled" id="export_labelled" value="1" <?php echo extension_loaded('zip') ? 'checked="checked"' : ''; ?> /> - <?php echo _t('sub.import_export.export_labelled'); ?> + <input type="checkbox" name="export_labelled" id="export_labelled" value="1" <?= extension_loaded('zip') ? 'checked="checked"' : '' ?> /> + <?= _t('sub.import_export.export_labelled') ?> </label> <label class="checkbox" for="export_starred"> - <input type="checkbox" name="export_starred" id="export_starred" value="1" <?php echo extension_loaded('zip') ? 'checked="checked"' : ''; ?> /> - <?php echo _t('sub.import_export.export_starred'); ?> + <input type="checkbox" name="export_starred" id="export_starred" value="1" <?= extension_loaded('zip') ? 'checked="checked"' : '' ?> /> + <?= _t('sub.import_export.export_starred') ?> </label> <?php @@ -49,10 +49,10 @@ $select_args = ' size="' . min(10, count($this->feeds)) .'" multiple="multiple"'; } ?> - <select name="export_feeds[]"<?php echo $select_args; ?> size="10"> - <?php echo extension_loaded('zip') ? '' : '<option></option>'; ?> + <select name="export_feeds[]"<?= $select_args ?> size="10"> + <?= extension_loaded('zip') ? '' : '<option></option>' ?> <?php foreach ($this->feeds as $feed) { ?> - <option value="<?php echo $feed->id(); ?>"><?php echo $feed->name(); ?></option> + <option value="<?= $feed->id() ?>"><?= $feed->name() ?></option> <?php } ?> </select> </div> @@ -60,7 +60,7 @@ <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.export'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.export') ?></button> </div> </div> </form> diff --git a/app/views/index/about.phtml b/app/views/index/about.phtml index 649729952..320847886 100644 --- a/app/views/index/about.phtml +++ b/app/views/index/about.phtml @@ -1,26 +1,26 @@ <div class="post content"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('index.about'); ?></h1> + <h1><?= _t('index.about') ?></h1> <dl class="infos"> - <dt><?php echo _t('index.about.project_website'); ?></dt> - <dd><a href="<?php echo FRESHRSS_WEBSITE; ?>"><?php echo FRESHRSS_WEBSITE; ?></a></dd> + <dt><?= _t('index.about.project_website') ?></dt> + <dd><a href="<?= FRESHRSS_WEBSITE ?>"><?= FRESHRSS_WEBSITE ?></a></dd> - <dt><?php echo _t('index.about.bugs_reports'); ?></dt> - <dd><?php echo _t('index.about.github'); ?></dd> + <dt><?= _t('index.about.bugs_reports') ?></dt> + <dd><?= _t('index.about.github') ?></dd> - <dt><?php echo _t('index.about.license'); ?></dt> - <dd><?php echo _t('index.about.agpl3'); ?></dd> + <dt><?= _t('index.about.license') ?></dt> + <dd><?= _t('index.about.agpl3') ?></dd> <?php if (FreshRSS_Auth::hasAccess()): ?> - <dt><?php echo _t('index.about.version'); ?></dt> - <dd><?php echo FRESHRSS_VERSION; ?></dd> + <dt><?= _t('index.about.version') ?></dt> + <dd><?= FRESHRSS_VERSION ?></dd> <?php endif; ?> </dl> - <p><?php echo _t('index.about.freshrss_description'); ?></p> + <p><?= _t('index.about.freshrss_description') ?></p> - <h1><?php echo _t('index.about.credits'); ?></h1> - <p><?php echo _t('index.about.credits_content'); ?></p> + <h1><?= _t('index.about.credits') ?></h1> + <p><?= _t('index.about.credits_content') ?></p> </div> diff --git a/app/views/index/global.phtml b/app/views/index/global.phtml index 2f25b6dc2..a49e16525 100644 --- a/app/views/index/global.phtml +++ b/app/views/index/global.phtml @@ -9,7 +9,7 @@ } ?> -<div id="stream" class="global<?php echo $class; ?>"> +<div id="stream" class="global<?= $class ?>"> <?php $params = Minz_Request::fetchGET(); unset($params['c']); @@ -26,8 +26,8 @@ if (!empty($feeds)) { ?> - <div class="box category" data-unread="<?php echo $cat->nbNotRead(); ?>"> - <div class="box-title"><a class="title" data-unread="<?php echo format_number($cat->nbNotRead()); ?>" href="<?php echo Minz_Url::display($url_base); ?>"><?php echo $cat->name(); ?></a></div> + <div class="box category" data-unread="<?= $cat->nbNotRead() ?>"> + <div class="box-title"><a class="title" data-unread="<?= format_number($cat->nbNotRead()) ?>" href="<?= Minz_Url::display($url_base) ?>"><?= $cat->name() ?></a></div> <ul class="box-content"> <?php @@ -37,9 +37,9 @@ $empty = $feed->nbEntries() === 0 ? ' empty' : ''; $url_base['params']['get'] = 'f_' . $feed->id(); ?> - <li id="f_<?php echo $feed->id(); ?>" class="item feed<?php echo $error, $empty, $feed->mute() ? ' mute' : ''; ?>" data-unread="<?php echo $feed->nbNotRead(); ?>" data-priority="<?php echo $feed->priority(); ?>"> - <img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> - <a class="item-title" data-unread="<?php echo format_number($feed->nbNotRead()); ?>" href="<?php echo Minz_Url::display($url_base); ?>"><?php echo $feed->name(); ?></a> + <li id="f_<?= $feed->id() ?>" class="item feed<?= $error, $empty, $feed->mute() ? ' mute' : '' ?>" data-unread="<?= $feed->nbNotRead() ?>" data-priority="<?= $feed->priority() ?>"> + <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" /> + <a class="item-title" data-unread="<?= format_number($feed->nbNotRead()) ?>" href="<?= Minz_Url::display($url_base) ?>"><?= $feed->name() ?></a> </li> <?php } ?> </ul> @@ -51,7 +51,7 @@ </div> <div id="overlay"> - <a class="close" href="#"><?php echo _i('close'); ?></a> + <a class="close" href="#"><?= _i('close') ?></a> </div> -<div id="panel"<?php echo FreshRSS_Context::$user_conf->display_posts ? '' : ' class="hide_posts"'; ?>> +<div id="panel"<?= FreshRSS_Context::$user_conf->display_posts ? '' : ' class="hide_posts"' ?>> </div> diff --git a/app/views/index/logs.phtml b/app/views/index/logs.phtml index a88f89278..2deb1c315 100644 --- a/app/views/index/logs.phtml +++ b/app/views/index/logs.phtml @@ -1,11 +1,11 @@ <div class="post content"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('index.log'); ?></h1> - <form method="post" action="<?php echo _url('index', 'logs'); ?>"><p> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <h1><?= _t('index.log') ?></h1> + <form method="post" action="<?= _url('index', 'logs') ?>"><p> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <input type="hidden" name="clearLogs" /> - <button type="submit" class="btn"><?php echo _t('index.log.clear'); ?></button> + <button type="submit" class="btn"><?= _t('index.log.clear') ?></button> </p></form> <?php $items = $this->logsPaginator->items(); ?> @@ -15,12 +15,12 @@ <?php $this->logsPaginator->render('logs_pagination.phtml', 'page'); ?> <?php foreach ($items as $log) { ?> - <div class="log <?php echo $log->level(); ?>"><span class="date"><?php echo @date('Y-m-d H:i:s', @strtotime($log->date())); ?></span><?php echo htmlspecialchars($log->info(), ENT_NOQUOTES, 'UTF-8'); ?></div> + <div class="log <?= $log->level() ?>"><span class="date"><?= @date('Y-m-d H:i:s', @strtotime($log->date())) ?></span><?= htmlspecialchars($log->info(), ENT_NOQUOTES, 'UTF-8') ?></div> <?php } ?> <?php $this->logsPaginator->render('logs_pagination.phtml','page'); ?> </div> <?php } else { ?> - <p class="alert alert-warn"><?php echo _t('index.log.empty'); ?></p> + <p class="alert alert-warn"><?= _t('index.log.empty') ?></p> <?php } ?> </div> diff --git a/app/views/index/normal.phtml b/app/views/index/normal.phtml index ac2ea812d..f556df201 100644 --- a/app/views/index/normal.phtml +++ b/app/views/index/normal.phtml @@ -14,9 +14,9 @@ if (!empty($this->entries)) { $today = @strtotime('today'); ?> -<div id="stream" class="normal<?php echo $hidePosts ? ' hide_posts' : ''; ?>"><?php +<div id="stream" class="normal<?= $hidePosts ? ' hide_posts' : '' ?>"><?php ?><div id="new-article"> - <a href="<?php echo Minz_Url::display(Minz_Request::currentRequest()); ?>"><?php echo _t('gen.js.new_article'); /* TODO: move string in JS*/ ?></a> + <a href="<?= Minz_Url::display(Minz_Request::currentRequest()) ?>"><?= _t('gen.js.new_article'); /* TODO: move string in JS*/ ?></a> </div><?php foreach ($this->entries as $item) { $this->entry = Minz_ExtensionManager::callHook('entry_before_display', $item); @@ -36,23 +36,23 @@ if (!empty($this->entries)) { if ($display_today && $this->entry->isDay(FreshRSS_Days::TODAY, $today)) { ?><div class="day" id="day_today"><?php echo _t('gen.date.today'); - ?><span class="date"> — <?php echo timestamptodate(time(), false); ?></span><?php - ?><span class="name"><?php echo FreshRSS_Context::$name; ?></span><?php + ?><span class="date"> — <?= timestamptodate(time(), false) ?></span><?php + ?><span class="name"><?= FreshRSS_Context::$name ?></span><?php ?></div><?php $display_today = false; } if ($display_yesterday && $this->entry->isDay(FreshRSS_Days::YESTERDAY, $today)) { ?><div class="day" id="day_yesterday"><?php echo _t('gen.date.yesterday'); - ?><span class="date"> — <?php echo timestamptodate(time() - 86400, false); ?></span><?php - ?><span class="name"><?php echo FreshRSS_Context::$name; ?></span><?php + ?><span class="date"> — <?= timestamptodate(time() - 86400, false) ?></span><?php + ?><span class="name"><?= FreshRSS_Context::$name ?></span><?php ?></div><?php $display_yesterday = false; } if ($display_others && $this->entry->isDay(FreshRSS_Days::BEFORE_YESTERDAY, $today)) { ?><div class="day" id="day_before_yesterday"><?php echo _t('gen.date.before_yesterday'); - ?><span class="name"><?php echo FreshRSS_Context::$name; ?></span><?php + ?><span class="name"><?= FreshRSS_Context::$name ?></span><?php ?></div><?php $display_others = false; } @@ -65,8 +65,8 @@ if (!empty($this->entries)) { $this->renderHelper('index/normal/entry_header'); ?><div class="flux_content"> - <div class="content <?php echo $content_width; ?>"> - <h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?php echo $this->entry->link(); ?>"><?php echo $this->entry->title(); ?></a></h1> + <div class="content <?= $content_width ?>"> + <h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $this->entry->link() ?>"><?= $this->entry->title() ?></a></h1> <div class="author"><?php $authors = $this->entry->authors(); if (is_array($authors)): @@ -75,7 +75,7 @@ if (!empty($this->entries)) { echo $first ? _t('gen.short.by_author') . ' ' : '· '; $first = false; ?> -<em><a href="<?php echo _url('index', 'index', 'search', 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))); ?>"><?php echo $author; ?></a></em> +<em><a href="<?= _url('index', 'index', 'search', 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))) ?>"><?= $author ?></a></em> <?php endforeach; ?> </div><?php endif; @@ -96,7 +96,7 @@ if (!empty($this->entries)) { <?php } else { ?> <div id="stream" class="prompt alert alert-warn normal"> - <h2><?php echo _t('index.feed.empty'); ?></h2> - <a href="<?php echo _url('subscription', 'index'); ?>"><?php echo _t('index.feed.add'); ?></a><br /><br /> + <h2><?= _t('index.feed.empty') ?></h2> + <a href="<?= _url('subscription', 'index') ?>"><?= _t('index.feed.add') ?></a><br /><br /> </div> <?php } ?> diff --git a/app/views/index/reader.phtml b/app/views/index/reader.phtml index 129fae937..bbb6bd22b 100644 --- a/app/views/index/reader.phtml +++ b/app/views/index/reader.phtml @@ -9,16 +9,16 @@ if (!empty($this->entries)) { <div id="stream" class="reader"> <div id="new-article"> - <a href="<?php echo Minz_Url::display(Minz_Request::currentRequest()); ?>"><?php echo _t('gen.js.new_article'); /* TODO: move string in JS*/ ?></a> + <a href="<?= Minz_Url::display(Minz_Request::currentRequest()) ?>"><?= _t('gen.js.new_article'); /* TODO: move string in JS*/ ?></a> </div> <?php foreach ($this->entries as $item) { $item = Minz_ExtensionManager::callHook('entry_before_display', $item); if (is_null($item)) { continue; } - ?><div class="flux<?php echo !$item->isRead() ? ' not_read' : ''; ?><?php echo $item->isFavorite() ? ' favorite' : ''; ?>" id="flux_<?php echo $item->id(); ?>"> + ?><div class="flux<?= !$item->isRead() ? ' not_read' : '' ?><?= $item->isFavorite() ? ' favorite' : '' ?>" id="flux_<?= $item->id() ?>"> <div class="flux_content"> - <div class="content <?php echo $content_width; ?>"> + <div class="content <?= $content_width ?>"> <?php $feed = FreshRSS_CategoryDAO::findFeed($this->categories, $item->feed()); //We most likely already have the feed object in cache if (empty($feed)) $feed = $item->feed(true); @@ -31,16 +31,16 @@ if (!empty($this->entries)) { $readUrl['params']['is_read'] = 0; } ?> - <a class="read" href="<?php echo Minz_Url::display($readUrl); ?>"> - <?php echo _i($item->isRead() ? 'read' : 'unread'); ?> + <a class="read" href="<?= Minz_Url::display($readUrl) ?>"> + <?= _i($item->isRead() ? 'read' : 'unread') ?> </a> - <a class="bookmark" href="<?php echo Minz_Url::display($favoriteUrl); ?>"> - <?php echo _i($item->isFavorite() ? 'starred' : 'non-starred'); ?> + <a class="bookmark" href="<?= Minz_Url::display($favoriteUrl) ?>"> + <?= _i($item->isFavorite() ? 'starred' : 'non-starred') ?> </a> - <a class="website" href="<?php echo _url('index', 'reader', 'get', 'f_' . $feed->id()); ?>"> - <img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> <span><?php echo $feed->name(); ?></span> + <a class="website" href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>"> + <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" /> <span><?= $feed->name() ?></span> </a> - <h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?php echo $item->link(); ?>"><?php echo $item->title(); ?></a></h1> + <h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $item->link() ?>"><?= $item->title() ?></a></h1> <div class="author"><?php $authors = $item->authors(); @@ -50,7 +50,7 @@ if (!empty($this->entries)) { echo $first ? _t('gen.short.by_author') . ' ' : '· '; $first = false; ?> -<em><a href="<?php echo _url('index', 'index', 'search', 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))); ?>"><?php echo $author; ?></a></em> +<em><a href="<?= _url('index', 'index', 'search', 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))) ?>"><?= $author ?></a></em> <?php endforeach; echo ' — '; @@ -58,7 +58,7 @@ if (!empty($this->entries)) { echo $item->date(); ?></div> - <?php echo $item->content(); ?> + <?= $item->content() ?> </div> </div> </div> @@ -69,7 +69,7 @@ if (!empty($this->entries)) { <?php } else { ?> <div id="stream" class="prompt alert alert-warn reader"> - <h2><?php echo _t('index.feed.empty'); ?></h2> - <a href="<?php echo _url('subscription', 'index'); ?>"><?php echo _t('index.feed.add'); ?></a><br /><br /> + <h2><?= _t('index.feed.empty') ?></h2> + <a href="<?= _url('subscription', 'index') ?>"><?= _t('index.feed.add') ?></a><br /><br /> </div> <?php } ?> diff --git a/app/views/index/rss.phtml b/app/views/index/rss.phtml index 104e03d15..00be01c28 100755 --- a/app/views/index/rss.phtml +++ b/app/views/index/rss.phtml @@ -1,23 +1,23 @@ -<?php echo '<?xml version="1.0" encoding="UTF-8" ?>'; ?> +<?= '<?xml version="1.0" encoding="UTF-8" ?>'; ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"> <channel> - <title><?php echo $this->rss_title; ?></title> - <link><?php echo Minz_Url::display(null, 'html', true); ?></link> - <description><?php echo _t('index.feed.rss_of', $this->rss_title); ?></description> - <pubDate><?php echo date('D, d M Y H:i:s O'); ?></pubDate> - <lastBuildDate><?php echo gmdate('D, d M Y H:i:s'); ?> GMT</lastBuildDate> - <atom:link href="<?php echo Minz_Url::display($this->url, 'html', true); ?>" rel="self" type="application/rss+xml" /> + <title><?= $this->rss_title ?></title> + <link><?= Minz_Url::display(null, 'html', true) ?></link> + <description><?= _t('index.feed.rss_of', $this->rss_title) ?></description> + <pubDate><?= date('D, d M Y H:i:s O') ?></pubDate> + <lastBuildDate><?= gmdate('D, d M Y H:i:s') ?> GMT</lastBuildDate> + <atom:link href="<?= Minz_Url::display($this->url, 'html', true) ?>" rel="self" type="application/rss+xml" /> <?php foreach ($this->entries as $item) { ?> <item> - <title><?php echo $item->title(); ?></title> - <link><?php echo $item->link(); ?></link> + <title><?= $item->title() ?></title> + <link><?= $item->link() ?></link> <?php $authors = $item->authors(); if (is_array($authors)) { foreach ($authors as $author) { - echo "\t\t\t" , '<author>', $author, '</author>', "\n"; + echo "\t\t\t" , '<dc:creator>', $author, '</dc:creator>', "\n"; } } $categories = $item->tags(); @@ -30,8 +30,8 @@ foreach ($this->entries as $item) { <description><![CDATA[<?php echo $item->content(); ?>]]></description> - <pubDate><?php echo date('D, d M Y H:i:s O', $item->date(true)); ?></pubDate> - <guid isPermaLink="false"><?php echo $item->id(); ?></guid> + <pubDate><?= date('D, d M Y H:i:s O', $item->date(true)) ?></pubDate> + <guid isPermaLink="false"><?= $item->id() ?></guid> </item> <?php } ?> diff --git a/app/views/index/tos.phtml b/app/views/index/tos.phtml new file mode 100644 index 000000000..38dd0add6 --- /dev/null +++ b/app/views/index/tos.phtml @@ -0,0 +1,13 @@ +<div class="post content"> + <?php if ($this->can_register) { ?> + <a href="<?= _url('auth', 'register') ?>"> + <?= _t('gen.action.back') ?> + </a> + <?php } else { ?> + <a href="<?= _url('index', 'index') ?>"> + <?= _t('gen.action.back') ?> + </a> + <?php } ?> + + <?= $this->terms_of_service ?> +</div> diff --git a/app/views/stats/idle.phtml b/app/views/stats/idle.phtml index 88c78d465..9421893fb 100644 --- a/app/views/stats/idle.phtml +++ b/app/views/stats/idle.phtml @@ -1,9 +1,9 @@ <?php $this->partial('aside_stats'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('admin.stats.idle'); ?></h1> + <h1><?= _t('admin.stats.idle') ?></h1> <?php $current_url = Minz_Url::display( @@ -16,21 +16,21 @@ $nothing = false; ?> <div class="stat"> - <h2><?php echo _t('gen.date.' . $period); ?></h2> + <h2><?= _t('gen.date.' . $period) ?></h2> <form id="form-delete" method="post"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <?php foreach ($feeds as $feed) { ?> <ul class="horizontal-list"> <li class="item"> <div class="stick"> - <a class="btn" href="<?php echo _url('index', 'index', 'get', 'f_' . $feed['id']); ?>"><?php echo _i('link'); ?> <?php echo _t('gen.action.filter'); ?></a> - <a class="btn" href="<?php echo _url('subscription', 'index', 'id', $feed['id']); ?>"><?php echo _i('configure'); ?> <?php echo _t('gen.action.manage'); ?></a> - <button class="btn btn-attention confirm" form="form-delete" formaction="<?php echo _url('feed', 'delete', 'id', $feed['id'], 'r', $current_url); ?>"><?php echo _t('gen.action.remove'); ?></button> + <a class="btn" href="<?= _url('index', 'index', 'get', 'f_' . $feed['id']) ?>"><?= _i('link') ?> <?= _t('gen.action.filter') ?></a> + <a class="btn" href="<?= _url('subscription', 'index', 'id', $feed['id']) ?>"><?= _i('configure') ?> <?= _t('gen.action.manage') ?></a> + <button class="btn btn-attention confirm" form="form-delete" formaction="<?= _url('feed', 'delete', 'id', $feed['id'], 'r', $current_url) ?>"><?= _t('gen.action.remove') ?></button> </div> </li> <li class="item"> - <span title="<?php echo timestamptodate($feed['last_date'], false); ?>"><?php echo $feed['name']; ?> (<?php echo _t('admin.stats.number_entries', $feed['nb_articles']); ?>)</span> + <span title="<?= timestamptodate($feed['last_date'], false) ?>"><?= $feed['name'] ?> (<?= _t('admin.stats.number_entries', $feed['nb_articles']) ?>)</span> </li> </ul> <?php } ?> @@ -43,7 +43,7 @@ if ($nothing) { ?> <p class="alert alert-warn"> - <span class="alert-head"><?php echo _t('admin.stats.no_idle'); ?></span> + <span class="alert-head"><?= _t('admin.stats.no_idle') ?></span> </p> <?php } ?> </div> diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml index 2ff3e6c52..4af197c5b 100644 --- a/app/views/stats/index.phtml +++ b/app/views/stats/index.phtml @@ -1,63 +1,63 @@ <?php $this->partial('aside_stats'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('admin.stats.main'); ?></h1> + <h1><?= _t('admin.stats.main') ?></h1> <div class="stat half"> - <h2><?php echo _t('admin.stats.entry_repartition'); ?></h2> + <h2><?= _t('admin.stats.entry_repartition') ?></h2> <table> <thead> <tr> <th> </th> - <th><?php echo _t('admin.stats.main_stream'); ?></th> - <th><?php echo _t('admin.stats.all_feeds'); ?></th> + <th><?= _t('admin.stats.main_stream') ?></th> + <th><?= _t('admin.stats.all_feeds') ?></th> </tr> </thead> <tbody> <tr> - <th><?php echo _t('admin.stats.status_total'); ?></th> - <td class="numeric"><?php echo format_number($this->repartition['main_stream']['total']); ?></td> - <td class="numeric"><?php echo format_number($this->repartition['all_feeds']['total']); ?></td> + <th><?= _t('admin.stats.status_total') ?></th> + <td class="numeric"><?= format_number($this->repartition['main_stream']['total']) ?></td> + <td class="numeric"><?= format_number($this->repartition['all_feeds']['total']) ?></td> </tr> <tr> - <th><?php echo _t('admin.stats.status_read'); ?></th> - <td class="numeric"><?php echo format_number($this->repartition['main_stream']['count_reads']); ?></td> - <td class="numeric"><?php echo format_number($this->repartition['all_feeds']['count_reads']); ?></td> + <th><?= _t('admin.stats.status_read') ?></th> + <td class="numeric"><?= format_number($this->repartition['main_stream']['count_reads']) ?></td> + <td class="numeric"><?= format_number($this->repartition['all_feeds']['count_reads']) ?></td> </tr> <tr> - <th><?php echo _t('admin.stats.status_unread'); ?></th> - <td class="numeric"><?php echo format_number($this->repartition['main_stream']['count_unreads']); ?></td> - <td class="numeric"><?php echo format_number($this->repartition['all_feeds']['count_unreads']); ?></td> + <th><?= _t('admin.stats.status_unread') ?></th> + <td class="numeric"><?= format_number($this->repartition['main_stream']['count_unreads']) ?></td> + <td class="numeric"><?= format_number($this->repartition['all_feeds']['count_unreads']) ?></td> </tr> <tr> - <th><?php echo _t('admin.stats.status_favorites'); ?></th> - <td class="numeric"><?php echo format_number($this->repartition['main_stream']['count_favorites']); ?></td> - <td class="numeric"><?php echo format_number($this->repartition['all_feeds']['count_favorites']); ?></td> + <th><?= _t('admin.stats.status_favorites') ?></th> + <td class="numeric"><?= format_number($this->repartition['main_stream']['count_favorites']) ?></td> + <td class="numeric"><?= format_number($this->repartition['all_feeds']['count_favorites']) ?></td> </tr> </tbody> </table> </div><!-- --><div class="stat half"> - <h2><?php echo _t('admin.stats.top_feed'); ?></h2> + <h2><?= _t('admin.stats.top_feed') ?></h2> <table> <thead> <tr> - <th><?php echo _t('admin.stats.feed'); ?></th> - <th><?php echo _t('admin.stats.category'); ?></th> - <th><?php echo _t('admin.stats.entry_count'); ?></th> - <th><?php echo _t('admin.stats.percent_of_total'); ?></th> + <th><?= _t('admin.stats.feed') ?></th> + <th><?= _t('admin.stats.category') ?></th> + <th><?= _t('admin.stats.entry_count') ?></th> + <th><?= _t('admin.stats.percent_of_total') ?></th> </tr> </thead> <tbody> <?php foreach ($this->topFeed as $feed) { ?> <tr> - <td><a href="<?php echo _url('stats', 'repartition', 'id', $feed['id']); ?>"><?php echo $feed['name']; ?></a></td> - <td><?php echo $feed['category']; ?></td> - <td class="numeric"><?php echo format_number($feed['count']); ?></td> - <td class="numeric"><?php echo format_number($feed['count'] / $this->repartition['all_feeds']['total'] * 100, 1);?></td> + <td><a href="<?= _url('stats', 'repartition', 'id', $feed['id']) ?>"><?= $feed['name'] ?></a></td> + <td><?= $feed['category'] ?></td> + <td class="numeric"><?= format_number($feed['count']) ?></td> + <td class="numeric"><?= format_number($feed['count'] / $this->repartition['all_feeds']['total'] * 100, 1) ?></td> </tr> <?php } ?> </tbody> @@ -65,18 +65,18 @@ </div> <div class="stat"> - <h2><?php echo _t('admin.stats.entry_per_day'); ?></h2> + <h2><?= _t('admin.stats.entry_per_day') ?></h2> <div id="statsEntryPerDay" class="statGraph"></div> </div> <div class="stat half"> - <h2><?php echo _t('admin.stats.feed_per_category'); ?></h2> + <h2><?= _t('admin.stats.feed_per_category') ?></h2> <div id="statsFeedPerCategory" class="statGraph"></div> <div id="statsFeedPerCategoryLegend"></div> </div> <div class="stat half"> - <h2><?php echo _t('admin.stats.entry_per_category'); ?></h2> + <h2><?= _t('admin.stats.entry_per_category') ?></h2> <div id="statsEntryPerCategory" class="statGraph"></div> <div id="statsEntryPerCategoryLegend"></div> </div> @@ -90,4 +90,4 @@ echo htmlspecialchars(json_encode(array( 'entryByCategory' => $this->entryByCategory, ), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES, 'UTF-8'); ?></script> -<script src="../scripts/stats.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/stats.js'); ?>"></script> +<script src="../scripts/stats.js?<?= @filemtime(PUBLIC_PATH . '/scripts/stats.js') ?>"></script> diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml index 4bce418c9..7b445a7cc 100644 --- a/app/views/stats/repartition.phtml +++ b/app/views/stats/repartition.phtml @@ -1,12 +1,12 @@ <?php $this->partial('aside_stats'); ?> <div class="post "> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('admin.stats.repartition'); ?></h1> + <h1><?= _t('admin.stats.repartition') ?></h1> <select id="feed_select" class="select-change"> - <option data-url="<?php echo _url('stats', 'repartition')?>"><?php echo _t('admin.stats.all_feeds')?></option> + <option data-url="<?= _url('stats', 'repartition') ?>"><?= _t('admin.stats.all_feeds') ?></option> <?php foreach ($this->categories as $category) { $feeds = $category->feeds(); if (!empty($feeds)) { @@ -24,40 +24,40 @@ </select> <?php if ($this->feed) {?> - <a class="btn" href="<?php echo _url('subscription', 'index', 'id', $this->feed->id()); ?>"> - <?php echo _i('configure'); ?> <?php echo _t('gen.action.manage'); ?> + <a class="btn" href="<?= _url('subscription', 'index', 'id', $this->feed->id()) ?>"> + <?= _i('configure') ?> <?= _t('gen.action.manage') ?> </a> <?php }?> <div class="stat"> <table> <tr> - <th><?php echo _t('admin.stats.status_total'); ?></th> - <th><?php echo _t('admin.stats.status_read'); ?></th> - <th><?php echo _t('admin.stats.status_unread'); ?></th> - <th><?php echo _t('admin.stats.status_favorites'); ?></th> + <th><?= _t('admin.stats.status_total') ?></th> + <th><?= _t('admin.stats.status_read') ?></th> + <th><?= _t('admin.stats.status_unread') ?></th> + <th><?= _t('admin.stats.status_favorites') ?></th> </tr> <tr> - <td class="numeric"><?php echo $this->repartition['total']; ?></td> - <td class="numeric"><?php echo $this->repartition['count_reads']; ?></td> - <td class="numeric"><?php echo $this->repartition['count_unreads']; ?></td> - <td class="numeric"><?php echo $this->repartition['count_favorites']; ?></td> + <td class="numeric"><?= $this->repartition['total'] ?></td> + <td class="numeric"><?= $this->repartition['count_reads'] ?></td> + <td class="numeric"><?= $this->repartition['count_unreads'] ?></td> + <td class="numeric"><?= $this->repartition['count_favorites'] ?></td> </tr> </table> </div> <div class="stat"> - <h2><?php echo _t('admin.stats.entry_per_hour', $this->averageHour); ?></h2> + <h2><?= _t('admin.stats.entry_per_hour', $this->averageHour) ?></h2> <div id="statsEntryPerHour" class="statGraph"></div> </div> <div class="stat half"> - <h2><?php echo _t('admin.stats.entry_per_day_of_week', $this->averageDayOfWeek); ?></h2> + <h2><?= _t('admin.stats.entry_per_day_of_week', $this->averageDayOfWeek) ?></h2> <div id="statsEntryPerDayOfWeek" class="statGraph"></div> </div> <div class="stat half"> - <h2><?php echo _t('admin.stats.entry_per_month', $this->averageMonth); ?></h2> + <h2><?= _t('admin.stats.entry_per_month', $this->averageMonth) ?></h2> <div id="statsEntryPerMonth" class="statGraph"></div> </div> </div> @@ -71,4 +71,4 @@ echo htmlspecialchars(json_encode(array( 'months' => $this->months, ), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES, 'UTF-8'); ?></script> -<script src="../scripts/repartition.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/repartition.js'); ?>"></script> +<script src="../scripts/repartition.js?<?= @filemtime(PUBLIC_PATH . '/scripts/repartition.js') ?>"></script> diff --git a/app/views/subscription/bookmarklet.phtml b/app/views/subscription/bookmarklet.phtml index 76ac700e0..e6f311f58 100644 --- a/app/views/subscription/bookmarklet.phtml +++ b/app/views/subscription/bookmarklet.phtml @@ -1,17 +1,20 @@ <?php $this->partial('aside_subscription'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <legend><?php echo _t('sub.bookmarklet.title'); ?></legend> - <p><a class="btn btn-important" href="javascript:(function(){var%20url%20=%20location.href;var%20otherWindow=window.open('about:blank','_blank');otherWindow.opener=null;otherWindow.location='<?php echo Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true); ?>&url_rss='+encodeURIComponent(url);})();"><?php echo _t('sub.bookmarklet.label'); ?></a></p> - <?php echo _t('sub.bookmarklet.documentation'); ?> + <legend><?= _t('sub.bookmarklet.title') ?></legend> + <p><a class="btn btn-important" href="javascript:(function(){var%20url%20=%20location.href;var%20otherWindow=window.open('about:blank','_blank');otherWindow.opener=null;otherWindow.location='<?= Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true) ?>&url_rss='+encodeURIComponent(url);})();"><?= _t('sub.bookmarklet.label') ?></a></p> + <?= _t('sub.bookmarklet.documentation') ?> - <legend><?php echo _t('sub.firefox.title'); ?></legend> - <p><?php echo _t('sub.firefox.documentation'); ?></p> - <pre>browser.contentHandlers.types.number.uri → <?php echo Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true); ?>&url_rss=%s</pre> + <legend><?= _t('sub.firefox.title') ?></legend> + <p class="alert alert-warn"> + <?= _t('sub.firefox.obsolete_63', $this->default_category->name()) ?> + </p> + <p><?= _t('sub.firefox.documentation') ?></p> + <pre>browser.contentHandlers.types.number.uri → <?= Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true) ?>&url_rss=%s</pre> - <legend><?php echo _t('sub.api.title'); ?></legend> - <p><?php echo _t('sub.api.documentation'); ?></p> - <pre><?php echo Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true); ?>&url_rss=%s</pre> + <legend><?= _t('sub.api.title') ?></legend> + <p><?= _t('sub.api.documentation') ?></p> + <pre><?= Minz_Url::display(array('c' => 'feed', 'a' => 'add'), 'html', true) ?>&url_rss=%s</pre> </div>
\ No newline at end of file diff --git a/app/views/subscription/feed.phtml b/app/views/subscription/feed.phtml index 60664fdee..1a167777f 100644 --- a/app/views/subscription/feed.phtml +++ b/app/views/subscription/feed.phtml @@ -9,7 +9,7 @@ if ($this->feed) { } else { ?> <div class="alert alert-warn"> - <span class="alert-head"><?php echo _t('sub.feed.no_selected'); ?></span> - <?php echo _t('sub.feed.think_to_add'); ?> + <span class="alert-head"><?= _t('sub.feed.no_selected') ?></span> + <?= _t('sub.feed.think_to_add') ?> </div> <?php } ?> diff --git a/app/views/subscription/index.phtml b/app/views/subscription/index.phtml index 20f72ad66..da7593a8d 100644 --- a/app/views/subscription/index.phtml +++ b/app/views/subscription/index.phtml @@ -1,77 +1,77 @@ <?php $this->partial('aside_subscription'); ?> <div class="post drop-section"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h2><?php echo _t('sub.title'); ?></h2> + <h2><?= _t('sub.title') ?></h2> - <form id="add_rss" method="post" action="<?php echo _url('feed', 'add'); ?>" autocomplete="off"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <form id="add_rss" method="post" action="<?= _url('feed', 'add') ?>" autocomplete="off"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> <div class="stick"> - <input type="url" name="url_rss" class="long" placeholder="<?php echo _t('sub.feed.add'); ?>" /> + <input type="url" name="url_rss" class="long" placeholder="<?= _t('sub.feed.add') ?>" /> <div class="dropdown"> <div id="dropdown-cat" class="dropdown-target"></div> - <a class="dropdown-toggle btn" href="#dropdown-cat"><?php echo _i('down'); ?></a> + <a class="dropdown-toggle btn" href="#dropdown-cat"><?= _i('down') ?></a> <ul class="dropdown-menu"> <li class="dropdown-close"><a href="#close">❌</a></li> - <li class="dropdown-header"><?php echo _t('sub.category'); ?></li> + <li class="dropdown-header"><?= _t('sub.category') ?></li> <li class="input"> <select name="category" id="category"> <?php foreach ($this->categories as $cat) { ?> - <option value="<?php echo $cat->id(); ?>"<?php echo $cat->id() == 1 ? ' selected="selected"' : ''; ?>> - <?php echo $cat->name(); ?> + <option value="<?= $cat->id() ?>"<?= $cat->id() == 1 ? ' selected="selected"' : '' ?>> + <?= $cat->name() ?> </option> <?php } ?> - <option value="nc"><?php echo _t('sub.category.new'); ?></option> + <option value="nc"><?= _t('sub.category.new') ?></option> </select> </li> <li class="input" aria-hidden="true"> - <input type="text" name="new_category[name]" id="new_category_name" autocomplete="off" placeholder="<?php echo _t('sub.category.new'); ?>" /> + <input type="text" name="new_category[name]" id="new_category_name" autocomplete="off" placeholder="<?= _t('sub.category.new') ?>" /> </li> <li class="separator"></li> - <li class="dropdown-header"><?php echo _t('sub.feed.auth.http'); ?></li> + <li class="dropdown-header"><?= _t('sub.feed.auth.http') ?></li> <li class="input"> - <input type="text" name="http_user" id="http_user_feed" value=" " autocomplete="off" placeholder="<?php echo _t('sub.feed.auth.username'); ?>" /> + <input type="text" name="http_user" id="http_user_feed" value=" " autocomplete="off" placeholder="<?= _t('sub.feed.auth.username') ?>" /> </li> <li class="input"> - <input type="password" name="http_pass" id="http_pass_feed" autocomplete="new-password" placeholder="<?php echo _t('sub.feed.auth.password'); ?>" /> + <input type="password" name="http_pass" id="http_pass_feed" autocomplete="new-password" placeholder="<?= _t('sub.feed.auth.password') ?>" /> </li> </ul> </div> - <button class="btn" type="submit"><?php echo _i('add'); ?></button> + <button class="btn" type="submit"><?= _i('add') ?></button> </div> </form> <p class="alert alert-warn"> - <?php echo _t('sub.feed.moved_category_deleted', $this->default_category->name()); ?> + <?= _t('sub.feed.moved_category_deleted', $this->default_category->name()) ?> </p> <?php if ($this->onlyFeedsWithError): ?> <p class="alert alert-warn"> - <?php echo _t('sub.feed.showing.error'); ?> + <?= _t('sub.feed.showing.error') ?> </p> <?php endif; ?> <div class="box"> - <div class="box-title"><label for="new-category"><?php echo _t('sub.category.add'); ?></label></div> + <div class="box-title"><label for="new-category"><?= _t('sub.category.add') ?></label></div> <ul class="box-content box-content-centered"> - <form action="<?php echo _url('category', 'create'); ?>" method="post"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <li class="item"><input type="text" id="new-category" name="new-category" placeholder="<?php echo _t('sub.category.new'); ?>" /></li> - <li class="item"><button class="btn btn-important" type="submit"><?php echo _t('gen.action.submit'); ?></button></li> + <form action="<?= _url('category', 'create') ?>" method="post"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <li class="item"><input type="text" id="new-category" name="new-category" placeholder="<?= _t('sub.category.new') ?>" /></li> + <li class="item"><button class="btn btn-important" type="submit"><?= _t('gen.action.submit') ?></button></li> </form> </ul> </div> <form id="controller-category" method="post" aria-hidden="true"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> </form> <?php @@ -80,10 +80,10 @@ ?> <div class="box"> <div class="box-title"> - <a class="configure open-slider" href="<?php echo _url('subscription', 'category', 'id', $cat->id()); ?>"><?php echo _i('configure'); ?></a> - <?php echo $cat->name(); ?> + <a class="configure open-slider" href="<?= _url('subscription', 'category', 'id', $cat->id()) ?>"><?= _i('configure') ?></a> + <?= $cat->name() ?> </div> - <ul class="box-content" data-cat-id="<?php echo $cat->id(); ?>"> + <ul class="box-content" data-cat-id="<?= $cat->id() ?>"> <?php if (!empty($feeds)) { ?> <?php foreach ($feeds as $feed) { @@ -93,17 +93,17 @@ $error = $feed->inError() ? ' error' : ''; $empty = $feed->nbEntries() == 0 ? ' empty' : ''; ?> - <li class="item feed<?php echo $error, $empty, $feed->mute() ? ' mute': ''; ?>" + <li class="item feed<?= $error, $empty, $feed->mute() ? ' mute': '' ?>" draggable="true" - data-feed-id="<?php echo $feed->id(); ?>" + data-feed-id="<?= $feed->id() ?>" dropzone="move"> - <a class="configure open-slider" href="<?php echo _url('subscription', 'feed', 'id', $feed->id()); ?>"><?php echo _i('configure'); ?></a> - <img class="favicon" src="<?php echo $feed->favicon(); ?>" alt="✇" /> <?php echo $feed->name(); ?> + <a class="configure open-slider" href="<?= _url('subscription', 'feed', 'id', $feed->id()) ?>"><?= _i('configure') ?></a> + <img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" /> <?= $feed->name() ?> </li> <?php } } else { ?> - <li class="item disabled" dropzone="move"><?php echo _t('sub.category.empty'); ?></li> + <li class="item disabled" dropzone="move"><?= _t('sub.category.empty') ?></li> <?php } ?> </ul> </div> @@ -111,16 +111,16 @@ <ul> <?php if ($this->onlyFeedsWithError): ?> - <li><a href="<?php echo _url('subscription', 'index'); ?>"><?php echo _t('sub.feed.show.all'); ?></a></li> + <li><a href="<?= _url('subscription', 'index') ?>"><?= _t('sub.feed.show.all') ?></a></li> <?php else: ?> - <li><a href="<?php echo _url('subscription', 'index', 'error', 1); ?>"><?php echo _t('sub.feed.show.error'); ?></a></li> + <li><a href="<?= _url('subscription', 'index', 'error', 1) ?>"><?= _t('sub.feed.show.error') ?></a></li> <?php endif; ?> </ul> </div> <?php $class = $this->displaySlider ? ' class="active"' : ''; ?> -<a href="#" id="close-slider"<?php echo $class; ?>></a> -<div id="slider"<?php echo $class; ?>> +<a href="#" id="close-slider"<?= $class ?>></a> +<div id="slider"<?= $class ?>> <?php if (isset($this->feed)) { $this->renderHelper('feed/update'); diff --git a/app/views/update/apply.phtml b/app/views/update/apply.phtml index 8221929ae..c5e6884db 100644 --- a/app/views/update/apply.phtml +++ b/app/views/update/apply.phtml @@ -1,9 +1,9 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('admin.update'); ?></h1> + <h1><?= _t('admin.update') ?></h1> <?php ask_info_update(); ?> </div> diff --git a/app/views/update/checkInstall.phtml b/app/views/update/checkInstall.phtml index 33d78cbe7..183f914c0 100644 --- a/app/views/update/checkInstall.phtml +++ b/app/views/update/checkInstall.phtml @@ -1,15 +1,15 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h2><?php echo _t('admin.check_install.php'); ?></h2> + <h2><?= _t('admin.check_install.php') ?></h2> <?php foreach ($this->status_php as $key => $status) { ?> - <p class="alert <?php echo $status ? 'alert-success' : 'alert-error'; ?>"> + <p class="alert <?= $status ? 'alert-success' : 'alert-error' ?>"> <?php if ($key === 'php') { - echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok'), PHP_VERSION, '5.3.8'); + echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok'), PHP_VERSION, '5.6.0'); } else { echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')); } @@ -17,20 +17,20 @@ </p> <?php } ?> - <h2><?php echo _t('admin.check_install.files'); ?></h2> + <h2><?= _t('admin.check_install.files') ?></h2> <?php foreach ($this->status_files as $key => $status) { ?> - <p class="alert <?php echo $status ? 'alert-success' : 'alert-error'; ?>"> - <?php echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')); ?> + <p class="alert <?= $status ? 'alert-success' : 'alert-error' ?>"> + <?= _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')) ?> </p> <?php } ?> <?php /* - <h2><?php echo _t('admin.check_install.database'); ?></h2> + <h2><?= _t('admin.check_install.database') ?></h2> <?php foreach ($this->status_database as $key => $status) { ?> - <p class="alert <?php echo $status ? 'alert-success' : 'alert-error'; ?>"> - <?php echo _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')); ?> + <p class="alert <?= $status ? 'alert-success' : 'alert-error' ?>"> + <?= _t('admin.check_install.' . $key . '.' . ($status ? 'ok' : 'nok')) ?> </p> <?php } ?> */ ?> diff --git a/app/views/update/index.phtml b/app/views/update/index.phtml index 0599d5b0d..041941e39 100644 --- a/app/views/update/index.phtml +++ b/app/views/update/index.phtml @@ -1,16 +1,16 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <h1><?php echo _t('admin.update'); ?></h1> + <h1><?= _t('admin.update') ?></h1> <p> - <?php echo _i('help'); ?> <?php echo _t('admin.update.current_version', FRESHRSS_VERSION); ?> + <?= _i('help') ?> <?= _t('admin.update.current_version', FRESHRSS_VERSION) ?> </p> <p> - <?php echo _t('admin.update.last', $this->last_update_time); ?> + <?= _t('admin.update.last', $this->last_update_time) ?> </p> <?php if (!empty($this->message)) { ?> @@ -28,9 +28,9 @@ break; } ?> - <p class="alert <?php echo $class; ?>"> - <span class="alert-head"><?php echo $this->message['title']; ?></span> - <?php echo $this->message['body']; ?> + <p class="alert <?= $class ?>"> + <span class="alert-head"><?= $this->message['title'] ?></span> + <?= $this->message['body'] ?> </p> <?php } ?> @@ -38,11 +38,11 @@ if (empty($this->message) || $this->message['status'] !== 'good') { ?> <p> - <a href="<?php echo _url('update', 'check'); ?>" class="btn"><?php echo _t('admin.update.check'); ?></a> + <a href="<?= _url('update', 'check') ?>" class="btn"><?= _t('admin.update.check') ?></a> </p> <?php } ?> <?php if ($this->update_to_apply) { ?> - <a class="btn btn-important" href="<?php echo _url('update', 'apply'); ?>"><?php echo _t('admin.update.apply'); ?></a> + <a class="btn btn-important" href="<?= _url('update', 'apply') ?>"><?= _t('admin.update.apply') ?></a> <?php } ?> </div> diff --git a/app/views/user/manage.phtml b/app/views/user/manage.phtml index d0e5928ef..93d1008b5 100644 --- a/app/views/user/manage.phtml +++ b/app/views/user/manage.phtml @@ -1,98 +1,109 @@ <?php $this->partial('aside_configure'); ?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('user', 'create'); ?>" autocomplete="off"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('admin.user.create'); ?></legend> + <form method="post" action="<?= _url('user', 'create') ?>" autocomplete="off"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('admin.user.create') ?></legend> <div class="form-group"> - <label class="group-name" for="new_user_language"><?php echo _t('admin.user.language'); ?></label> + <label class="group-name" for="new_user_language"><?= _t('admin.user.language') ?></label> <div class="group-controls"> <select name="new_user_language" id="new_user_language"> <?php $languages = Minz_Translate::availableLanguages(); ?> <?php foreach ($languages as $lang) { ?> - <option value="<?php echo $lang; ?>"<?php echo FreshRSS_Context::$user_conf->language === $lang ? ' selected="selected"' : ''; ?>><?php echo _t('gen.lang.' . $lang); ?></option> + <option value="<?= $lang ?>"<?= FreshRSS_Context::$user_conf->language === $lang ? ' selected="selected"' : '' ?>><?= _t('gen.lang.' . $lang) ?></option> <?php } ?> </select> </div> </div> <div class="form-group"> - <label class="group-name" for="new_user_name"><?php echo _t('admin.user.username'); ?></label> + <label class="group-name" for="new_user_name"><?= _t('admin.user.username') ?></label> <div class="group-controls"> - <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" autocomplete="off" pattern="<?php echo FreshRSS_user_Controller::USERNAME_PATTERN; ?>" placeholder="demo" /> + <input id="new_user_name" name="new_user_name" type="text" size="16" required="required" autocomplete="off" pattern="<?= FreshRSS_user_Controller::USERNAME_PATTERN ?>" placeholder="demo" /> </div> </div> + <?php if ($this->show_email_field) { ?> + <div class="form-group"> + <label class="group-name" for="new_user_email"> + <?= _t('gen.auth.email') ?> + </label> + <div class="group-controls"> + <input id="new_user_email" name="new_user_email" type="email" required /> + </div> + </div> + <?php } ?> + <div class="form-group"> - <label class="group-name" for="new_user_passwordPlain"><?php echo _t('admin.user.password_form'); ?></label> + <label class="group-name" for="new_user_passwordPlain"><?= _t('admin.user.password_form') ?></label> <div class="group-controls"> <div class="stick"> <input type="password" id="new_user_passwordPlain" name="new_user_passwordPlain" autocomplete="new-password" pattern=".{7,}" /> - <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?php echo _i('key'); ?></a> + <a class="btn toggle-password" data-toggle="new_user_passwordPlain"><?= _i('key') ?></a> </div> - <?php echo _i('help'); ?> <?php echo _t('admin.user.password_format'); ?> - <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> + <?= _i('help') ?> <?= _t('admin.user.password_format') ?> + <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.create'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.create') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> - <form method="post" action="<?php echo _url('user', 'update'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('admin.user.update_users'); ?></legend> + <form method="post" action="<?= _url('user', 'update') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('admin.user.update_users') ?></legend> <div class="form-group"> - <label class="group-name" for="current_user"><?php echo _t('admin.user.selected'); ?></label> + <label class="group-name" for="current_user"><?= _t('admin.user.selected') ?></label> <div class="group-controls"> <select id="current_user" name="username"> <option selected="selected"> </option> <?php foreach (listUsers() as $username) { ?> - <option value="<?php echo $username; ?>"><?php echo $username; ?></option> + <option value="<?= $username ?>"><?= $username ?></option> <?php } ?> </select> </div> </div> <div class="form-group"> - <label class="group-name" for="newPasswordPlain"><?php echo _t('admin.user.password_form'); ?></label> + <label class="group-name" for="newPasswordPlain"><?= _t('admin.user.password_form') ?></label> <div class="group-controls"> <div class="stick"> - <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/> - <a class="btn toggle-password" data-toggle="newPasswordPlain"><?php echo _i('key'); ?></a> + <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?= cryptAvailable() ? '' : 'disabled="disabled" ' ?>/> + <a class="btn toggle-password" data-toggle="newPasswordPlain"><?= _i('key') ?></a> </div> - <?php echo _i('help'); ?> <?php echo _t('conf.profile.password_format'); ?> - <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> + <?= _i('help') ?> <?= _t('conf.profile.password_format') ?> + <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> </div> </div> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.update'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.update') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> - <form method="post" action="<?php echo _url('user', 'delete'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('admin.user.delete_users'); ?></legend> + <form method="post" action="<?= _url('user', 'delete') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('admin.user.delete_users') ?></legend> <div class="form-group"> - <label class="group-name" for="user-list"><?php echo _t('admin.user.selected'); ?></label> + <label class="group-name" for="user-list"><?= _t('admin.user.selected') ?></label> <div class="group-controls"> <select id="user-list" class="select-change" name="username"> <option selected="selected"> </option> <?php foreach (listUsers() as $username) { ?> - <option data-url="<?php echo _url('user', 'manage', 'u', $username); ?>" <?php echo $this->current_user === $username ? 'selected="selected"' : ''; ?> value="<?php echo $username; ?>"><?php echo $username; ?></option> + <option data-url="<?= _url('user', 'manage', 'u', $username) ?>" <?= $this->current_user === $username ? 'selected="selected"' : '' ?> value="<?= $username ?>"><?= $username ?></option> <?php } ?> </select> @@ -104,7 +115,7 @@ <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-attention confirm"><?php echo _t('gen.action.remove'); ?></button> + <button type="submit" class="btn btn-attention confirm"><?= _t('gen.action.remove') ?></button> </div> </div> </form> diff --git a/app/views/user/profile.phtml b/app/views/user/profile.phtml index 83140376d..b8bb5cee9 100644 --- a/app/views/user/profile.phtml +++ b/app/views/user/profile.phtml @@ -1,82 +1,100 @@ -<?php $this->partial('aside_configure'); ?> +<?php + if (!$this->disable_aside) { + $this->partial('aside_configure'); + } +?> <div class="post"> - <a href="<?php echo _url('index', 'index'); ?>"><?php echo _t('gen.action.back_to_rss_feeds'); ?></a> + <a href="<?= _url('index', 'index') ?>"><?= _t('gen.action.back_to_rss_feeds') ?></a> - <form method="post" action="<?php echo _url('user', 'profile'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.profile'); ?></legend> + <form method="post" action="<?= _url('user', 'profile') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.profile') ?></legend> <div class="form-group"> - <label class="group-name" for="current_user"><?php echo _t('conf.user.current'); ?></label> + <label class="group-name" for="current_user"><?= _t('conf.user.current') ?></label> <div class="group-controls"> - <input id="current_user" type="text" disabled="disabled" value="<?php echo Minz_Session::param('currentUser', '_'); ?>" /> - <label class="checkbox" for="is_admin"> - <input type="checkbox" id="is_admin" disabled="disabled" <?php echo FreshRSS_Auth::hasAccess('admin') ? 'checked="checked" ' : ''; ?>/> - <?php echo _t('conf.user.is_admin'); ?> - </label> + <input id="current_user" type="text" disabled="disabled" value="<?= Minz_Session::param('currentUser', '_') ?>" /> + </div> + </div> + + <?php if (FreshRSS_Auth::hasAccess('admin')) { ?> + <div class="form-group"> + <div class="group-controls"> + <label class="checkbox" for="is_admin"> + <input type="checkbox" id="is_admin" disabled checked /> + <?= _t('conf.user.is_admin') ?> + </label> + </div> + </div> + <?php } ?> + + <div class="form-group"> + <label class="group-name" for="email"><?= _t('conf.profile.email') ?></label> + <div class="group-controls"> + <input id="email" name="email" type="email" value="<?= FreshRSS_Context::$user_conf->mail_login ?>" /> </div> </div> <div class="form-group"> - <label class="group-name" for="newPasswordPlain"><?php echo _t('conf.profile.password_form'); ?></label> + <label class="group-name" for="newPasswordPlain"><?= _t('conf.profile.password_form') ?></label> <div class="group-controls"> <div class="stick"> - <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/> - <a class="btn toggle-password" data-toggle="newPasswordPlain"><?php echo _i('key'); ?></a> + <input type="password" id="newPasswordPlain" name="newPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?= cryptAvailable() ? '' : 'disabled="disabled" ' ?>/> + <a class="btn toggle-password" data-toggle="newPasswordPlain"><?= _i('key') ?></a> </div> - <?php echo _i('help'); ?> <?php echo _t('conf.profile.password_format'); ?> - <noscript><b><?php echo _t('gen.js.should_be_activated'); ?></b></noscript> + <?= _i('help') ?> <?= _t('conf.profile.password_format') ?> + <noscript><b><?= _t('gen.js.should_be_activated') ?></b></noscript> </div> </div> <?php if (FreshRSS_Context::$system_conf->api_enabled) { ?> <div class="form-group"> - <label class="group-name" for="apiPasswordPlain"><?php echo _t('conf.profile.password_api'); ?></label> + <label class="group-name" for="apiPasswordPlain"><?= _t('conf.profile.password_api') ?></label> <div class="group-controls"> <div class="stick"> - <input type="password" id="apiPasswordPlain" name="apiPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?php echo cryptAvailable() ? '' : 'disabled="disabled" '; ?>/> - <a class="btn toggle-password" data-toggle="apiPasswordPlain"><?php echo _i('key'); ?></a> + <input type="password" id="apiPasswordPlain" name="apiPasswordPlain" autocomplete="new-password" pattern=".{7,}" <?= cryptAvailable() ? '' : 'disabled="disabled" ' ?>/> + <a class="btn toggle-password" data-toggle="apiPasswordPlain"><?= _i('key') ?></a> </div> - <?php echo _i('help'); ?> <kbd><a href="../api/"><?php echo Minz_Url::display('/api/', 'html', true); ?></a></kbd> + <?= _i('help') ?> <kbd><a href="../api/"><?= Minz_Url::display('/api/', 'html', true) ?></a></kbd> </div> </div> <?php } ?> <?php if (FreshRSS_Auth::accessNeedsAction()) { ?> <div class="form-group"> - <label class="group-name" for="token"><?php echo _t('admin.auth.token'); ?></label> + <label class="group-name" for="token"><?= _t('admin.auth.token') ?></label> <?php $token = FreshRSS_Context::$user_conf->token; ?> <div class="group-controls"> - <input type="text" id="token" name="token" value="<?php echo $token; ?>" placeholder="<?php echo _t('gen.short.blank_to_disable'); ?>"<?php - echo FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?php echo $token; ?>"/> - <?php echo _i('help'); ?> <?php echo _t('admin.auth.token_help'); ?> - <kbd><?php echo Minz_Url::display(array('a' => 'rss', 'params' => array('user' => Minz_Session::param('currentUser'), 'token' => $token, 'hours' => FreshRSS_Context::$user_conf->since_hours_posts_per_rss)), 'html', true); ?></kbd> + <input type="text" id="token" name="token" value="<?= $token ?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>"<?php + echo FreshRSS_Auth::accessNeedsAction() ? '' : ' disabled="disabled"'; ?> data-leave-validation="<?= $token ?>"/> + <?= _i('help') ?> <?= _t('admin.auth.token_help') ?> + <kbd><?= Minz_Url::display(array('a' => 'rss', 'params' => array('user' => Minz_Session::param('currentUser'), 'token' => $token, 'hours' => FreshRSS_Context::$user_conf->since_hours_posts_per_rss)), 'html', true) ?></kbd> </div> </div> <?php } ?> <div class="form-group form-actions"> <div class="group-controls"> - <button type="submit" class="btn btn-important"><?php echo _t('gen.action.submit'); ?></button> - <button type="reset" class="btn"><?php echo _t('gen.action.cancel'); ?></button> + <button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button> + <button type="reset" class="btn"><?= _t('gen.action.cancel') ?></button> </div> </div> </form> <?php if (!FreshRSS_Auth::hasAccess('admin')) { ?> - <form id="crypto-form" method="post" action="<?php echo _url('user', 'delete'); ?>"> - <input type="hidden" name="_csrf" value="<?php echo FreshRSS_Auth::csrfToken(); ?>" /> - <legend><?php echo _t('conf.profile.delete'); ?></legend> + <form id="crypto-form" method="post" action="<?= _url('user', 'delete') ?>"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <legend><?= _t('conf.profile.delete') ?></legend> - <p class="alert alert-warn"><span class="alert-head"><?php echo _t('gen.short.attention'); ?></span> <?php echo _t('conf.profile.delete.warn'); ?></p> + <p class="alert alert-warn"><span class="alert-head"><?= _t('gen.short.attention') ?></span> <?= _t('conf.profile.delete.warn') ?></p> <div class="form-group"> - <label class="group-name" for="passwordPlain"><?php echo _t('gen.auth.password'); ?></label> + <label class="group-name" for="passwordPlain"><?= _t('gen.auth.password') ?></label> <div class="group-controls"> <input type="password" id="passwordPlain" required="required" /> <input type="hidden" id="challenge" name="challenge" /><br /> - <noscript><strong><?php echo _t('gen.js.should_be_activated'); ?></strong></noscript> + <noscript><strong><?= _t('gen.js.should_be_activated') ?></strong></noscript> </div> </div> @@ -88,9 +106,9 @@ 'php', true )); ?> - <input type="hidden" name="r" value="<?php echo $redirect_url; ?>" /> - <input type="hidden" name="username" id="username" value="<?php echo Minz_Session::param('currentUser', '_'); ?>" /> - <button type="submit" class="btn btn-attention confirm"><?php echo _t('gen.action.remove'); ?></button> + <input type="hidden" name="r" value="<?= $redirect_url ?>" /> + <input type="hidden" name="username" id="username" value="<?= Minz_Session::param('currentUser', '_') ?>" /> + <button type="submit" class="btn btn-attention confirm"><?= _t('gen.action.remove') ?></button> </div> </div> </form> diff --git a/app/views/user/validateEmail.phtml b/app/views/user/validateEmail.phtml new file mode 100644 index 000000000..e525b1b6b --- /dev/null +++ b/app/views/user/validateEmail.phtml @@ -0,0 +1,22 @@ +<div class="post"> + <p> + <?= _t('user.email.validation.need_to', FreshRSS_Context::$system_conf->title) ?> + </p> + + <p> + <?= _t('user.email.validation.email_sent_to', FreshRSS_Context::$user_conf->mail_login) ?> + </p> + + <form action="<?= _url('user', 'sendValidationEmail') ?>" method="post"> + <input type="hidden" name="_csrf" value="<?= FreshRSS_Auth::csrfToken() ?>" /> + <button type="submit" class="btn"> + <?= _t('user.email.validation.resend_email') ?> + </button> + </form> + + <p> + <small> + <?= _t('user.email.validation.change_email', _url('user', 'profile')) ?> + </small> + </p> +</div> diff --git a/app/views/user_mailer/email_need_validation.txt b/app/views/user_mailer/email_need_validation.txt new file mode 100644 index 000000000..c1f4d9911 --- /dev/null +++ b/app/views/user_mailer/email_need_validation.txt @@ -0,0 +1,5 @@ +<?= _t('user.mailer.email_need_validation.welcome', $this->username) ?> + +<?= _t('user.mailer.email_need_validation.body', $this->site_title) ?> + +<?= $this->validation_url ?> |
