mirror of https://github.com/FreshRSS/FreshRSS.git
New feature: shareable user query (#6052)
* New feature: shareable user query Share the output of a user query by RSS / HTML / OPML with other people through unique URLs. Replaces the global admin token, which was the only option (but unsafe) to share RSS outputs with other people. Also add a new HTML output for people without an RSS reader. fix https://github.com/FreshRSS/FreshRSS/issues/3066#issuecomment-648977890 fix https://github.com/FreshRSS/FreshRSS/issues/3178#issuecomment-769435504 * Remove unused method * Fix token saving * Implement HTML view * Update i18n for master token * Revert i18n get_favorite * Fix missing i18n for user queries from before this PR * Remove irrelevant tests * Add link to RSS version * Fix getGet * Fix getState * Fix getSearch * Alternative getSearch * Default getOrder * Explicit default state * Fix test * Add OPML sharing * Remove many redundant SQL queries from original implementation of user queries * Fix article tags * Use default user settings * Prepare public search * Fixes * Allow user search on article tags * Implement user search * Revert filter bug * Revert wrong SQL left outer join change * Implement checkboxes * Safe check of OPML * Fix label * Remove RSS button to favour new sharing method That sharing button was using a global admin token * First version of HTTP 304 * Disallow some recusrivity fix https://github.com/FreshRSS/FreshRSS/issues/6086 * Draft of nav * Minor httpConditional * Add support for offset for pagination * Fix offset pagination * Fix explicit order ASC * Add documentation * Help links i18n * Note about deprecated master token * Typo * Doc about format
This commit is contained in:
parent
25166c218b
commit
39cc1c11ec
|
@ -15,7 +15,11 @@ Il y a une API pour les clients (mobiles), ainsi qu’une [interface en ligne de
|
|||
Grâce au standard [WebSub](https://freshrss.github.io/FreshRSS/fr/users/08_PubSubHubbub.html),
|
||||
FreshRSS est capable de recevoir des notifications push instantanées depuis les sources compatibles, [Friendica](https://friendi.ca), [WordPress](https://wordpress.org/plugins/pubsubhubbub/), Blogger, Medium, etc.
|
||||
|
||||
FreshRSS supporte nativement le moissonnage du Web (Web Scraping) basique, basé sur [XPath](https://www.w3.org/TR/xpath-10/), pour les sites Web sans flux RSS / Atom.
|
||||
FreshRSS supporte nativement le [moissonnage du Web (Web Scraping)](https://freshrss.github.io/FreshRSS/en/users/11_website_scraping.html) basique,
|
||||
basé sur [XPath](https://www.w3.org/TR/xpath-10/), pour les sites Web sans flux RSS / Atom.
|
||||
Supporte aussi les documents JSON.
|
||||
|
||||
FreshRSS permet de [repartager des sélections d’articles par HTML, RSS, et OPML](https://freshrss.github.io/FreshRSS/en/users/user_queries.html).
|
||||
|
||||
Plusieurs [méthodes de connexion](https://freshrss.github.io/FreshRSS/en/admins/09_AccessControl.html) sont supportées : formulaire Web (avec un mode anonyme), Authentification HTTP (compatible avec proxy), OpenID Connect.
|
||||
|
||||
|
|
|
@ -15,7 +15,11 @@ There is an API for (mobile) clients, and a [Command-Line Interface](cli/README.
|
|||
Thanks to the [WebSub](https://freshrss.github.io/FreshRSS/en/users/WebSub.html) standard,
|
||||
FreshRSS is able to receive instant push notifications from compatible sources, such as [Friendica](https://friendi.ca), [WordPress](https://wordpress.org/plugins/pubsubhubbub/), Blogger, Medium, etc.
|
||||
|
||||
FreshRSS natively supports basic Web scraping, based on [XPath](https://www.w3.org/TR/xpath-10/), for Web sites not providing any RSS / Atom feed.
|
||||
FreshRSS natively supports basic [Web scraping](https://freshrss.github.io/FreshRSS/en/users/11_website_scraping.html),
|
||||
based on [XPath](https://www.w3.org/TR/xpath-10/), for Web sites not providing any RSS / Atom feed.
|
||||
Also supports JSON documents.
|
||||
|
||||
FreshRSS offers the ability to [reshare selections of articles by HTML, RSS, and OPML](https://freshrss.github.io/FreshRSS/en/users/user_queries.html).
|
||||
|
||||
Different [login methods](https://freshrss.github.io/FreshRSS/en/admins/09_AccessControl.html) are supported: Web form (including an anonymous option), HTTP Authentication (compatible with proxy delegation), OpenID Connect.
|
||||
|
||||
|
|
|
@ -301,12 +301,8 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
|
|||
public function queriesAction(): void {
|
||||
FreshRSS_View::appendScript(Minz_Url::display('/scripts/draggable.js?' . @filemtime(PUBLIC_PATH . '/scripts/draggable.js')));
|
||||
|
||||
$category_dao = FreshRSS_Factory::createCategoryDao();
|
||||
$feed_dao = FreshRSS_Factory::createFeedDao();
|
||||
$tag_dao = FreshRSS_Factory::createTagDao();
|
||||
|
||||
if (Minz_Request::isPost()) {
|
||||
/** @var array<int,array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string}> $params */
|
||||
/** @var array<int,array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string,'token'?:string}> $params */
|
||||
$params = Minz_Request::paramArray('queries');
|
||||
|
||||
$queries = [];
|
||||
|
@ -318,7 +314,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
|
|||
if (!empty($query['search'])) {
|
||||
$query['search'] = urldecode($query['search']);
|
||||
}
|
||||
$queries[$key] = (new FreshRSS_UserQuery($query, $feed_dao, $category_dao, $tag_dao))->toArray();
|
||||
$queries[$key] = (new FreshRSS_UserQuery($query, FreshRSS_Context::categories(), FreshRSS_Context::labels()))->toArray();
|
||||
}
|
||||
FreshRSS_Context::userConf()->queries = $queries;
|
||||
FreshRSS_Context::userConf()->save();
|
||||
|
@ -327,13 +323,13 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
|
|||
} else {
|
||||
$this->view->queries = [];
|
||||
foreach (FreshRSS_Context::userConf()->queries as $key => $query) {
|
||||
$this->view->queries[intval($key)] = new FreshRSS_UserQuery($query, $feed_dao, $category_dao, $tag_dao);
|
||||
$this->view->queries[intval($key)] = new FreshRSS_UserQuery($query, FreshRSS_Context::categories(), FreshRSS_Context::labels());
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->categories = $category_dao->listCategories(false) ?: [];
|
||||
$this->view->feeds = $feed_dao->listFeeds();
|
||||
$this->view->tags = $tag_dao->listTags() ?: [];
|
||||
$this->view->categories = FreshRSS_Context::categories();
|
||||
$this->view->feeds = FreshRSS_Context::feeds();
|
||||
$this->view->tags = FreshRSS_Context::labels();
|
||||
|
||||
if (Minz_Request::paramTernary('id') !== null) {
|
||||
$id = Minz_Request::paramInt('id');
|
||||
|
@ -363,20 +359,21 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
|
|||
return;
|
||||
}
|
||||
|
||||
$category_dao = FreshRSS_Factory::createCategoryDao();
|
||||
$feed_dao = FreshRSS_Factory::createFeedDao();
|
||||
$tag_dao = FreshRSS_Factory::createTagDao();
|
||||
|
||||
$query = new FreshRSS_UserQuery(FreshRSS_Context::userConf()->queries[$id], $feed_dao, $category_dao, $tag_dao);
|
||||
$query = new FreshRSS_UserQuery(FreshRSS_Context::userConf()->queries[$id], FreshRSS_Context::categories(), FreshRSS_Context::labels());
|
||||
$this->view->query = $query;
|
||||
$this->view->queryId = $id;
|
||||
$this->view->categories = $category_dao->listCategories(false) ?: [];
|
||||
$this->view->feeds = $feed_dao->listFeeds();
|
||||
$this->view->tags = $tag_dao->listTags() ?: [];
|
||||
$this->view->categories = FreshRSS_Context::categories();
|
||||
$this->view->feeds = FreshRSS_Context::feeds();
|
||||
$this->view->tags = FreshRSS_Context::labels();
|
||||
|
||||
if (Minz_Request::isPost()) {
|
||||
$params = array_filter(Minz_Request::paramArray('query'));
|
||||
$queryParams = [];
|
||||
$name = Minz_Request::paramString('name') ?: _t('conf.query.number', $id + 1);
|
||||
if ('' === $name) {
|
||||
$name = _t('conf.query.number', $id + 1);
|
||||
}
|
||||
$queryParams['name'] = $name;
|
||||
if (!empty($params['get']) && is_string($params['get'])) {
|
||||
$queryParams['get'] = htmlspecialchars_decode($params['get'], ENT_QUOTES);
|
||||
}
|
||||
|
@ -389,15 +386,21 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
|
|||
if (!empty($params['state']) && is_array($params['state'])) {
|
||||
$queryParams['state'] = (int)(array_sum($params['state']));
|
||||
}
|
||||
$name = Minz_Request::paramString('name') ?: _t('conf.query.number', $id + 1);
|
||||
if ('' === $name) {
|
||||
$name = _t('conf.query.number', $id + 1);
|
||||
if (empty($params['token']) || !is_string($params['token'])) {
|
||||
$queryParams['token'] = FreshRSS_UserQuery::generateToken($name);
|
||||
} else {
|
||||
$queryParams['token'] = $params['token'];
|
||||
}
|
||||
if (!empty($params['shareRss']) && ctype_digit($params['shareRss'])) {
|
||||
$queryParams['shareRss'] = (bool)$params['shareRss'];
|
||||
}
|
||||
if (!empty($params['shareOpml']) && ctype_digit($params['shareOpml'])) {
|
||||
$queryParams['shareOpml'] = (bool)$params['shareOpml'];
|
||||
}
|
||||
$queryParams['name'] = $name;
|
||||
$queryParams['url'] = Minz_Url::display(['params' => $queryParams]);
|
||||
|
||||
$queries = FreshRSS_Context::userConf()->queries;
|
||||
$queries[$id] = (new FreshRSS_UserQuery($queryParams, $feed_dao, $category_dao, $tag_dao))->toArray();
|
||||
$queries[$id] = (new FreshRSS_UserQuery($queryParams, FreshRSS_Context::categories(), FreshRSS_Context::labels()))->toArray();
|
||||
FreshRSS_Context::userConf()->queries = $queries;
|
||||
FreshRSS_Context::userConf()->save();
|
||||
|
||||
|
@ -433,18 +436,15 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
|
|||
* lean data.
|
||||
*/
|
||||
public function bookmarkQueryAction(): void {
|
||||
$category_dao = FreshRSS_Factory::createCategoryDao();
|
||||
$feed_dao = FreshRSS_Factory::createFeedDao();
|
||||
$tag_dao = FreshRSS_Factory::createTagDao();
|
||||
$queries = [];
|
||||
foreach (FreshRSS_Context::userConf()->queries as $key => $query) {
|
||||
$queries[$key] = (new FreshRSS_UserQuery($query, $feed_dao, $category_dao, $tag_dao))->toArray();
|
||||
$queries[$key] = (new FreshRSS_UserQuery($query, FreshRSS_Context::categories(), FreshRSS_Context::labels()))->toArray();
|
||||
}
|
||||
$params = $_GET;
|
||||
unset($params['rid']);
|
||||
$params['url'] = Minz_Url::display(['params' => $params]);
|
||||
$params['name'] = _t('conf.query.number', count($queries) + 1);
|
||||
$queries[] = (new FreshRSS_UserQuery($params, $feed_dao, $category_dao, $tag_dao))->toArray();
|
||||
$queries[] = (new FreshRSS_UserQuery($params, FreshRSS_Context::categories(), FreshRSS_Context::labels()))->toArray();
|
||||
|
||||
FreshRSS_Context::userConf()->queries = $queries;
|
||||
FreshRSS_Context::userConf()->save();
|
||||
|
|
|
@ -776,7 +776,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
|
|||
*/
|
||||
private static function applyLabelActions(int $nbNewEntries) {
|
||||
$tagDAO = FreshRSS_Factory::createTagDao();
|
||||
$labels = $tagDAO->listTags() ?: [];
|
||||
$labels = FreshRSS_Context::labels();
|
||||
$labels = array_filter($labels, static function (FreshRSS_Tag $label) {
|
||||
return !empty($label->filtersAction('label'));
|
||||
});
|
||||
|
|
|
@ -364,7 +364,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
|
|||
}
|
||||
|
||||
$tagDAO = FreshRSS_Factory::createTagDao();
|
||||
$labels = $tagDAO->listTags() ?: [];
|
||||
$labels = FreshRSS_Context::labels();
|
||||
$knownLabels = [];
|
||||
foreach ($labels as $label) {
|
||||
$knownLabels[$label->name()]['id'] = $label->id();
|
||||
|
|
|
@ -6,6 +6,10 @@ declare(strict_types=1);
|
|||
*/
|
||||
class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
||||
|
||||
public function firstAction(): void {
|
||||
$this->view->html_url = Minz_Url::display(['c' => 'index', 'a' => 'index'], 'html', 'root');
|
||||
}
|
||||
|
||||
/**
|
||||
* This action only redirect on the default view mode (normal or global)
|
||||
*/
|
||||
|
@ -36,7 +40,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
}
|
||||
|
||||
try {
|
||||
FreshRSS_Context::updateUsingRequest();
|
||||
FreshRSS_Context::updateUsingRequest(true);
|
||||
} catch (FreshRSS_Context_Exception $e) {
|
||||
Minz_Error::error(404);
|
||||
}
|
||||
|
@ -48,7 +52,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
'media-src' => '*',
|
||||
]);
|
||||
|
||||
$this->view->categories = FreshRSS_Context::$categories;
|
||||
$this->view->categories = FreshRSS_Context::categories();
|
||||
|
||||
$this->view->rss_title = FreshRSS_Context::$name . ' | ' . FreshRSS_View::title();
|
||||
$title = FreshRSS_Context::$name;
|
||||
|
@ -60,15 +64,10 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
FreshRSS_Context::$id_max = time() . '000000';
|
||||
|
||||
$this->view->callbackBeforeFeeds = static function (FreshRSS_View $view) {
|
||||
try {
|
||||
$tagDAO = FreshRSS_Factory::createTagDao();
|
||||
$view->tags = $tagDAO->listTags(true) ?: [];
|
||||
$view->nbUnreadTags = 0;
|
||||
foreach ($view->tags as $tag) {
|
||||
$view->nbUnreadTags += $tag->nbUnread();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Minz_Log::notice($e->getMessage());
|
||||
$view->tags = FreshRSS_Context::labels(true);
|
||||
$view->nbUnreadTags = 0;
|
||||
foreach ($view->tags as $tag) {
|
||||
$view->nbUnreadTags += $tag->nbUnread();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -117,12 +116,12 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
FreshRSS_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
|
||||
|
||||
try {
|
||||
FreshRSS_Context::updateUsingRequest();
|
||||
FreshRSS_Context::updateUsingRequest(true);
|
||||
} catch (FreshRSS_Context_Exception $e) {
|
||||
Minz_Error::error(404);
|
||||
}
|
||||
|
||||
$this->view->categories = FreshRSS_Context::$categories;
|
||||
$this->view->categories = FreshRSS_Context::categories();
|
||||
|
||||
$this->view->rss_title = FreshRSS_Context::$name . ' | ' . FreshRSS_View::title();
|
||||
$title = _t('index.feed.title_global');
|
||||
|
@ -141,6 +140,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
|
||||
/**
|
||||
* This action displays the RSS feed of FreshRSS.
|
||||
* @deprecated See user query RSS sharing instead
|
||||
*/
|
||||
public function rssAction(): void {
|
||||
$allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous;
|
||||
|
@ -156,7 +156,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
}
|
||||
|
||||
try {
|
||||
FreshRSS_Context::updateUsingRequest();
|
||||
FreshRSS_Context::updateUsingRequest(false);
|
||||
} catch (FreshRSS_Context_Exception $e) {
|
||||
Minz_Error::error(404);
|
||||
}
|
||||
|
@ -168,13 +168,19 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
Minz_Error::error(404);
|
||||
}
|
||||
|
||||
// No layout for RSS output.
|
||||
$this->view->rss_url = PUBLIC_TO_INDEX_PATH . '/' . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']);
|
||||
$this->view->html_url = Minz_Url::display('', 'html', true);
|
||||
$this->view->rss_title = FreshRSS_Context::$name . ' | ' . FreshRSS_View::title();
|
||||
$this->view->rss_url = htmlspecialchars(
|
||||
PUBLIC_TO_INDEX_PATH . '/' . (empty($_SERVER['QUERY_STRING']) ? '' : '?' . $_SERVER['QUERY_STRING']), ENT_COMPAT, 'UTF-8');
|
||||
|
||||
// No layout for RSS output.
|
||||
$this->view->_layout(null);
|
||||
header('Content-Type: application/rss+xml; charset=utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated See user query OPML sharing instead
|
||||
*/
|
||||
public function opmlAction(): void {
|
||||
$allow_anonymous = FreshRSS_Context::systemConf()->allow_anonymous;
|
||||
$token = FreshRSS_Context::userConf()->token;
|
||||
|
@ -187,7 +193,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
}
|
||||
|
||||
try {
|
||||
FreshRSS_Context::updateUsingRequest();
|
||||
FreshRSS_Context::updateUsingRequest(false);
|
||||
} catch (FreshRSS_Context_Exception $e) {
|
||||
Minz_Error::error(404);
|
||||
}
|
||||
|
@ -196,25 +202,23 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
$type = (string)$get[0];
|
||||
$id = (int)$get[1];
|
||||
|
||||
$catDAO = FreshRSS_Factory::createCategoryDao();
|
||||
$categories = $catDAO->listCategories(true, true);
|
||||
$this->view->excludeMutedFeeds = true;
|
||||
|
||||
switch ($type) {
|
||||
case 'a':
|
||||
$this->view->categories = $categories;
|
||||
$this->view->categories = FreshRSS_Context::categories();
|
||||
break;
|
||||
case 'c':
|
||||
$cat = $categories[$id] ?? null;
|
||||
$cat = FreshRSS_Context::categories()[$id] ?? null;
|
||||
if ($cat == null) {
|
||||
Minz_Error::error(404);
|
||||
return;
|
||||
}
|
||||
$this->view->categories = [ $cat ];
|
||||
$this->view->categories = [ $cat->id() => $cat ];
|
||||
break;
|
||||
case 'f':
|
||||
// We most likely already have the feed object in cache
|
||||
$feed = FreshRSS_CategoryDAO::findFeed($categories, $id);
|
||||
$feed = FreshRSS_Category::findFeed(FreshRSS_Context::categories(), $id);
|
||||
if ($feed === null) {
|
||||
$feedDAO = FreshRSS_Factory::createFeedDao();
|
||||
$feed = $feedDAO->searchById($id);
|
||||
|
@ -223,7 +227,7 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
return;
|
||||
}
|
||||
}
|
||||
$this->view->feeds = [ $feed ];
|
||||
$this->view->feeds = [ $feed->id() => $feed ];
|
||||
break;
|
||||
case 's':
|
||||
case 't':
|
||||
|
@ -255,17 +259,14 @@ class FreshRSS_index_Controller extends FreshRSS_ActionController {
|
|||
$id = 0;
|
||||
}
|
||||
|
||||
$limit = FreshRSS_Context::$number;
|
||||
|
||||
$date_min = 0;
|
||||
if (FreshRSS_Context::$sinceHours) {
|
||||
if (FreshRSS_Context::$sinceHours > 0) {
|
||||
$date_min = time() - (FreshRSS_Context::$sinceHours * 3600);
|
||||
$limit = FreshRSS_Context::userConf()->max_posts_per_rss;
|
||||
}
|
||||
|
||||
foreach ($entryDAO->listWhere(
|
||||
$type, $id, FreshRSS_Context::$state, FreshRSS_Context::$order,
|
||||
$limit, FreshRSS_Context::$first_id,
|
||||
FreshRSS_Context::$number, FreshRSS_Context::$offset, FreshRSS_Context::$first_id,
|
||||
FreshRSS_Context::$search, $date_min)
|
||||
as $entry) {
|
||||
yield $entry;
|
||||
|
|
|
@ -193,7 +193,7 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
|
|||
if ($id !== 0) {
|
||||
$this->view->displaySlider = true;
|
||||
$feedDAO = FreshRSS_Factory::createFeedDao();
|
||||
$this->view->feed = $feedDAO->searchById($id);
|
||||
$this->view->feed = $feedDAO->searchById($id) ?? FreshRSS_Feed::default();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,7 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController {
|
|||
}
|
||||
|
||||
$this->view->categories = $categoryDAO->listCategories(true) ?: [];
|
||||
$this->view->feed = $id === null ? null : $feedDAO->searchById($id);
|
||||
$this->view->feed = $id === null ? FreshRSS_Feed::default() : ($feedDAO->searchById($id) ?? FreshRSS_Feed::default());
|
||||
$this->view->days = $statsDAO->getDays();
|
||||
$this->view->months = $statsDAO->getMonths();
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
|
|||
break;
|
||||
default:
|
||||
$feedDAO = FreshRSS_Factory::createFeedDao();
|
||||
$this->view->feed = $feedDAO->searchById($id);
|
||||
$this->view->feed = $feedDAO->searchById($id) ?? FreshRSS_Feed::default();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -199,6 +199,6 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
|
|||
Minz_Error::error(403);
|
||||
}
|
||||
$tagDAO = FreshRSS_Factory::createTagDao();
|
||||
$this->view->tags = $tagDAO->listTags() ?: [];
|
||||
$this->view->tags = $tagDAO->listTags(true) ?: [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ class FreshRSS extends Minz_FrontController {
|
|||
}
|
||||
}
|
||||
//Use prepend to insert before extensions. Added in reverse order.
|
||||
if (Minz_Request::controllerName() !== 'index') {
|
||||
if (!in_array(Minz_Request::controllerName(), ['index', ''], true)) {
|
||||
FreshRSS_View::prependScript(Minz_Url::display('/scripts/extra.js?' . @filemtime(PUBLIC_PATH . '/scripts/extra.js')));
|
||||
}
|
||||
FreshRSS_View::prependScript(Minz_Url::display('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js')));
|
||||
|
|
|
@ -16,14 +16,12 @@ class FreshRSS_BooleanSearch {
|
|||
private string $operator;
|
||||
|
||||
/** @param 'AND'|'OR'|'AND NOT' $operator */
|
||||
public function __construct(string $input, int $level = 0, string $operator = 'AND') {
|
||||
public function __construct(string $input, int $level = 0, string $operator = 'AND', bool $allowUserQueries = true) {
|
||||
$this->operator = $operator;
|
||||
$input = trim($input);
|
||||
if ($input === '') {
|
||||
return;
|
||||
}
|
||||
$this->raw_input = $input;
|
||||
|
||||
if ($level === 0) {
|
||||
$input = preg_replace('/:"(.*?)"/', ':"\1"', $input);
|
||||
if (!is_string($input)) {
|
||||
|
@ -34,9 +32,11 @@ class FreshRSS_BooleanSearch {
|
|||
return;
|
||||
}
|
||||
|
||||
$input = $this->parseUserQueryNames($input);
|
||||
$input = $this->parseUserQueryIds($input);
|
||||
$input = $this->parseUserQueryNames($input, $allowUserQueries);
|
||||
$input = $this->parseUserQueryIds($input, $allowUserQueries);
|
||||
$input = trim($input);
|
||||
}
|
||||
$this->raw_input = $input;
|
||||
|
||||
// Either parse everything as a series of BooleanSearch’s combined by implicit AND
|
||||
// or parse everything as a series of Search’s combined by explicit OR
|
||||
|
@ -46,7 +46,7 @@ class FreshRSS_BooleanSearch {
|
|||
/**
|
||||
* Parse the user queries (saved searches) by name and expand them in the input string.
|
||||
*/
|
||||
private function parseUserQueryNames(string $input): string {
|
||||
private function parseUserQueryNames(string $input, bool $allowUserQueries = true): string {
|
||||
$all_matches = [];
|
||||
if (preg_match_all('/\bsearch:(?P<delim>[\'"])(?P<search>.*)(?P=delim)/U', $input, $matchesFound)) {
|
||||
$all_matches[] = $matchesFound;
|
||||
|
@ -60,7 +60,7 @@ class FreshRSS_BooleanSearch {
|
|||
/** @var array<string,FreshRSS_UserQuery> */
|
||||
$queries = [];
|
||||
foreach (FreshRSS_Context::userConf()->queries as $raw_query) {
|
||||
$query = new FreshRSS_UserQuery($raw_query);
|
||||
$query = new FreshRSS_UserQuery($raw_query, FreshRSS_Context::categories(), FreshRSS_Context::labels());
|
||||
$queries[$query->getName()] = $query;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,11 @@ class FreshRSS_BooleanSearch {
|
|||
$name = trim($matches['search'][$i]);
|
||||
if (!empty($queries[$name])) {
|
||||
$fromS[] = $matches[0][$i];
|
||||
$toS[] = '(' . trim($queries[$name]->getSearch()->getRawInput()) . ')';
|
||||
if ($allowUserQueries) {
|
||||
$toS[] = '(' . trim($queries[$name]->getSearch()->getRawInput()) . ')';
|
||||
} else {
|
||||
$toS[] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +91,7 @@ class FreshRSS_BooleanSearch {
|
|||
/**
|
||||
* Parse the user queries (saved searches) by ID and expand them in the input string.
|
||||
*/
|
||||
private function parseUserQueryIds(string $input): string {
|
||||
private function parseUserQueryIds(string $input, bool $allowUserQueries = true): string {
|
||||
$all_matches = [];
|
||||
|
||||
if (preg_match_all('/\bS:(?P<search>\d+)/', $input, $matchesFound)) {
|
||||
|
@ -95,14 +99,10 @@ class FreshRSS_BooleanSearch {
|
|||
}
|
||||
|
||||
if (!empty($all_matches)) {
|
||||
$category_dao = FreshRSS_Factory::createCategoryDao();
|
||||
$feed_dao = FreshRSS_Factory::createFeedDao();
|
||||
$tag_dao = FreshRSS_Factory::createTagDao();
|
||||
|
||||
/** @var array<string,FreshRSS_UserQuery> */
|
||||
$queries = [];
|
||||
foreach (FreshRSS_Context::userConf()->queries as $raw_query) {
|
||||
$query = new FreshRSS_UserQuery($raw_query, $feed_dao, $category_dao, $tag_dao);
|
||||
$query = new FreshRSS_UserQuery($raw_query, FreshRSS_Context::categories(), FreshRSS_Context::labels());
|
||||
$queries[] = $query;
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,11 @@ class FreshRSS_BooleanSearch {
|
|||
$id = (int)(trim($matches['search'][$i])) - 1;
|
||||
if (!empty($queries[$id])) {
|
||||
$fromS[] = $matches[0][$i];
|
||||
$toS[] = '(' . trim($queries[$id]->getSearch()->getRawInput()) . ')';
|
||||
if ($allowUserQueries) {
|
||||
$toS[] = '(' . trim($queries[$id]->getSearch()->getRawInput()) . ')';
|
||||
} else {
|
||||
$toS[] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ class FreshRSS_Category extends Minz_Model {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array<FreshRSS_Feed>
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
* @throws Minz_ConfigurationNamespaceException
|
||||
* @throws Minz_PDOConnectionException
|
||||
*/
|
||||
|
@ -110,10 +110,8 @@ class FreshRSS_Category extends Minz_Model {
|
|||
$this->nbNotRead += $feed->nbNotRead();
|
||||
$this->hasFeedsWithError |= ($feed->inError() && !$feed->mute());
|
||||
}
|
||||
|
||||
$this->sortFeeds();
|
||||
}
|
||||
|
||||
return $this->feeds ?? [];
|
||||
}
|
||||
|
||||
|
@ -143,7 +141,6 @@ class FreshRSS_Category extends Minz_Model {
|
|||
if (!is_array($values)) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
$this->feeds = $values;
|
||||
$this->sortFeeds();
|
||||
}
|
||||
|
@ -157,7 +154,6 @@ class FreshRSS_Category extends Minz_Model {
|
|||
}
|
||||
$feed->_category($this);
|
||||
$this->feeds[] = $feed;
|
||||
|
||||
$this->sortFeeds();
|
||||
}
|
||||
|
||||
|
@ -243,8 +239,54 @@ class FreshRSS_Category extends Minz_Model {
|
|||
if ($this->feeds === null) {
|
||||
return;
|
||||
}
|
||||
usort($this->feeds, static function (FreshRSS_Feed $a, FreshRSS_Feed $b) {
|
||||
uasort($this->feeds, static function (FreshRSS_Feed $a, FreshRSS_Feed $b) {
|
||||
return strnatcasecmp($a->name(), $b->name());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Access cached feed
|
||||
* @param array<FreshRSS_Category> $categories
|
||||
*/
|
||||
public static function findFeed(array $categories, int $feed_id): ?FreshRSS_Feed {
|
||||
foreach ($categories as $category) {
|
||||
foreach ($category->feeds() as $feed) {
|
||||
if ($feed->id() === $feed_id) {
|
||||
$feed->_category($category); // Should already be done; just to be safe
|
||||
return $feed;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access cached feeds
|
||||
* @param array<FreshRSS_Category> $categories
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
*/
|
||||
public static function findFeeds(array $categories): array {
|
||||
$result = [];
|
||||
foreach ($categories as $category) {
|
||||
foreach ($category->feeds() as $feed) {
|
||||
$result[$feed->id()] = $feed;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<FreshRSS_Category> $categories
|
||||
*/
|
||||
public static function countUnread(array $categories, int $minPriority = 0): int {
|
||||
$n = 0;
|
||||
foreach ($categories as $category) {
|
||||
foreach ($category->feeds() as $feed) {
|
||||
if ($feed->priority() >= $minPriority) {
|
||||
$n += $feed->nbNotRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,19 +245,19 @@ SQL;
|
|||
$sql = 'SELECT * FROM `_category` WHERE id=:id';
|
||||
$res = $this->fetchAssoc($sql, ['id' => $id]) ?? [];
|
||||
/** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate':int,'error':int|bool,'attributes':string}> $res */
|
||||
$cat = self::daoToCategory($res);
|
||||
return $cat[0] ?? null;
|
||||
$categories = self::daoToCategories($res);
|
||||
return reset($categories) ?: null;
|
||||
}
|
||||
|
||||
public function searchByName(string $name): ?FreshRSS_Category {
|
||||
$sql = 'SELECT * FROM `_category` WHERE name=:name';
|
||||
$res = $this->fetchAssoc($sql, ['name' => $name]) ?? [];
|
||||
/** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate':int,'error':int|bool,'attributes':string}> $res */
|
||||
$cat = self::daoToCategory($res);
|
||||
return $cat[0] ?? null;
|
||||
$categories = self::daoToCategories($res);
|
||||
return reset($categories) ?: null;
|
||||
}
|
||||
|
||||
/** @return array<FreshRSS_Category> */
|
||||
/** @return array<int,FreshRSS_Category> */
|
||||
public function listSortedCategories(bool $prePopulateFeeds = true, bool $details = false): array {
|
||||
$categories = $this->listCategories($prePopulateFeeds, $details);
|
||||
|
||||
|
@ -277,7 +277,7 @@ SQL;
|
|||
return $categories;
|
||||
}
|
||||
|
||||
/** @return array<FreshRSS_Category> */
|
||||
/** @return array<int,FreshRSS_Category> */
|
||||
public function listCategories(bool $prePopulateFeeds = true, bool $details = false): array {
|
||||
if ($prePopulateFeeds) {
|
||||
$sql = 'SELECT c.id AS c_id, c.name AS c_name, c.kind AS c_kind, c.`lastUpdate` AS c_last_update, c.error AS c_error, c.attributes AS c_attributes, '
|
||||
|
@ -293,7 +293,7 @@ SQL;
|
|||
$res = $stm->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||
/** @var array<array{'c_name':string,'c_id':int,'c_kind':int,'c_last_update':int,'c_error':int|bool,'c_attributes'?:string,
|
||||
* 'id'?:int,'name'?:string,'url'?:string,'kind'?:int,'category'?:int,'website'?:string,'priority'?:int,'error'?:int|bool,'cache_nbEntries'?:int,'cache_nbUnreads'?:int,'ttl'?:int}> $res */
|
||||
return self::daoToCategoryPrepopulated($res);
|
||||
return self::daoToCategoriesPrepopulated($res);
|
||||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
|
@ -305,11 +305,11 @@ SQL;
|
|||
} else {
|
||||
$res = $this->fetchAssoc('SELECT * FROM `_category` ORDER BY name');
|
||||
/** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */
|
||||
return $res == null ? [] : self::daoToCategory($res);
|
||||
return empty($res) ? [] : self::daoToCategories($res);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<FreshRSS_Category> */
|
||||
/** @return array<int,FreshRSS_Category> */
|
||||
public function listCategoriesOrderUpdate(int $defaultCacheDuration = 86400, int $limit = 0): array {
|
||||
$sql = 'SELECT * FROM `_category` WHERE kind = :kind AND `lastUpdate` < :lu ORDER BY `lastUpdate`'
|
||||
. ($limit < 1 ? '' : ' LIMIT ' . $limit);
|
||||
|
@ -318,7 +318,7 @@ SQL;
|
|||
$stm->bindValue(':kind', FreshRSS_Category::KIND_DYNAMIC_OPML, PDO::PARAM_INT) &&
|
||||
$stm->bindValue(':lu', time() - $defaultCacheDuration, PDO::PARAM_INT) &&
|
||||
$stm->execute()) {
|
||||
return self::daoToCategory($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
return self::daoToCategories($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
} else {
|
||||
$info = $stm ? $stm->errorInfo() : $this->pdo->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
|
@ -333,9 +333,9 @@ SQL;
|
|||
$sql = 'SELECT * FROM `_category` WHERE id=:id';
|
||||
$res = $this->fetchAssoc($sql, [':id' => self::DEFAULTCATEGORYID]) ?? [];
|
||||
/** @var array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $res */
|
||||
$cat = self::daoToCategory($res);
|
||||
if (isset($cat[0])) {
|
||||
return $cat[0];
|
||||
$categories = self::daoToCategories($res);
|
||||
if (isset($categories[self::DEFAULTCATEGORYID])) {
|
||||
return $categories[self::DEFAULTCATEGORYID];
|
||||
} else {
|
||||
if (FreshRSS_Context::$isCli) {
|
||||
fwrite(STDERR, 'FreshRSS database error: Default category not found!' . "\n");
|
||||
|
@ -394,41 +394,13 @@ SQL;
|
|||
return isset($res[0]) ? (int)$res[0] : -1;
|
||||
}
|
||||
|
||||
/** @param array<FreshRSS_Category> $categories */
|
||||
public static function findFeed(array $categories, int $feed_id): ?FreshRSS_Feed {
|
||||
foreach ($categories as $category) {
|
||||
foreach ($category->feeds() as $feed) {
|
||||
if ($feed->id() === $feed_id) {
|
||||
$feed->_category($category); // Should already be done; just to be safe
|
||||
return $feed;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<FreshRSS_Category> $categories
|
||||
*/
|
||||
public static function countUnread(array $categories, int $minPriority = 0): int {
|
||||
$n = 0;
|
||||
foreach ($categories as $category) {
|
||||
foreach ($category->feeds() as $feed) {
|
||||
if ($feed->priority() >= $minPriority) {
|
||||
$n += $feed->nbNotRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array{'c_name':string,'c_id':int,'c_kind':int,'c_last_update':int,'c_error':int|bool,'c_attributes'?:string,
|
||||
* 'id'?:int,'name'?:string,'url'?:string,'kind'?:int,'website'?:string,'priority'?:int,
|
||||
* 'error'?:int|bool,'cache_nbEntries'?:int,'cache_nbUnreads'?:int,'ttl'?:int}> $listDAO
|
||||
* @return array<int,FreshRSS_Category>
|
||||
*/
|
||||
private static function daoToCategoryPrepopulated(array $listDAO): array {
|
||||
private static function daoToCategoriesPrepopulated(array $listDAO): array {
|
||||
$list = [];
|
||||
$previousLine = [];
|
||||
$feedsDao = [];
|
||||
|
@ -441,11 +413,11 @@ SQL;
|
|||
$cat = new FreshRSS_Category(
|
||||
$previousLine['c_name'],
|
||||
$previousLine['c_id'],
|
||||
$feedDao::daoToFeed($feedsDao, $previousLine['c_id'])
|
||||
$feedDao::daoToFeeds($feedsDao, $previousLine['c_id'])
|
||||
);
|
||||
$cat->_kind($previousLine['c_kind']);
|
||||
$cat->_attributes($previousLine['c_attributes'] ?? '[]');
|
||||
$list[(int)$previousLine['c_id']] = $cat;
|
||||
$list[$cat->id()] = $cat;
|
||||
|
||||
$feedsDao = []; //Prepare for next category
|
||||
}
|
||||
|
@ -459,13 +431,13 @@ SQL;
|
|||
$cat = new FreshRSS_Category(
|
||||
$previousLine['c_name'],
|
||||
$previousLine['c_id'],
|
||||
$feedDao::daoToFeed($feedsDao, $previousLine['c_id'])
|
||||
$feedDao::daoToFeeds($feedsDao, $previousLine['c_id'])
|
||||
);
|
||||
$cat->_kind($previousLine['c_kind']);
|
||||
$cat->_lastUpdate($previousLine['c_last_update'] ?? 0);
|
||||
$cat->_error($previousLine['c_error'] ?? 0);
|
||||
$cat->_attributes($previousLine['c_attributes'] ?? []);
|
||||
$list[(int)$previousLine['c_id']] = $cat;
|
||||
$list[$cat->id()] = $cat;
|
||||
}
|
||||
|
||||
return $list;
|
||||
|
@ -473,11 +445,10 @@ SQL;
|
|||
|
||||
/**
|
||||
* @param array<array{'name':string,'id':int,'kind':int,'lastUpdate'?:int,'error'?:int|bool,'attributes'?:string}> $listDAO
|
||||
* @return array<FreshRSS_Category>
|
||||
* @return array<int,FreshRSS_Category>
|
||||
*/
|
||||
private static function daoToCategory(array $listDAO): array {
|
||||
private static function daoToCategories(array $listDAO): array {
|
||||
$list = [];
|
||||
|
||||
foreach ($listDAO as $dao) {
|
||||
FreshRSS_DatabaseDAO::pdoInt($dao, ['id', 'kind', 'lastUpdate', 'error']);
|
||||
$cat = new FreshRSS_Category(
|
||||
|
@ -488,9 +459,8 @@ SQL;
|
|||
$cat->_lastUpdate($dao['lastUpdate'] ?? 0);
|
||||
$cat->_error($dao['error'] ?? 0);
|
||||
$cat->_attributes($dao['attributes'] ?? '');
|
||||
$list[] = $cat;
|
||||
$list[$cat->id()] = $cat;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,11 @@ final class FreshRSS_Context {
|
|||
/**
|
||||
* @var array<int,FreshRSS_Category>
|
||||
*/
|
||||
public static array $categories = [];
|
||||
private static array $categories = [];
|
||||
/**
|
||||
* @var array<int,FreshRSS_Tag>
|
||||
*/
|
||||
public static array $tags = [];
|
||||
private static array $tags = [];
|
||||
public static string $name = '';
|
||||
public static string $description = '';
|
||||
public static int $total_unread = 0;
|
||||
|
@ -47,6 +47,7 @@ final class FreshRSS_Context {
|
|||
*/
|
||||
public static string $order = 'DESC';
|
||||
public static int $number = 0;
|
||||
public static int $offset = 0;
|
||||
public static FreshRSS_BooleanSearch $search;
|
||||
public static string $first_id = '';
|
||||
public static string $next_id = '';
|
||||
|
@ -173,10 +174,33 @@ final class FreshRSS_Context {
|
|||
FreshRSS_Context::$user_conf = null;
|
||||
}
|
||||
|
||||
/** @return array<int,FreshRSS_Category> */
|
||||
public static function categories(): array {
|
||||
if (empty(self::$categories)) {
|
||||
$catDAO = FreshRSS_Factory::createCategoryDao();
|
||||
self::$categories = $catDAO->listSortedCategories(true, false);
|
||||
}
|
||||
return self::$categories;
|
||||
}
|
||||
|
||||
/** @return array<int,FreshRSS_Feed> */
|
||||
public static function feeds(): array {
|
||||
return FreshRSS_Category::findFeeds(self::categories());
|
||||
}
|
||||
|
||||
/** @return array<int,FreshRSS_Tag> */
|
||||
public static function labels(bool $precounts = false): array {
|
||||
if (empty(self::$tags) || $precounts) {
|
||||
$tagDAO = FreshRSS_Factory::createTagDao();
|
||||
self::$tags = $tagDAO->listTags($precounts) ?: [];
|
||||
}
|
||||
return self::$tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* This action updates the Context object by using request parameters.
|
||||
*
|
||||
* Parameters are:
|
||||
* HTTP GET request parameters are:
|
||||
* - state (default: conf->default_view)
|
||||
* - search (default: empty string)
|
||||
* - order (default: conf->sort_order)
|
||||
|
@ -187,18 +211,15 @@ final class FreshRSS_Context {
|
|||
* @throws Minz_ConfigurationNamespaceException
|
||||
* @throws Minz_PDOConnectionException
|
||||
*/
|
||||
public static function updateUsingRequest(): void {
|
||||
if (empty(self::$categories)) {
|
||||
$catDAO = FreshRSS_Factory::createCategoryDao();
|
||||
self::$categories = $catDAO->listSortedCategories();
|
||||
public static function updateUsingRequest(bool $computeStatistics): void {
|
||||
if ($computeStatistics && self::$total_unread === 0) {
|
||||
// Update number of read / unread variables.
|
||||
$entryDAO = FreshRSS_Factory::createEntryDao();
|
||||
self::$total_starred = $entryDAO->countUnreadReadFavorites();
|
||||
self::$total_unread = FreshRSS_Category::countUnread(self::categories(), FreshRSS_Feed::PRIORITY_MAIN_STREAM);
|
||||
self::$total_important_unread = FreshRSS_Category::countUnread(self::categories(), FreshRSS_Feed::PRIORITY_IMPORTANT);
|
||||
}
|
||||
|
||||
// Update number of read / unread variables.
|
||||
$entryDAO = FreshRSS_Factory::createEntryDao();
|
||||
self::$total_starred = $entryDAO->countUnreadReadFavorites();
|
||||
self::$total_unread = FreshRSS_CategoryDAO::countUnread(self::$categories, FreshRSS_Feed::PRIORITY_MAIN_STREAM);
|
||||
self::$total_important_unread = FreshRSS_CategoryDAO::countUnread(self::$categories, FreshRSS_Feed::PRIORITY_IMPORTANT);
|
||||
|
||||
self::_get(Minz_Request::paramString('get') ?: 'a');
|
||||
|
||||
self::$state = Minz_Request::paramInt('state') ?: FreshRSS_Context::userConf()->default_state;
|
||||
|
@ -224,6 +245,7 @@ final class FreshRSS_Context {
|
|||
FreshRSS_Context::userConf()->max_posts_per_rss,
|
||||
FreshRSS_Context::userConf()->posts_per_page);
|
||||
}
|
||||
self::$offset = Minz_Request::paramInt('offset');
|
||||
self::$first_id = Minz_Request::paramString('next');
|
||||
self::$sinceHours = Minz_Request::paramInt('hours');
|
||||
}
|
||||
|
@ -394,7 +416,7 @@ final class FreshRSS_Context {
|
|||
break;
|
||||
case 'f':
|
||||
// We try to find the corresponding feed. When allowing robots, always retrieve the full feed including description
|
||||
$feed = FreshRSS_Context::systemConf()->allow_robots ? null : FreshRSS_CategoryDAO::findFeed(self::$categories, $id);
|
||||
$feed = FreshRSS_Context::systemConf()->allow_robots ? null : FreshRSS_Category::findFeed(self::$categories, $id);
|
||||
if ($feed === null) {
|
||||
$feedDAO = FreshRSS_Factory::createFeedDao();
|
||||
$feed = $feedDAO->searchById($id);
|
||||
|
@ -417,7 +439,7 @@ final class FreshRSS_Context {
|
|||
if ($cat === null) {
|
||||
throw new FreshRSS_Context_Exception('Invalid category: ' . $id);
|
||||
}
|
||||
//self::$categories[$id] = $cat;
|
||||
self::$categories[$id] = $cat;
|
||||
} else {
|
||||
$cat = self::$categories[$id];
|
||||
}
|
||||
|
@ -433,7 +455,7 @@ final class FreshRSS_Context {
|
|||
if ($tag === null) {
|
||||
throw new FreshRSS_Context_Exception('Invalid tag: ' . $id);
|
||||
}
|
||||
//self::$tags[$id] = $tag;
|
||||
self::$tags[$id] = $tag;
|
||||
} else {
|
||||
$tag = self::$tags[$id];
|
||||
}
|
||||
|
|
|
@ -815,6 +815,28 @@ HTML;
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{array<string>,array<string>} Array of first tags to show, then array of remaining tags
|
||||
*/
|
||||
public function tagsFormattingHelper(): array {
|
||||
$firstTags = [];
|
||||
$remainingTags = [];
|
||||
|
||||
if (FreshRSS_Context::hasUserConf() && in_array(FreshRSS_Context::userConf()->show_tags, ['b', 'f', 'h'], true)) {
|
||||
$maxTagsDisplayed = (int)FreshRSS_Context::userConf()->show_tags_max;
|
||||
$tags = $this->tags();
|
||||
if (!empty($tags)) {
|
||||
if ($maxTagsDisplayed > 0) {
|
||||
$firstTags = array_slice($tags, 0, $maxTagsDisplayed);
|
||||
$remainingTags = array_slice($tags, $maxTagsDisplayed);
|
||||
} else {
|
||||
$firstTags = $tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$firstTags,$remainingTags];
|
||||
}
|
||||
|
||||
/**
|
||||
* Integer format conversion for Google Reader API format
|
||||
* @param string|int $dec Decimal number
|
||||
|
|
|
@ -1063,7 +1063,7 @@ SQL;
|
|||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
private function sqlListWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL,
|
||||
string $order = 'DESC', int $limit = 1, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $order = 'DESC', int $limit = 1, int $offset = 0, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null,
|
||||
int $date_min = 0): array {
|
||||
if (!$state) {
|
||||
$state = FreshRSS_Entry::STATE_ALL;
|
||||
|
@ -1120,7 +1120,9 @@ SQL;
|
|||
. 'WHERE ' . $where
|
||||
. $search
|
||||
. 'ORDER BY e.id ' . $order
|
||||
. ($limit > 0 ? ' LIMIT ' . intval($limit) : '')]; //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
|
||||
. ($limit > 0 ? ' LIMIT ' . $limit : '') // http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
|
||||
. ($offset > 0 ? ' OFFSET ' . $offset : '')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1131,9 +1133,9 @@ SQL;
|
|||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
private function listWhereRaw(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL,
|
||||
string $order = 'DESC', int $limit = 1, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null,
|
||||
string $order = 'DESC', int $limit = 1, int $offset = 0, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null,
|
||||
int $date_min = 0) {
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $order, $limit, $offset, $firstId, $filters, $date_min);
|
||||
|
||||
if ($order !== 'DESC' && $order !== 'ASC') {
|
||||
$order = 'DESC';
|
||||
|
@ -1152,7 +1154,7 @@ SQL;
|
|||
} else {
|
||||
$info = $stm == null ? $this->pdo->errorInfo() : $stm->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
return $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
|
||||
return $this->listWhereRaw($type, $id, $state, $order, $limit, $offset, $firstId, $filters, $date_min);
|
||||
}
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
return false;
|
||||
|
@ -1167,9 +1169,9 @@ SQL;
|
|||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
public function listWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL,
|
||||
string $order = 'DESC', int $limit = 1, string $firstId = '',
|
||||
string $order = 'DESC', int $limit = 1, int $offset = 0, string $firstId = '',
|
||||
?FreshRSS_BooleanSearch $filters = null, int $date_min = 0): Traversable {
|
||||
$stm = $this->listWhereRaw($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
|
||||
$stm = $this->listWhereRaw($type, $id, $state, $order, $limit, $offset, $firstId, $filters, $date_min);
|
||||
if ($stm) {
|
||||
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
||||
if (is_array($row)) {
|
||||
|
@ -1233,9 +1235,9 @@ SQL;
|
|||
* @throws FreshRSS_EntriesGetter_Exception
|
||||
*/
|
||||
public function listIdsWhere(string $type = 'a', int $id = 0, int $state = FreshRSS_Entry::STATE_ALL,
|
||||
string $order = 'DESC', int $limit = 1, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null): ?array {
|
||||
string $order = 'DESC', int $limit = 1, int $offset = 0, string $firstId = '', ?FreshRSS_BooleanSearch $filters = null): ?array {
|
||||
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters);
|
||||
[$values, $sql] = $this->sqlListWhere($type, $id, $state, $order, $limit, $offset, $firstId, $filters);
|
||||
$stm = $this->pdo->prepare($sql);
|
||||
if ($stm !== false && $stm->execute($values) && ($res = $stm->fetchAll(PDO::FETCH_COLUMN, 0)) !== false) {
|
||||
/** @var array<numeric-string> $res */
|
||||
|
|
|
@ -76,7 +76,7 @@ class FreshRSS_Feed extends Minz_Model {
|
|||
}
|
||||
}
|
||||
|
||||
public static function example(): FreshRSS_Feed {
|
||||
public static function default(): FreshRSS_Feed {
|
||||
$f = new FreshRSS_Feed('http://example.net/', false);
|
||||
$f->faviconPrepare();
|
||||
return $f;
|
||||
|
@ -708,7 +708,8 @@ class FreshRSS_Feed extends Minz_Model {
|
|||
$view = new FreshRSS_View();
|
||||
$view->_path('index/rss.phtml');
|
||||
$view->internal_rendering = true;
|
||||
$view->rss_url = $feedSourceUrl;
|
||||
$view->rss_url = htmlspecialchars($feedSourceUrl, ENT_COMPAT, 'UTF-8');
|
||||
$view->html_url = $view->rss_url;
|
||||
$view->entries = [];
|
||||
|
||||
try {
|
||||
|
|
|
@ -322,7 +322,7 @@ SQL;
|
|||
}
|
||||
/** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
||||
* 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */
|
||||
$feeds = self::daoToFeed($res);
|
||||
$feeds = self::daoToFeeds($res);
|
||||
return $feeds[$id] ?? null;
|
||||
}
|
||||
|
||||
|
@ -331,7 +331,7 @@ SQL;
|
|||
$res = $this->fetchAssoc($sql, [':url' => $url]);
|
||||
/** @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
||||
* 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res */
|
||||
return empty($res[0]) ? null : (current(self::daoToFeed($res)) ?: null);
|
||||
return empty($res[0]) ? null : (current(self::daoToFeeds($res)) ?: null);
|
||||
}
|
||||
|
||||
/** @return array<int> */
|
||||
|
@ -343,14 +343,14 @@ SQL;
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array<FreshRSS_Feed>
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
*/
|
||||
public function listFeeds(): array {
|
||||
$sql = 'SELECT * FROM `_feed` ORDER BY name';
|
||||
$res = $this->fetchAssoc($sql);
|
||||
/** @var array<array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
||||
* 'priority':int,'pathEntries':string,'httpAuth':string,'error':int,'ttl':int,'attributes':string}>|null $res */
|
||||
return $res == null ? [] : self::daoToFeed($res);
|
||||
return $res == null ? [] : self::daoToFeeds($res);
|
||||
}
|
||||
|
||||
/** @return array<string,string> */
|
||||
|
@ -375,7 +375,7 @@ SQL;
|
|||
|
||||
/**
|
||||
* @param int $defaultCacheDuration Use -1 to return all feeds, without filtering them by TTL.
|
||||
* @return array<FreshRSS_Feed>
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
*/
|
||||
public function listFeedsOrderUpdate(int $defaultCacheDuration = 3600, int $limit = 0): array {
|
||||
$sql = 'SELECT id, url, kind, category, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, ttl, attributes, `cache_nbEntries`, `cache_nbUnreads` '
|
||||
|
@ -387,7 +387,7 @@ SQL;
|
|||
. ($limit < 1 ? '' : 'LIMIT ' . intval($limit));
|
||||
$stm = $this->pdo->query($sql);
|
||||
if ($stm !== false) {
|
||||
return self::daoToFeed($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
return self::daoToFeeds($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
} else {
|
||||
$info = $this->pdo->errorInfo();
|
||||
if ($this->autoUpdateDb($info)) {
|
||||
|
@ -409,7 +409,7 @@ SQL;
|
|||
|
||||
/**
|
||||
* @param bool|null $muted to include only muted feeds
|
||||
* @return array<FreshRSS_Feed>
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
*/
|
||||
public function listByCategory(int $cat, ?bool $muted = null): array {
|
||||
$sql = 'SELECT * FROM `_feed` WHERE category=:category';
|
||||
|
@ -425,9 +425,9 @@ SQL;
|
|||
* @var array<int,array{'url':string,'kind':int,'category':int,'name':string,'website':string,'lastUpdate':int,
|
||||
* 'priority'?:int,'pathEntries'?:string,'httpAuth':string,'error':int,'ttl'?:int,'attributes'?:string}> $res
|
||||
*/
|
||||
$feeds = self::daoToFeed($res);
|
||||
$feeds = self::daoToFeeds($res);
|
||||
|
||||
usort($feeds, static function (FreshRSS_Feed $a, FreshRSS_Feed $b) {
|
||||
uasort($feeds, static function (FreshRSS_Feed $a, FreshRSS_Feed $b) {
|
||||
return strnatcasecmp($a->name(), $b->name());
|
||||
});
|
||||
|
||||
|
@ -585,7 +585,7 @@ SQL;
|
|||
* 'pathEntries'?:string,'httpAuth'?:string,'error'?:int|bool,'ttl'?:int,'attributes'?:string,'cache_nbUnreads'?:int,'cache_nbEntries'?:int}> $listDAO
|
||||
* @return array<int,FreshRSS_Feed>
|
||||
*/
|
||||
public static function daoToFeed(array $listDAO, ?int $catID = null): array {
|
||||
public static function daoToFeeds(array $listDAO, ?int $catID = null): array {
|
||||
$list = [];
|
||||
|
||||
foreach ($listDAO as $key => $dao) {
|
||||
|
|
|
@ -184,16 +184,16 @@ SQL;
|
|||
public function searchById(int $id): ?FreshRSS_Tag {
|
||||
$res = $this->fetchAssoc('SELECT * FROM `_tag` WHERE id=:id', [':id' => $id]);
|
||||
/** @var array<array{'id':int,'name':string,'attributes'?:string}>|null $res */
|
||||
return $res === null ? null : self::daoToTag($res)[0] ?? null;
|
||||
return $res === null ? null : (current(self::daoToTags($res)) ?: null);
|
||||
}
|
||||
|
||||
public function searchByName(string $name): ?FreshRSS_Tag {
|
||||
$res = $this->fetchAssoc('SELECT * FROM `_tag` WHERE name=:name', [':name' => $name]);
|
||||
/** @var array<array{'id':int,'name':string,'attributes'?:string}>|null $res */
|
||||
return $res === null ? null : self::daoToTag($res)[0] ?? null;
|
||||
return $res === null ? null : (current(self::daoToTags($res)) ?: null);
|
||||
}
|
||||
|
||||
/** @return array<FreshRSS_Tag>|false */
|
||||
/** @return array<int,FreshRSS_Tag>|false */
|
||||
public function listTags(bool $precounts = false) {
|
||||
if ($precounts) {
|
||||
$sql = <<<'SQL'
|
||||
|
@ -211,7 +211,7 @@ SQL;
|
|||
$stm = $this->pdo->query($sql);
|
||||
if ($stm !== false) {
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||
return self::daoToTag($res);
|
||||
return self::daoToTags($res);
|
||||
} else {
|
||||
$info = $this->pdo->errorInfo();
|
||||
Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info));
|
||||
|
@ -430,9 +430,9 @@ SQL;
|
|||
|
||||
/**
|
||||
* @param iterable<array{'id':int,'name':string,'attributes'?:string}> $listDAO
|
||||
* @return array<FreshRSS_Tag>
|
||||
* @return array<int,FreshRSS_Tag>
|
||||
*/
|
||||
private static function daoToTag(iterable $listDAO): array {
|
||||
private static function daoToTags(iterable $listDAO): array {
|
||||
$list = [];
|
||||
foreach ($listDAO as $dao) {
|
||||
if (empty($dao['id']) || empty($dao['name'])) {
|
||||
|
@ -446,7 +446,7 @@ SQL;
|
|||
if (isset($dao['unreads'])) {
|
||||
$tag->_nbUnread($dao['unreads']);
|
||||
}
|
||||
$list[] = $tag;
|
||||
$list[$tag->id()] = $tag;
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ declare(strict_types=1);
|
|||
* @property bool $onread_jump_next
|
||||
* @property string $passwordHash
|
||||
* @property int $posts_per_page
|
||||
* @property array<array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string}> $queries
|
||||
* @property array<array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string,'token'?:string}> $queries
|
||||
* @property bool $reading_confirm
|
||||
* @property int $since_hours_posts_per_rss
|
||||
* @property bool $show_fav_unread
|
||||
|
@ -81,6 +81,20 @@ final class FreshRSS_UserConfiguration extends Minz_Configuration {
|
|||
return parent::get('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the default configuration for users.
|
||||
* @throws Minz_FileNotExistException
|
||||
*/
|
||||
public static function default(): FreshRSS_UserConfiguration {
|
||||
static $default_user_conf = null;
|
||||
if ($default_user_conf == null) {
|
||||
$namespace = 'user_default';
|
||||
FreshRSS_UserConfiguration::register($namespace, '_', FRESHRSS_PATH . '/config-user.default.php');
|
||||
$default_user_conf = FreshRSS_UserConfiguration::get($namespace);
|
||||
}
|
||||
return $default_user_conf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $key
|
||||
* @return array<int|string,mixed>|null
|
||||
|
|
|
@ -18,17 +18,34 @@ class FreshRSS_UserQuery {
|
|||
private FreshRSS_BooleanSearch $search;
|
||||
private int $state = 0;
|
||||
private string $url = '';
|
||||
private ?FreshRSS_FeedDAO $feed_dao;
|
||||
private ?FreshRSS_CategoryDAO $category_dao;
|
||||
private ?FreshRSS_TagDAO $tag_dao;
|
||||
private string $token = '';
|
||||
private bool $shareRss = false;
|
||||
private bool $shareOpml = false;
|
||||
/** @var array<int,FreshRSS_Category> $categories */
|
||||
private array $categories;
|
||||
/** @var array<int,FreshRSS_Tag> $labels */
|
||||
private array $labels;
|
||||
|
||||
public static function generateToken(string $salt): string {
|
||||
if (!FreshRSS_Context::hasSystemConf()) {
|
||||
return '';
|
||||
}
|
||||
$hash = md5(FreshRSS_Context::systemConf()->salt . $salt . random_bytes(16));
|
||||
if (function_exists('gmp_init')) {
|
||||
// Shorten the hash if possible by converting from base 16 to base 62
|
||||
$hash = gmp_strval(gmp_init($hash, 16), 62);
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string} $query
|
||||
* @param array{get?:string,name?:string,order?:string,search?:string,state?:int,url?:string,token?:string,shareRss?:bool,shareOpml?:bool} $query
|
||||
* @param array<int,FreshRSS_Category> $categories
|
||||
* @param array<int,FreshRSS_Tag> $labels
|
||||
*/
|
||||
public function __construct(array $query, FreshRSS_FeedDAO $feed_dao = null, FreshRSS_CategoryDAO $category_dao = null, FreshRSS_TagDAO $tag_dao = null) {
|
||||
$this->category_dao = $category_dao;
|
||||
$this->feed_dao = $feed_dao;
|
||||
$this->tag_dao = $tag_dao;
|
||||
public function __construct(array $query, array $categories, array $labels) {
|
||||
$this->categories = $categories;
|
||||
$this->labels = $labels;
|
||||
if (isset($query['get'])) {
|
||||
$this->parseGet($query['get']);
|
||||
}
|
||||
|
@ -49,8 +66,18 @@ class FreshRSS_UserQuery {
|
|||
if (!isset($query['search'])) {
|
||||
$query['search'] = '';
|
||||
}
|
||||
if (!empty($query['token'])) {
|
||||
$this->token = $query['token'];
|
||||
}
|
||||
if (isset($query['shareRss'])) {
|
||||
$this->shareRss = $query['shareRss'];
|
||||
}
|
||||
if (isset($query['shareOpml'])) {
|
||||
$this->shareOpml = $query['shareOpml'];
|
||||
}
|
||||
|
||||
// linked too deeply with the search object, need to use dependency injection
|
||||
$this->search = new FreshRSS_BooleanSearch($query['search']);
|
||||
$this->search = new FreshRSS_BooleanSearch($query['search'], 0, 'AND', false);
|
||||
if (!empty($query['state'])) {
|
||||
$this->state = intval($query['state']);
|
||||
}
|
||||
|
@ -59,16 +86,19 @@ class FreshRSS_UserQuery {
|
|||
/**
|
||||
* Convert the current object to an array.
|
||||
*
|
||||
* @return array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string}
|
||||
* @return array{'get'?:string,'name'?:string,'order'?:string,'search'?:string,'state'?:int,'url'?:string,'token'?:string}
|
||||
*/
|
||||
public function toArray(): array {
|
||||
return array_filter([
|
||||
'get' => $this->get,
|
||||
'name' => $this->name,
|
||||
'order' => $this->order,
|
||||
'search' => $this->search->__toString(),
|
||||
'search' => $this->search->getRawInput(),
|
||||
'state' => $this->state,
|
||||
'url' => $this->url,
|
||||
'token' => $this->token,
|
||||
'shareRss' => $this->shareRss,
|
||||
'shareOpml' => $this->shareOpml,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -77,92 +107,43 @@ class FreshRSS_UserQuery {
|
|||
*/
|
||||
private function parseGet(string $get): void {
|
||||
$this->get = $get;
|
||||
if (preg_match('/(?P<type>[acfst])(_(?P<id>\d+))?/', $get, $matches)) {
|
||||
if (preg_match('/(?P<type>[acfistT])(_(?P<id>\d+))?/', $get, $matches)) {
|
||||
$id = intval($matches['id'] ?? '0');
|
||||
switch ($matches['type']) {
|
||||
case 'a':
|
||||
$this->parseAll();
|
||||
$this->get_type = 'all';
|
||||
break;
|
||||
case 'c':
|
||||
$this->parseCategory($id);
|
||||
$this->get_type = 'category';
|
||||
$c = $this->categories[$id] ?? null;
|
||||
$this->get_name = $c === null ? '' : $c->name();
|
||||
break;
|
||||
case 'f':
|
||||
$this->parseFeed($id);
|
||||
$this->get_type = 'feed';
|
||||
$f = FreshRSS_Category::findFeed($this->categories, $id);
|
||||
$this->get_name = $f === null ? '' : $f->name();
|
||||
break;
|
||||
case 'i':
|
||||
$this->get_type = 'important';
|
||||
break;
|
||||
case 's':
|
||||
$this->parseFavorite();
|
||||
$this->get_type = 'favorite';
|
||||
break;
|
||||
case 't':
|
||||
$this->parseTag($id);
|
||||
$this->get_type = 'label';
|
||||
$l = $this->labels[$id] ?? null;
|
||||
$this->get_name = $l === null ? '' : $l->name();
|
||||
break;
|
||||
case 'T':
|
||||
$this->get_type = 'all_labels';
|
||||
break;
|
||||
}
|
||||
if ($this->get_name === '' && in_array($matches['type'], ['c', 'f', 't'], true)) {
|
||||
$this->deprecated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the query string when it is an "all" query
|
||||
*/
|
||||
private function parseAll(): void {
|
||||
$this->get_name = 'all';
|
||||
$this->get_type = 'all';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the query string when it is a "category" query
|
||||
*/
|
||||
private function parseCategory(int $id): void {
|
||||
if ($this->category_dao === null) {
|
||||
$this->category_dao = FreshRSS_Factory::createCategoryDao();
|
||||
}
|
||||
$category = $this->category_dao->searchById($id);
|
||||
if ($category !== null) {
|
||||
$this->get_name = $category->name();
|
||||
} else {
|
||||
$this->deprecated = true;
|
||||
}
|
||||
$this->get_type = 'category';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the query string when it is a "feed" query
|
||||
*/
|
||||
private function parseFeed(int $id): void {
|
||||
if ($this->feed_dao === null) {
|
||||
$this->feed_dao = FreshRSS_Factory::createFeedDao();
|
||||
}
|
||||
$feed = $this->feed_dao->searchById($id);
|
||||
if ($feed !== null) {
|
||||
$this->get_name = $feed->name();
|
||||
} else {
|
||||
$this->deprecated = true;
|
||||
}
|
||||
$this->get_type = 'feed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the query string when it is a "tag" query
|
||||
*/
|
||||
private function parseTag(int $id): void {
|
||||
if ($this->tag_dao === null) {
|
||||
$this->tag_dao = FreshRSS_Factory::createTagDao();
|
||||
}
|
||||
$tag = $this->tag_dao->searchById($id);
|
||||
if ($tag !== null) {
|
||||
$this->get_name = $tag->name();
|
||||
} else {
|
||||
$this->deprecated = true;
|
||||
}
|
||||
$this->get_type = 'tag';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the query string when it is a "favorite" query
|
||||
*/
|
||||
private function parseFavorite(): void {
|
||||
$this->get_name = 'favorite';
|
||||
$this->get_type = 'favorite';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user query is deprecated.
|
||||
* It is deprecated if the category or the feed used in the query are
|
||||
|
@ -219,7 +200,7 @@ class FreshRSS_UserQuery {
|
|||
}
|
||||
|
||||
public function getOrder(): string {
|
||||
return $this->order;
|
||||
return $this->order ?: FreshRSS_Context::userConf()->sort_order;
|
||||
}
|
||||
|
||||
public function getSearch(): FreshRSS_BooleanSearch {
|
||||
|
@ -227,11 +208,74 @@ class FreshRSS_UserQuery {
|
|||
}
|
||||
|
||||
public function getState(): int {
|
||||
return $this->state;
|
||||
$state = $this->state;
|
||||
if (!($state & FreshRSS_Entry::STATE_READ) && !($state & FreshRSS_Entry::STATE_NOT_READ)) {
|
||||
$state |= FreshRSS_Entry::STATE_READ | FreshRSS_Entry::STATE_NOT_READ;
|
||||
}
|
||||
if (!($state & FreshRSS_Entry::STATE_FAVORITE) && !($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) {
|
||||
$state |= FreshRSS_Entry::STATE_FAVORITE | FreshRSS_Entry::STATE_NOT_FAVORITE;
|
||||
}
|
||||
return $state;
|
||||
}
|
||||
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function getToken(): string {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
public function setToken(string $token): void {
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
public function setShareRss(bool $shareRss): void {
|
||||
$this->shareRss = $shareRss;
|
||||
}
|
||||
|
||||
public function shareRss(): bool {
|
||||
return $this->shareRss;
|
||||
}
|
||||
|
||||
public function setShareOpml(bool $shareOpml): void {
|
||||
$this->shareOpml = $shareOpml;
|
||||
}
|
||||
|
||||
public function shareOpml(): bool {
|
||||
return $this->shareOpml;
|
||||
}
|
||||
|
||||
protected function sharedUrl(bool $xmlEscaped = true): string {
|
||||
$currentUser = Minz_User::name() ?? '';
|
||||
return Minz_Url::display("/api/query.php?user={$currentUser}&t={$this->token}", $xmlEscaped ? 'html' : '', true);
|
||||
}
|
||||
|
||||
public function sharedUrlRss(bool $xmlEscaped = true): string {
|
||||
if ($this->shareRss && $this->token !== '') {
|
||||
return $this->sharedUrl($xmlEscaped) . ($xmlEscaped ? '&' : '&') . 'f=rss';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function sharedUrlHtml(bool $xmlEscaped = true): string {
|
||||
if ($this->shareRss && $this->token !== '') {
|
||||
return $this->sharedUrl($xmlEscaped) . ($xmlEscaped ? '&' : '&') . 'f=html';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* OPML is only safe for some query types, otherwise it risks leaking unwanted feed information.
|
||||
*/
|
||||
public function safeForOpml(): bool {
|
||||
return in_array($this->get_type, ['all', 'category', 'feed'], true);
|
||||
}
|
||||
|
||||
public function sharedUrlOpml(bool $xmlEscaped = true): string {
|
||||
if ($this->shareOpml && $this->token !== '' && $this->safeForOpml()) {
|
||||
return $this->sharedUrl($xmlEscaped) . ($xmlEscaped ? '&' : '&') . 'f=opml';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class FreshRSS_View extends Minz_View {
|
|||
public $callbackBeforeFeeds;
|
||||
/** @var callable */
|
||||
public $callbackBeforePagination;
|
||||
/** @var array<FreshRSS_Category> */
|
||||
/** @var array<int,FreshRSS_Category> */
|
||||
public array $categories;
|
||||
public ?FreshRSS_Category $category;
|
||||
public ?FreshRSS_Tag $tag;
|
||||
|
@ -18,11 +18,11 @@ class FreshRSS_View extends Minz_View {
|
|||
/** @var iterable<FreshRSS_Entry> */
|
||||
public $entries;
|
||||
public FreshRSS_Entry $entry;
|
||||
public ?FreshRSS_Feed $feed;
|
||||
/** @var array<FreshRSS_Feed> */
|
||||
public FreshRSS_Feed $feed;
|
||||
/** @var array<int,FreshRSS_Feed> */
|
||||
public array $feeds;
|
||||
public int $nbUnreadTags;
|
||||
/** @var array<FreshRSS_Tag> */
|
||||
/** @var array<int,FreshRSS_Tag> */
|
||||
public array $tags;
|
||||
/** @var array<int,array{'id':int,'name':string,'id_entry':string,'checked':bool}> */
|
||||
public array $tagsForEntry;
|
||||
|
@ -100,6 +100,8 @@ class FreshRSS_View extends Minz_View {
|
|||
public int $nbPage;
|
||||
|
||||
// RSS view
|
||||
public FreshRSS_UserQuery $userQuery;
|
||||
public string $html_url = '';
|
||||
public string $rss_title = '';
|
||||
public string $rss_url = '';
|
||||
public string $rss_base = '';
|
||||
|
|
|
@ -3,11 +3,11 @@ declare(strict_types=1);
|
|||
|
||||
final class FreshRSS_ViewJavascript extends FreshRSS_View {
|
||||
|
||||
/** @var array<FreshRSS_Category> */
|
||||
/** @var array<int,FreshRSS_Category> */
|
||||
public array $categories;
|
||||
/** @var array<FreshRSS_Feed> */
|
||||
/** @var array<int,FreshRSS_Feed> */
|
||||
public array $feeds;
|
||||
/** @var array<FreshRSS_Tag> */
|
||||
/** @var array<int,FreshRSS_Tag> */
|
||||
public array $tags;
|
||||
|
||||
public string $nonce;
|
||||
|
|
|
@ -3,10 +3,10 @@ declare(strict_types=1);
|
|||
|
||||
final class FreshRSS_ViewStats extends FreshRSS_View {
|
||||
|
||||
/** @var array<FreshRSS_Category> */
|
||||
/** @var array<int,FreshRSS_Category> */
|
||||
public array $categories;
|
||||
public ?FreshRSS_Feed $feed;
|
||||
/** @var array<FreshRSS_Feed> */
|
||||
public FreshRSS_Feed $feed;
|
||||
/** @var array<int,FreshRSS_Feed> */
|
||||
public array $feeds;
|
||||
public bool $displaySlider = false;
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ class FreshRSS_Export_Service {
|
|||
$view = new FreshRSS_View();
|
||||
$view->categories = $this->category_dao->listCategories(true) ?: [];
|
||||
|
||||
$feed = FreshRSS_CategoryDAO::findFeed($view->categories, $feed_id);
|
||||
$feed = FreshRSS_Category::findFeed($view->categories, $feed_id);
|
||||
if ($feed === null) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -107,7 +107,8 @@ final class FreshRSS_dotpath_Util
|
|||
$view = new FreshRSS_View();
|
||||
$view->_path('index/rss.phtml');
|
||||
$view->internal_rendering = true;
|
||||
$view->rss_url = $feedSourceUrl;
|
||||
$view->rss_url = htmlspecialchars($feedSourceUrl, ENT_COMPAT, 'UTF-8');
|
||||
$view->html_url = $view->rss_url;
|
||||
$view->entries = [];
|
||||
|
||||
$view->rss_title = isset($dotPaths['feedTitle'])
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (pro pokročilé uživatele s HTTPS)',
|
||||
'none' => 'Žádný (nebezpečné)',
|
||||
'title' => 'Ověřování',
|
||||
'token' => 'Ověřovací token',
|
||||
'token_help' => 'Umožňuje přístup k výstupu RSS výchozího uživatele bez ověřování:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Metoda ověřování',
|
||||
'unsafe_autologin' => 'Povolit nebezpečné automatické přihlášení pomocí formátu: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Zobrazit podle kanálu',
|
||||
'order' => 'Seřadit podle data',
|
||||
'search' => 'Výraz',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Stav',
|
||||
'tags' => 'Zobrazit podle štítku',
|
||||
'type' => 'Typ',
|
||||
),
|
||||
'get_all' => 'Zobrazit všechny články',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Zobrazit kategorii „%s“',
|
||||
'get_favorite' => 'Zobrazit oblíbené články',
|
||||
'get_feed' => 'Zobrazit kanál „%s“',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Název',
|
||||
'no_filter' => 'Žádný filtr',
|
||||
'number' => 'Dotaz č. %d',
|
||||
'order_asc' => 'Zobrazit nejdříve nejstarší články',
|
||||
'order_desc' => 'Zobrazit nejdříve nejnovější články',
|
||||
'search' => 'Hledat „%s“',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Zobrazit všechny články',
|
||||
'state_1' => 'Zobrazit přečtené články',
|
||||
'state_2' => 'Zobrazit nepřečtené články',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (HTTPS für erfahrene Benutzer)',
|
||||
'none' => 'Keine (gefährlich)',
|
||||
'title' => 'Authentifizierung',
|
||||
'token' => 'Authentifizierungs-Token',
|
||||
'token_help' => 'Erlaubt den Zugriff auf die RSS-Ausgabe des Standardbenutzers ohne Authentifizierung.',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Authentifizierungsmethode',
|
||||
'unsafe_autologin' => 'Erlaube unsicheres automatisches Anmelden mit folgendem Format: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Nach Feed filtern',
|
||||
'order' => 'Nach Datum sortieren',
|
||||
'search' => 'Suchbegriff',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Eigenschaft',
|
||||
'tags' => 'Nach Labels filtern',
|
||||
'type' => 'Filter-Typ',
|
||||
),
|
||||
'get_all' => 'Alle Artikel anzeigen',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Kategorie „%s“ anzeigen',
|
||||
'get_favorite' => 'Lieblingsartikel anzeigen',
|
||||
'get_feed' => 'Feed „%s“ anzeigen',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Name', // IGNORE
|
||||
'no_filter' => 'Kein Filter',
|
||||
'number' => 'Abfrage Nr. %d',
|
||||
'order_asc' => 'Älteste Artikel zuerst anzeigen',
|
||||
'order_desc' => 'Neueste Artikel zuerst anzeigen',
|
||||
'search' => 'Suche nach „%s“',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Alle Artikel anzeigen',
|
||||
'state_1' => 'Gelesene Artikel anzeigen',
|
||||
'state_2' => 'Ungelesene Artikel anzeigen',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (για έμπειρους χρήστες με )',
|
||||
'none' => 'Καμία (ριψοκίνδυνο)',
|
||||
'title' => 'Πιστοποίηση',
|
||||
'token' => 'Διακριτικό Πιστοποίησης (token)',
|
||||
'token_help' => 'Επιτρέπει την πρόσβαση στα RSS αποτελέσματα του προεπιλεγμένου χρήστη χωρίς έλεγχο ταυτότητας:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Μέθοδος Πιστοποίησης',
|
||||
'unsafe_autologin' => 'Επιτρέψτε την μη ασφαλή αυτόματη σύνδεση με την χρήση της μορφής: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Display by feed', // TODO
|
||||
'order' => 'Sort by date', // TODO
|
||||
'search' => 'Expression', // TODO
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'State', // TODO
|
||||
'tags' => 'Display by label', // TODO
|
||||
'type' => 'Type', // TODO
|
||||
),
|
||||
'get_all' => 'Display all articles', // TODO
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Display “%s” category', // TODO
|
||||
'get_favorite' => 'Display favourite articles', // TODO
|
||||
'get_feed' => 'Display “%s” feed', // TODO
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Name', // TODO
|
||||
'no_filter' => 'No filter', // TODO
|
||||
'number' => 'Query n°%d', // TODO
|
||||
'order_asc' => 'Display oldest articles first', // TODO
|
||||
'order_desc' => 'Display newest articles first', // TODO
|
||||
'search' => 'Search for “%s”', // TODO
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Display all articles', // TODO
|
||||
'state_1' => 'Display read articles', // TODO
|
||||
'state_2' => 'Display unread articles', // TODO
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (for advanced users with HTTPS)', // IGNORE
|
||||
'none' => 'None (dangerous)', // IGNORE
|
||||
'title' => 'Authentication', // IGNORE
|
||||
'token' => 'Authentication token', // IGNORE
|
||||
'token_help' => 'Allows access to RSS output of the default user without authentication:', // IGNORE
|
||||
'token' => 'Master authentication token', // IGNORE
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // IGNORE
|
||||
'type' => 'Authentication method', // IGNORE
|
||||
'unsafe_autologin' => 'Allow unsafe automatic login using the format: ', // IGNORE
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Display by feed', // IGNORE
|
||||
'order' => 'Sort by date', // IGNORE
|
||||
'search' => 'Expression', // IGNORE
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // IGNORE
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // IGNORE
|
||||
'state' => 'State', // IGNORE
|
||||
'tags' => 'Display by label', // IGNORE
|
||||
'type' => 'Type', // IGNORE
|
||||
),
|
||||
'get_all' => 'Display all articles', // IGNORE
|
||||
'get_all_labels' => 'Display articles with any label', // IGNORE
|
||||
'get_category' => 'Display “%s” category', // IGNORE
|
||||
'get_favorite' => 'Display favorite articles',
|
||||
'get_feed' => 'Display “%s” feed', // IGNORE
|
||||
'get_important' => 'Display articles from important feeds', // IGNORE
|
||||
'get_label' => 'Display articles with “%s” label', // IGNORE
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // IGNORE
|
||||
'name' => 'Name', // IGNORE
|
||||
'no_filter' => 'No filter', // IGNORE
|
||||
'number' => 'Query n°%d', // IGNORE
|
||||
'order_asc' => 'Display oldest articles first', // IGNORE
|
||||
'order_desc' => 'Display newest articles first', // IGNORE
|
||||
'search' => 'Search for “%s”', // IGNORE
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // IGNORE
|
||||
'help' => 'Give this link if you want to share this query with anyone', // IGNORE
|
||||
'html' => 'Shareable link to the HTML page', // IGNORE
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // IGNORE
|
||||
'rss' => 'Shareable link to the RSS feed', // IGNORE
|
||||
),
|
||||
'state_0' => 'Display all articles', // IGNORE
|
||||
'state_1' => 'Display read articles', // IGNORE
|
||||
'state_2' => 'Display unread articles', // IGNORE
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (for advanced users with HTTPS)',
|
||||
'none' => 'None (dangerous)',
|
||||
'title' => 'Authentication',
|
||||
'token' => 'Authentication token',
|
||||
'token_help' => 'Allows access to RSS output of the default user without authentication:',
|
||||
'token' => 'Master authentication token',
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:',
|
||||
'type' => 'Authentication method',
|
||||
'unsafe_autologin' => 'Allow unsafe automatic login using the format: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Display by feed',
|
||||
'order' => 'Sort by date',
|
||||
'search' => 'Expression',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds',
|
||||
'shareRss' => 'Enable sharing by HTML & RSS',
|
||||
'state' => 'State',
|
||||
'tags' => 'Display by label',
|
||||
'type' => 'Type',
|
||||
),
|
||||
'get_all' => 'Display all articles',
|
||||
'get_all_labels' => 'Display articles with any label',
|
||||
'get_category' => 'Display “%s” category',
|
||||
'get_favorite' => 'Display favourite articles',
|
||||
'get_feed' => 'Display “%s” feed',
|
||||
'get_important' => 'Display articles from important feeds',
|
||||
'get_label' => 'Display articles with “%s” label',
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.',
|
||||
'name' => 'Name',
|
||||
'no_filter' => 'No filter',
|
||||
'number' => 'Query n°%d',
|
||||
'order_asc' => 'Display oldest articles first',
|
||||
'order_desc' => 'Display newest articles first',
|
||||
'search' => 'Search for “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link',
|
||||
'help' => 'Give this link if you want to share this query with anyone',
|
||||
'html' => 'Shareable link to the HTML page',
|
||||
'opml' => 'Shareable link to the OPML list of feeds',
|
||||
'rss' => 'Shareable link to the RSS feed',
|
||||
),
|
||||
'state_0' => 'Display all articles',
|
||||
'state_1' => 'Display read articles',
|
||||
'state_2' => 'Display unread articles',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (para usuarios avanzados con HTTPS)',
|
||||
'none' => 'Ninguno (peligroso)',
|
||||
'title' => 'Identificación',
|
||||
'token' => 'Clave de identificación',
|
||||
'token_help' => 'Permite el acceso a la salida RSS del usuario por defecto sin necesidad de identificación:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Método de identificación',
|
||||
'unsafe_autologin' => 'Permite la identificación automática insegura usando el formato: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Mostrar por feed',
|
||||
'order' => 'Ordenar por fecha',
|
||||
'search' => 'Expresión',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Estado',
|
||||
'tags' => 'Mostrar por etiqueta',
|
||||
'type' => 'Tipo',
|
||||
),
|
||||
'get_all' => 'Mostrar todos los artículos',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Mostrar la categoría “%s”',
|
||||
'get_favorite' => 'Mostrar artículos favoritos',
|
||||
'get_feed' => 'Mostrar fuente “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Nombre',
|
||||
'no_filter' => 'Sin filtro',
|
||||
'number' => 'Consulta n° %d',
|
||||
'order_asc' => 'Mostrar primero los artículos más antiguos',
|
||||
'order_desc' => 'Mostrar primero los artículos más recientes',
|
||||
'search' => 'Buscar “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Mostrar todos los artículos',
|
||||
'state_1' => 'Mostrar artículos leídos',
|
||||
'state_2' => 'Mostrar artículos pendientes',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => ' HTTP (برای کاربران پیشرفته با HTTPS)',
|
||||
'none' => ' هیچ (خطرناک)',
|
||||
'title' => ' احراز هویت',
|
||||
'token' => ' نشانه احراز هویت',
|
||||
'token_help' => ' امکان دسترسی به خروجی RSS کاربر پیش فرض بدون احراز هویت را می دهد:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => ' روش احراز هویت',
|
||||
'unsafe_autologin' => ' اجازه ورود خودکار ناامن را با استفاده از قالب:',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => ' نمایش با فید',
|
||||
'order' => ' مرتب سازی بر اساس تاریخ',
|
||||
'search' => ' بیان',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => ' ایالت',
|
||||
'tags' => ' نمایش بر اساس برچسب',
|
||||
'type' => ' نوع',
|
||||
),
|
||||
'get_all' => ' نمایش همه مقالات',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => ' دسته «%s» را نمایش دهید',
|
||||
'get_favorite' => ' نمایش مقالات مورد علاقه',
|
||||
'get_feed' => ' فید "%s" را نمایش دهید',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => ' نام',
|
||||
'no_filter' => ' بدون فیلتر',
|
||||
'number' => ' پرس و جو n°%d',
|
||||
'order_asc' => ' ابتدا قدیمی ترین مقالات را نمایش دهید',
|
||||
'order_desc' => ' ابتدا جدیدترین مقالات را نمایش دهید',
|
||||
'search' => ' «%s» را جستجو کنید',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'نمایش همه مقالات',
|
||||
'state_1' => 'نمایش مقالات خوانده شده',
|
||||
'state_2' => 'نمایش مقالات خوانده نشده',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (pour utilisateurs avancés avec HTTPS)',
|
||||
'none' => 'Aucune (dangereux)',
|
||||
'title' => 'Authentification',
|
||||
'token' => 'Jeton d’identification',
|
||||
'token_help' => 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier :',
|
||||
'token' => 'Jeton d’identification maître',
|
||||
'token_help' => 'Permet d’accéder à toutes les sorties RSS de l’utilisateur et au rafraîchissement des flux sans besoin de s’authentifier :',
|
||||
'type' => 'Méthode d’authentification',
|
||||
'unsafe_autologin' => 'Autoriser les connexions automatiques non-sûres au format : ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Afficher par flux',
|
||||
'order' => 'Tri par date',
|
||||
'search' => 'Expression', // IGNORE
|
||||
'shareOpml' => 'Active le partage par OPML des catégories et flux correspondants',
|
||||
'shareRss' => 'Active le partage par HTML & RSS',
|
||||
'state' => 'État',
|
||||
'tags' => 'Afficher par étiquette',
|
||||
'type' => 'Type', // IGNORE
|
||||
),
|
||||
'get_all' => 'Afficher tous les articles',
|
||||
'get_all_labels' => 'Afficher les articles avec une étiquette',
|
||||
'get_category' => 'Afficher la catégorie <em>%s<em>',
|
||||
'get_favorite' => 'Afficher les articles favoris',
|
||||
'get_feed' => 'Afficher le flux <em>%s</em>',
|
||||
'get_important' => 'Afficher les articles des flux importants',
|
||||
'get_label' => 'Afficher les articles avec l’étiquette “%s”',
|
||||
'help' => 'Voir la <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation pour les filtres utilisateurs et repartage par HTML / RSS / OPML</a>.',
|
||||
'name' => 'Nom',
|
||||
'no_filter' => 'Aucun filtre appliqué',
|
||||
'number' => 'Filtre n°%d',
|
||||
'order_asc' => 'Afficher les articles les plus anciens en premier',
|
||||
'order_desc' => 'Afficher les articles les plus récents en premier',
|
||||
'search' => 'Recherche de « %s »',
|
||||
'share' => array(
|
||||
'_' => 'Partager ce filtre par lien',
|
||||
'help' => 'Donner ce lien pour partager le contenu du filtre avec d’autres personnes',
|
||||
'html' => 'Lien partageable de la page HTML',
|
||||
'opml' => 'Lien partageable de la liste des flux au format OPML',
|
||||
'rss' => 'Lien partageable du flux RSS',
|
||||
),
|
||||
'state_0' => 'Afficher tous les articles',
|
||||
'state_1' => 'Afficher les articles lus',
|
||||
'state_2' => 'Afficher les articles non lus',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (למשתמשים מתקדמים עם HTTPS)',
|
||||
'none' => 'ללא (מסוכן)',
|
||||
'title' => 'Authentication', // TODO
|
||||
'token' => 'מחרוזת אימות',
|
||||
'token_help' => 'Allows to access RSS output of the default user without authentication:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'שיטת אימות',
|
||||
'unsafe_autologin' => 'הרשאה להתחברות אוטומטית בפורמט: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Display by feed', // TODO
|
||||
'order' => 'Sort by date', // TODO
|
||||
'search' => 'Expression', // TODO
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'State', // TODO
|
||||
'tags' => 'Display by label', // TODO
|
||||
'type' => 'Type', // TODO
|
||||
),
|
||||
'get_all' => 'הצגת כל המאמרים',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'הצגת קטגוריה “%s”',
|
||||
'get_favorite' => 'הצגת מאמרים מועדפים',
|
||||
'get_feed' => 'הצגת הזנה %s',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Name', // TODO
|
||||
'no_filter' => 'ללא סינון',
|
||||
'number' => 'שאילתה מספר °%d',
|
||||
'order_asc' => 'הצגת מאמרים ישנים בראש',
|
||||
'order_desc' => 'הצגת מאמרים חדשים בראש',
|
||||
'search' => 'חיפוש “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'הצגת כל המאמרים',
|
||||
'state_1' => 'הצגת מאמרים שנקראו',
|
||||
'state_2' => 'הצגת מאמרים שלא נקראו',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (haladó felhasználóknak HTTPS-el)',
|
||||
'none' => 'nincs (veszélyes)',
|
||||
'title' => 'Hitelesítés',
|
||||
'token' => 'Hitelesítő token',
|
||||
'token_help' => 'Engedélyezi az alapértelmezett felhasználó RSS-ének olvasását hitelesítés nélkül:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Hitelesítési módszer',
|
||||
'unsafe_autologin' => 'Engedélyezze a nem biztonságos automata bejelentkezést a következő formátummal: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Rendezés hírforrás szerint',
|
||||
'order' => 'Rendezés dátum szerint',
|
||||
'search' => 'Kifejezés',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Státusz',
|
||||
'tags' => 'Rendezés címke szerint',
|
||||
'type' => 'Típus',
|
||||
),
|
||||
'get_all' => 'Minden cikk megjelenítése',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Listáz “%s” kategóriát',
|
||||
'get_favorite' => 'Kedvenc cikkek megjelenítése',
|
||||
'get_feed' => 'Listáz “%s” hírforrást',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Név',
|
||||
'no_filter' => 'Nincs szűrés',
|
||||
'number' => 'Lekérdezés %d',
|
||||
'order_asc' => 'Régebbi cikkek előre',
|
||||
'order_desc' => 'Újabb cikkek előre',
|
||||
'search' => 'Keresse a “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Minden cikk',
|
||||
'state_1' => 'Olvasott cikkek',
|
||||
'state_2' => 'Olvasatlan cikkek',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (untuk pengguna tingkat lanjut HTTPS)',
|
||||
'none' => 'None (dangerous)', // TODO
|
||||
'title' => 'Authentication', // TODO
|
||||
'token' => 'Authentication token', // TODO
|
||||
'token_help' => 'Memungkinkan akses ke output RSS dari pengguna default tanpa otentikasi:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Authentication method', // TODO
|
||||
'unsafe_autologin' => 'Izinkan login otomatis yang tidak aman menggunakan format: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Display by feed', // TODO
|
||||
'order' => 'Sort by date', // TODO
|
||||
'search' => 'Expression', // TODO
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'State', // TODO
|
||||
'tags' => 'Display by label', // TODO
|
||||
'type' => 'Type', // TODO
|
||||
),
|
||||
'get_all' => 'Display all articles', // TODO
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Display “%s” category', // TODO
|
||||
'get_favorite' => 'Display favorite articles',
|
||||
'get_favorite' => 'Display favourite articles', // TODO
|
||||
'get_feed' => 'Display “%s” feed', // TODO
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Name', // TODO
|
||||
'no_filter' => 'No filter', // TODO
|
||||
'number' => 'Query n°%d', // TODO
|
||||
'order_asc' => 'Display oldest articles first', // TODO
|
||||
'order_desc' => 'Display newest articles first', // TODO
|
||||
'search' => 'Search for “%s”', // TODO
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Display all articles', // TODO
|
||||
'state_1' => 'Display read articles', // TODO
|
||||
'state_2' => 'Display unread articles', // TODO
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (per gli utenti avanzati con HTTPS)',
|
||||
'none' => 'Nessuno (pericoloso)',
|
||||
'title' => 'Autenticazione',
|
||||
'token' => 'Token di autenticazione',
|
||||
'token_help' => 'Consenti accesso agli RSS dell utente predefinito senza autenticazione:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Metodo di autenticazione',
|
||||
'unsafe_autologin' => 'Consenti accesso automatico non sicuro usando il formato: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Mostra per feed',
|
||||
'order' => 'Ordina per data',
|
||||
'search' => 'Espressione',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Stato',
|
||||
'tags' => 'Mostra per tag', // DIRTY
|
||||
'type' => 'Tipo',
|
||||
),
|
||||
'get_all' => 'Mostra tutti gli articoli',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Mostra la categoria “%s” ',
|
||||
'get_favorite' => 'Mostra articoli preferiti',
|
||||
'get_feed' => 'Mostra feed “%s” ',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Nome',
|
||||
'no_filter' => 'Nessun filtro',
|
||||
'number' => 'Ricerca n°%d',
|
||||
'order_asc' => 'Mostra prima gli articoli più vecchi',
|
||||
'order_desc' => 'Mostra prima gli articoli più nuovi',
|
||||
'search' => 'Cerca per “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Mostra tutti gli articoli',
|
||||
'state_1' => 'Mostra gli articoli letti',
|
||||
'state_2' => 'Mostra gli articoli non letti',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (上級者はHTTPSでも)',
|
||||
'none' => 'なし (危険)',
|
||||
'title' => '認証',
|
||||
'token' => '認証トークン',
|
||||
'token_help' => 'ユーザーが承認無しで、RSSを出力できるようにします。:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => '認証メソッド',
|
||||
'unsafe_autologin' => '危険な自動ログインを有効にします',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'フィードごとに表示する',
|
||||
'order' => '日付ごとにソートする',
|
||||
'search' => '式',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => '状態',
|
||||
'tags' => 'タグごとに表示する',
|
||||
'type' => 'タイプ',
|
||||
),
|
||||
'get_all' => 'すべての著者を表示する',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => '“%s”カテゴリを表示する',
|
||||
'get_favorite' => 'お気に入りの著者を表示する',
|
||||
'get_feed' => '“%s”フィードを表示する',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => '名前',
|
||||
'no_filter' => 'フィルターはありません',
|
||||
'number' => 'クエリ n°%d',
|
||||
'order_asc' => '古い著者を最初に表示する',
|
||||
'order_desc' => '新しい著者を最初に表示する',
|
||||
'search' => '“%s”で検索する',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'すべての記事を表示する',
|
||||
'state_1' => '既読の記事を表示する',
|
||||
'state_2' => '未読の記事を表示する',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (HTTPS를 사용하는 고급 사용자용)',
|
||||
'none' => '사용하지 않음 (위험)',
|
||||
'title' => '인증',
|
||||
'token' => '인증 토큰',
|
||||
'token_help' => '기본 사용자의 RSS에 인증 없이 접근할 수 있도록 합니다:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => '인증',
|
||||
'unsafe_autologin' => '다음과 같은 안전하지 않은 방식의 로그인을 허가합니다: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => '피드별로 표시',
|
||||
'order' => '날짜순으로 정렬',
|
||||
'search' => '정규 표현식',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => '상태',
|
||||
'tags' => '태그별로 표시',
|
||||
'type' => '유형',
|
||||
),
|
||||
'get_all' => '모든 글 표시',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => '“%s” 카테고리 표시',
|
||||
'get_favorite' => '즐겨찾기에 등록된 글 표시',
|
||||
'get_feed' => '“%s” 피드 표시',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => '이름',
|
||||
'no_filter' => '필터가 없습니다',
|
||||
'number' => '쿼리 #%d',
|
||||
'order_asc' => '오래된 글 먼저 표시',
|
||||
'order_desc' => '최근 글 먼저 표시',
|
||||
'search' => '“%s”의 검색 결과',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => '모든 글 표시',
|
||||
'state_1' => '읽은 글 표시',
|
||||
'state_2' => '읽지 않은 글 표시',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (pieredzējušiem lietotājiem ar HTTPS)',
|
||||
'none' => 'Nav (bīstami)',
|
||||
'title' => 'Autentifikācija',
|
||||
'token' => 'Autentifikācijas žetons',
|
||||
'token_help' => 'Ļauj piekļūt noklusējuma lietotāja RSS izvadei bez autentifikācijas:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Autentifikācijas metode',
|
||||
'unsafe_autologin' => 'Atļaut nedrošu automātisku pieteikšanos, izmantojot formātu: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Rādīt pēc barotnes',
|
||||
'order' => 'Kārtot pēc datuma',
|
||||
'search' => 'Izteiksme',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Stāvoklis',
|
||||
'tags' => 'Rādīt pēc birkas',
|
||||
'type' => 'Veids',
|
||||
),
|
||||
'get_all' => 'Rādīt visus rakstus',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Rādīt kategoriju “%s”',
|
||||
'get_favorite' => 'Rādīt mīļākos rakstus',
|
||||
'get_feed' => 'Rādīt barotni “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Vārds',
|
||||
'no_filter' => 'Bez filtra',
|
||||
'number' => 'Pieprasījums nr. %d',
|
||||
'order_asc' => 'Vispirms rādīt vecākos rakstus',
|
||||
'order_desc' => 'Vispirms rādīt jaunākos rakstus',
|
||||
'search' => 'Meklēt “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Rādīt visus rakstus',
|
||||
'state_1' => 'Rādīt lasītos rakstus',
|
||||
'state_2' => 'Rādīt nelasītos rakstus',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (voor gevorderde gebruikers met HTTPS)',
|
||||
'none' => 'Geen (gevaarlijk)',
|
||||
'title' => 'Authenticatie',
|
||||
'token' => 'Authenticatie teken',
|
||||
'token_help' => 'Sta toegang toe tot de RSS uitvoer van de standaard gebruiker zonder authenticatie:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Authenticatie methode',
|
||||
'unsafe_autologin' => 'Sta onveilige automatische log in toe met het volgende formaat: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Weergeven op feed',
|
||||
'order' => 'Sorteren op datum',
|
||||
'search' => 'Expressie',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Status',
|
||||
'tags' => 'Weergeven op label',
|
||||
'type' => 'Type', // IGNORE
|
||||
),
|
||||
'get_all' => 'Toon alle artikelen',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Toon „%s” categorie',
|
||||
'get_favorite' => 'Toon favoriete artikelen',
|
||||
'get_feed' => 'Toon „%s” feed',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Naam',
|
||||
'no_filter' => 'Geen filter',
|
||||
'number' => 'Query n°%d', // IGNORE
|
||||
'order_asc' => 'Toon oudste artikelen eerst',
|
||||
'order_desc' => 'Toon nieuwste artikelen eerst',
|
||||
'search' => 'Zoek naar „%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Toon alle artikelen',
|
||||
'state_1' => 'Toon gelezen artikelen',
|
||||
'state_2' => 'Toon ongelezen artikelen',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (per utilizaires avançats amb HTTPS)',
|
||||
'none' => 'Cap (perilhós)',
|
||||
'title' => 'Autentificacion',
|
||||
'token' => 'Geton d’autentificacion',
|
||||
'token_help' => 'Permetre l’accès a la sortida RSS de l’utilizaire per defaut sens cap d’autentificacion :',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Mòde d’autentification',
|
||||
'unsafe_autologin' => 'Autorizar las connexions automaticas pas seguras al format : ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Afichatge per flux',
|
||||
'order' => 'Triar per data',
|
||||
'search' => 'Expression', // IGNORE
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Estat',
|
||||
'tags' => 'Afichatge per etiqueta',
|
||||
'type' => 'Tipe',
|
||||
),
|
||||
'get_all' => 'Mostrar totes los articles',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Mostrar la categoria « %s »',
|
||||
'get_favorite' => 'Mostrar los articles favorits',
|
||||
'get_feed' => 'Mostrar lo flux « %s »',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Nom',
|
||||
'no_filter' => 'Cap de filtre aplicat',
|
||||
'number' => 'Filtre n°%d',
|
||||
'order_asc' => 'Mostrar los articles mai ancians en primièr',
|
||||
'order_desc' => 'Mostrar los articles mai recents en primièr',
|
||||
'search' => 'Recèrca de « %s »',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Mostrar totes los articles',
|
||||
'state_1' => 'Mostrar los articles pas legits',
|
||||
'state_2' => 'Mostrar los articles pas legits',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (dla zaawansowanych użytkowników, z wykorzystaniem HTTPS)',
|
||||
'none' => 'Brak (niebezpieczna)',
|
||||
'title' => 'Uwierzytelnianie',
|
||||
'token' => 'Token uwierzytelniania',
|
||||
'token_help' => 'Pozwala na dostęp do treści RSS domyślnego użytkownika bez uwierzytelnienia:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Metoda uwierzytelniania',
|
||||
'unsafe_autologin' => 'Pozwól na niebezpieczne automatyczne logowanie następującym schematem: -> todo',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Według kanału',
|
||||
'order' => 'Sortowanie wg daty',
|
||||
'search' => 'Wyrażenie',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Stan',
|
||||
'tags' => 'Według tagu',
|
||||
'type' => 'Rodzaj',
|
||||
),
|
||||
'get_all' => 'Wyświetlenie wszystkich wiadomości',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Wyświetlenie kategorii “%s”',
|
||||
'get_favorite' => 'Wyświetlenie ulubionych wiadomości',
|
||||
'get_feed' => 'Wyświetlenie kanału “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Nazwa',
|
||||
'no_filter' => 'Brak filtrów',
|
||||
'number' => 'Zapytanie nr %d',
|
||||
'order_asc' => 'Wyświetl najpierw najstarsze wiadomości',
|
||||
'order_desc' => 'Wyświetl najpierw najnowsze wiadomości',
|
||||
'search' => 'Szukaj “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Wyświetl wszystkie wiadomości',
|
||||
'state_1' => 'Wyświetl przeczytane wiadomości',
|
||||
'state_2' => 'Wyświetl nieprzeczytane wiadomości',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (Para usuários avançados com HTTPS)',
|
||||
'none' => 'Nenhum (Perigoso)',
|
||||
'title' => 'Autenticação',
|
||||
'token' => 'Token de autenticação ',
|
||||
'token_help' => 'Permitir acesso a saída RSS para o usuário padrão sem autenticação',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Método de autenticação',
|
||||
'unsafe_autologin' => 'Permitir login automática insegura usando o seguinte formato: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Exibir por feed',
|
||||
'order' => 'Ordenar por data',
|
||||
'search' => 'Expressão',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Estado',
|
||||
'tags' => 'Exibir por tag', // DIRTY
|
||||
'type' => 'Tipo',
|
||||
),
|
||||
'get_all' => 'Mostrar todos os artigos',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Visualizar “%s” categoria',
|
||||
'get_favorite' => 'Visualizar artigos favoritos',
|
||||
'get_feed' => 'Visualizar “%s” feed',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Nome',
|
||||
'no_filter' => 'Sem filtro',
|
||||
'number' => 'Query n°%d', // IGNORE
|
||||
'order_asc' => 'Exibir artigos mais antigos primeiro',
|
||||
'order_desc' => 'Exibir artigos mais novos primeiro',
|
||||
'search' => 'Busca por “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Exibir todos os artigos',
|
||||
'state_1' => 'Exibir artigos lidos',
|
||||
'state_2' => 'Exibir artigos não lidos',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (для опытных пользователей с HTTPS)',
|
||||
'none' => 'Без аутентификации (небезопасно)',
|
||||
'title' => 'Аутентификации',
|
||||
'token' => 'Токен аутентификации',
|
||||
'token_help' => 'Разрешает доступ к RSS-лентам пользователя по умолчанию без аутентификации:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Способ аутентификации',
|
||||
'unsafe_autologin' => 'Разрешить небезопасный автоматический вход с использованием следующего формата: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Отображение по ленте',
|
||||
'order' => 'Сортировать по дате',
|
||||
'search' => 'Выражение',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Состояние',
|
||||
'tags' => 'Отображение по метке',
|
||||
'type' => 'Тип',
|
||||
),
|
||||
'get_all' => 'Показать все статьи',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Показать категорию “%s”',
|
||||
'get_favorite' => 'Показать избранные статьи',
|
||||
'get_feed' => 'Показать ленту “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Название',
|
||||
'no_filter' => 'Нет фильтров',
|
||||
'number' => 'Запрос №%d',
|
||||
'order_asc' => 'Показывать сначала старые статьи',
|
||||
'order_desc' => 'Показывать сначала новые статьи',
|
||||
'search' => 'Искать “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Показать все статьи',
|
||||
'state_1' => 'Показать прочитанные статьи',
|
||||
'state_2' => 'Показать непрочитанные статьи',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (pre pokročilých používateľov s HTTPS)',
|
||||
'none' => 'Žiadny (nebezpečné)',
|
||||
'title' => 'Prihlásenie',
|
||||
'token' => 'Token prihlásenia',
|
||||
'token_help' => 'Povoliť prístup k výstupu RSS prednastaveného používateľa bez prihlásenia:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Spôsob prihlásenia',
|
||||
'unsafe_autologin' => 'Povoliť nebezpečné automatické prihlásenie pomocou webového formulára: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Zobraziť podľa kanála',
|
||||
'order' => 'Zobraziť podľa dátumu',
|
||||
'search' => 'Výraz',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Štát',
|
||||
'tags' => 'Zobraziť podľa štítku',
|
||||
'type' => 'Typ',
|
||||
),
|
||||
'get_all' => 'Zobraziť všetky články',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => 'Zobraziť kategóriu “%s”',
|
||||
'get_favorite' => 'Zobraziť obľúbené články',
|
||||
'get_feed' => 'Zobraziť kanál “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'Meno',
|
||||
'no_filter' => 'Žiadny filter',
|
||||
'number' => 'Dopyt číslo %d',
|
||||
'order_asc' => 'Zobraziť staršie články hore',
|
||||
'order_desc' => 'Zobraziť novšie články hore',
|
||||
'search' => 'Vyhľadáva sa: “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Zobraziť všetky články',
|
||||
'state_1' => 'Zobraziť prečítané články',
|
||||
'state_2' => 'Zobraziť neprečítané články',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP (ileri kullanıcılar için, HTTPS)',
|
||||
'none' => 'Hiçbiri (tehlikeli)',
|
||||
'title' => 'Kimlik doğrulama',
|
||||
'token' => 'Kimlik doğrulama işareti',
|
||||
'token_help' => 'Kimlik doğrulama olmaksızın öntanımlı kullanıcının RSS çıktısına erişime izin ver:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => 'Kimlik doğrulama yöntemi',
|
||||
'unsafe_autologin' => 'Güvensiz otomatik girişe izin ver: ',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => 'Akışa göre göster',
|
||||
'order' => 'Tarihe göre göster',
|
||||
'search' => 'İfade',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => 'Durum',
|
||||
'tags' => 'Etikete göre göster',
|
||||
'type' => 'Tür',
|
||||
),
|
||||
'get_all' => 'Tüm makaleleri göster',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => '“%s” kategorisini göster',
|
||||
'get_favorite' => 'Favori makaleleri göster',
|
||||
'get_feed' => '“%s” akışını göster',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => 'İsim',
|
||||
'no_filter' => 'Filtre yok',
|
||||
'number' => 'Sorgu n°%d',
|
||||
'order_asc' => 'Önce eski makaleleri göster',
|
||||
'order_desc' => 'Önce yeni makaleleri göster',
|
||||
'search' => '“%s” için arama',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => 'Tüm makaleleri göster',
|
||||
'state_1' => 'Okunmuş makaleleri göster',
|
||||
'state_2' => 'Okunmamış makaleleri göster',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP(面向启用 HTTPS 的高级用户)',
|
||||
'none' => '无(危险)',
|
||||
'title' => '认证',
|
||||
'token' => '认证口令',
|
||||
'token_help' => '用于不经认证访问默认用户的 RSS 输出:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => '认证方式',
|
||||
'unsafe_autologin' => '允许不安全的自动登陆方式:',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => '按订阅源显示',
|
||||
'order' => '按日期排序',
|
||||
'search' => '表达式',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => '状态',
|
||||
'tags' => '按标签显示',
|
||||
'type' => '类型',
|
||||
),
|
||||
'get_all' => '显示所有文章',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => '显示分类 “%s”',
|
||||
'get_favorite' => '显示收藏文章',
|
||||
'get_feed' => '显示订阅源 “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => '名称',
|
||||
'no_filter' => '无过滤器',
|
||||
'number' => '查询 n°%d',
|
||||
'order_asc' => '由旧至新显示文章',
|
||||
'order_desc' => '由新至旧显示文章',
|
||||
'search' => '搜索 “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => '显示所有文章',
|
||||
'state_1' => '显示已读文章',
|
||||
'state_2' => '显示未读文章',
|
||||
|
|
|
@ -19,8 +19,8 @@ return array(
|
|||
'http' => 'HTTP(面向啟用 HTTPS 的高級用戶)',
|
||||
'none' => '無認證(危險)',
|
||||
'title' => '認證',
|
||||
'token' => '認證口令',
|
||||
'token_help' => '用於不經認證訪問預設使用者的 RSS 輸出:',
|
||||
'token' => 'Master authentication token', // TODO
|
||||
'token_help' => 'Allows access to all RSS outputs of the user as well as refreshing feeds without authentication:', // TODO
|
||||
'type' => '認證方式',
|
||||
'unsafe_autologin' => '允許不安全的自動登入方式:',
|
||||
),
|
||||
|
|
|
@ -120,20 +120,33 @@ return array(
|
|||
'feeds' => '按訂閱源顯示',
|
||||
'order' => '按日期排序',
|
||||
'search' => '表達式',
|
||||
'shareOpml' => 'Enable sharing by OPML of corresponding categories and feeds', // TODO
|
||||
'shareRss' => 'Enable sharing by HTML & RSS', // TODO
|
||||
'state' => '狀態',
|
||||
'tags' => '按標簽顯示',
|
||||
'type' => '類型',
|
||||
),
|
||||
'get_all' => '顯示所有文章',
|
||||
'get_all_labels' => 'Display articles with any label', // TODO
|
||||
'get_category' => '顯示分類 “%s”',
|
||||
'get_favorite' => '顯示收藏文章',
|
||||
'get_feed' => '顯示訂閱源 “%s”',
|
||||
'get_important' => 'Display articles from important feeds', // TODO
|
||||
'get_label' => 'Display articles with “%s” label', // TODO
|
||||
'help' => 'See the <a href="https://freshrss.github.io/FreshRSS/en/users/user_queries.html" target="_blank">documentation for user queries and resharing by HTML / RSS / OPML</a>.', // TODO
|
||||
'name' => '名稱',
|
||||
'no_filter' => '無過濾器',
|
||||
'number' => '查詢 n°%d',
|
||||
'order_asc' => '由舊至新顯示文章',
|
||||
'order_desc' => '由新至舊顯示文章',
|
||||
'search' => '搜尋 “%s”',
|
||||
'share' => array(
|
||||
'_' => 'Share this query by link', // TODO
|
||||
'help' => 'Give this link if you want to share this query with anyone', // TODO
|
||||
'html' => 'Shareable link to the HTML page', // TODO
|
||||
'opml' => 'Shareable link to the OPML list of feeds', // TODO
|
||||
'rss' => 'Shareable link to the RSS feed', // TODO
|
||||
),
|
||||
'state_0' => '顯示所有文章',
|
||||
'state_1' => '顯示已讀文章',
|
||||
'state_2' => '顯示未讀文章',
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
?>
|
||||
<header class="header">
|
||||
<div class="item title">
|
||||
<a href="<?= _url('index', 'index') ?>">
|
||||
<a href="<?= Minz_Url::display(['c' => 'index', 'a' => 'index'], 'html', 'root') ?>">
|
||||
<?php if (FreshRSS_Context::systemConf()->logo_html == '') { ?>
|
||||
<img class="logo" src="<?= _i('FreshRSS-logo', FreshRSS_Themes::ICON_URL) ?>" alt="FreshRSS" loading="lazy" />
|
||||
<?php
|
||||
|
@ -16,32 +17,29 @@
|
|||
|
||||
<div class="item search">
|
||||
<?php if (FreshRSS_Auth::hasAccess() || FreshRSS_Context::systemConf()->allow_anonymous) { ?>
|
||||
<form action="<?= _url('index', 'index') ?>" method="get">
|
||||
<form action="<?= $this->html_url ?>" method="get">
|
||||
<div class="stick">
|
||||
<?php if (Minz_Request::controllerName() === 'index'): ?>
|
||||
<?php if (in_array(Minz_Request::actionName(), ['normal', 'global', 'reader'], true)) { ?>
|
||||
<input type="hidden" name="a" value="<?= Minz_Request::actionName() ?>" />
|
||||
<?php } if (Minz_Request::paramString('get') !== '') { ?>
|
||||
<input type="hidden" name="get" value="<?= FreshRSS_Context::currentGet() ?>" />
|
||||
<?php } if (Minz_Request::paramInt('state') !== 0) { ?>
|
||||
<input type="hidden" name="state" value="<?= Minz_Request::paramInt('state') ?>" />
|
||||
<?php } ?>
|
||||
<?php endif; ?>
|
||||
<?php if (Minz_Request::paramString('user') !== '') { ?>
|
||||
<input type="hidden" name="user" value="<?= Minz_User::name() ?>" />
|
||||
<?php } if (ctype_alnum(Minz_Request::paramString('t'))) { ?>
|
||||
<input type="hidden" name="t" value="<?= Minz_Request::paramString('t') ?>" />
|
||||
<?php } if (ctype_upper(Minz_Request::paramString('order'))) { ?>
|
||||
<input type="hidden" name="order" value="<?= FreshRSS_Context::$order ?>" />
|
||||
<?php } if (ctype_lower(Minz_Request::paramString('f'))) { ?>
|
||||
<input type="hidden" name="f" value="<?= Minz_Request::paramString('f') ?>" />
|
||||
<?php } ?>
|
||||
<input type="search" name="search" id="search"
|
||||
value="<?= htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search->getRawInput(), ENT_QUOTES), ENT_COMPAT, 'UTF-8') ?>"
|
||||
value="<?= htmlspecialchars(htmlspecialchars_decode(Minz_Request::paramString('search'), ENT_QUOTES), ENT_COMPAT, 'UTF-8') ?>"
|
||||
placeholder="<?= _t('gen.menu.search') ?>" />
|
||||
|
||||
<?php $param_a = Minz_Request::actionName(); ?>
|
||||
<?php if (in_array($param_a, ['normal', 'global', 'reader'], true)) { ?>
|
||||
<input type="hidden" name="a" value="<?= $param_a ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<?php $get = Minz_Request::paramString('get'); ?>
|
||||
<?php if ($get !== '') { ?>
|
||||
<input type="hidden" name="get" value="<?= $get ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<?php $order = Minz_Request::paramString('order'); ?>
|
||||
<?php if ($order !== '') { ?>
|
||||
<input type="hidden" name="order" value="<?= $order ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<?php $state = Minz_Request::paramString('state'); ?>
|
||||
<?php if ($state !== '') { ?>
|
||||
<input type="hidden" name="state" value="<?= $state ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<button class="btn" type="submit"><?= _i('search') ?></button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -120,7 +118,7 @@
|
|||
</nav>
|
||||
<?php } elseif (FreshRSS_Auth::accessNeedsAction()) { ?>
|
||||
<div class="item configure">
|
||||
<a class="signin" href="<?= _url('auth', 'login') ?>"><?= _i('login') ?><?= _t('gen.auth.login') ?></a>
|
||||
<a class="signin" href="<?= Minz_Url::display(['c' => 'auth', 'a' => 'login'], 'html', 'root') ?>"><?= _i('login') ?><?= _t('gen.auth.login') ?></a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</header>
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
FreshRSS::preLayout();
|
||||
$class = '';
|
||||
if (_t('gen.dir') === 'rtl') {
|
||||
echo ' dir="rtl"';
|
||||
$class = 'rtl ';
|
||||
}
|
||||
if (FreshRSS_Context::userConf()->darkMode !== 'no') {
|
||||
$class .= 'darkMode_' . FreshRSS_Context::userConf()->darkMode;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>"<?php
|
||||
$class = '';
|
||||
if (_t('gen.dir') === 'rtl') {
|
||||
echo ' dir="rtl"';
|
||||
$class = 'rtl ';
|
||||
}
|
||||
?> class="<?= $class ?><?= (FreshRSS_Context::userConf()->darkMode === 'no') ? '' : 'darkMode_' . FreshRSS_Context::userConf()->darkMode ?>">
|
||||
<html lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>" class="<?= $class ?>">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
|
|
|
@ -41,26 +41,15 @@
|
|||
<li class="item">
|
||||
<span>
|
||||
<form action="<?= _url('index', 'index') ?>" method="get">
|
||||
<?php $param_a = Minz_Request::actionName(); ?>
|
||||
<?php if (in_array($param_a, ['normal', 'global', 'reader'], true)) { ?>
|
||||
<input type="hidden" name="a" value="<?= $param_a ?>" />
|
||||
<?php if (in_array(Minz_Request::actionName(), ['normal', 'global', 'reader'], true)) { ?>
|
||||
<input type="hidden" name="a" value="<?= Minz_Request::actionName() ?>" />
|
||||
<?php } if (Minz_Request::paramString('get') !== '') { ?>
|
||||
<input type="hidden" name="get" value="<?= FreshRSS_Context::currentGet() ?>" />
|
||||
<?php } if (ctype_upper(Minz_Request::paramString('order'))) { ?>
|
||||
<input type="hidden" name="order" value="<?= FreshRSS_Context::$order ?>" />
|
||||
<?php } if (Minz_Request::paramInt('state') !== 0) { ?>
|
||||
<input type="hidden" name="state" value="<?= FreshRSS_Context::$state ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<?php $get = Minz_Request::paramString('get'); ?>
|
||||
<?php if ($get !== '') { ?>
|
||||
<input type="hidden" name="get" value="<?= $get ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<?php $order = Minz_Request::paramString('order'); ?>
|
||||
<?php if ($order !== '') { ?>
|
||||
<input type="hidden" name="order" value="<?= $order ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<?php $state = Minz_Request::paramString('state'); ?>
|
||||
<?php if ($state !== '') { ?>
|
||||
<input type="hidden" name="state" value="<?= $state ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<div class="stick search">
|
||||
<input type="search" name="search"
|
||||
value="<?= htmlspecialchars(htmlspecialchars_decode(FreshRSS_Context::$search->getRawInput(), ENT_QUOTES), ENT_COMPAT, 'UTF-8'); ?>"
|
||||
|
@ -89,7 +78,7 @@
|
|||
<?php if (!empty($raw_query['url'])): ?>
|
||||
<a href="<?= $raw_query['url'] ?>"><?= $raw_query['name'] ?? $raw_query['url'] ?></a>
|
||||
<?php else: ?>
|
||||
<?php $query = new FreshRSS_UserQuery($raw_query); ?>
|
||||
<?php $query = new FreshRSS_UserQuery($raw_query, FreshRSS_Context::categories(), FreshRSS_Context::labels()); ?>
|
||||
<a href="<?= $query->getUrl() ?>"><?= $query->getName() ?></a>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
|
@ -210,20 +199,6 @@
|
|||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
$url_output['a'] = 'rss';
|
||||
if (FreshRSS_Context::userConf()->token) {
|
||||
$url_output['params']['user'] = Minz_User::name();
|
||||
$url_output['params']['token'] = FreshRSS_Context::userConf()->token;
|
||||
}
|
||||
if (FreshRSS_Context::userConf()->since_hours_posts_per_rss) {
|
||||
$url_output['params']['hours'] = FreshRSS_Context::userConf()->since_hours_posts_per_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>
|
||||
|
||||
<?php $nav_menu_hooks = Minz_ExtensionManager::callHookString('nav_menu'); ?>
|
||||
|
|
|
@ -2,17 +2,27 @@
|
|||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
FreshRSS::preLayout();
|
||||
$class = '';
|
||||
if (_t('gen.dir') === 'rtl') {
|
||||
echo ' dir="rtl"';
|
||||
$class = 'rtl ';
|
||||
}
|
||||
if (FreshRSS_Context::userConf()->darkMode !== 'no') {
|
||||
$class .= 'darkMode_' . FreshRSS_Context::userConf()->darkMode;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>">
|
||||
<html lang="<?= FreshRSS_Context::userConf()->language ?>" xml:lang="<?= FreshRSS_Context::userConf()->language ?>" class="<?= $class ?>">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="initial-scale=1.0" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<?= FreshRSS_View::metaThemeColor() ?>
|
||||
<?= FreshRSS_View::headStyle() ?>
|
||||
<script id="jsonVars" type="application/json">
|
||||
<?php $this->renderHelper('javascript_vars'); ?>
|
||||
</script>
|
||||
<?= FreshRSS_View::headScript() ?>
|
||||
<link rel="manifest" href="<?= Minz_Url::display('/themes/manifest.json') ?>" />
|
||||
<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') ?>" />
|
||||
|
@ -20,9 +30,15 @@
|
|||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<meta name="apple-mobile-web-app-title" content="<?= FreshRSS_Context::systemConf()->title ?>">
|
||||
<meta name="msapplication-TileColor" content="#FFF" />
|
||||
<meta name="theme-color" content="#FFF" />
|
||||
<?php if (!FreshRSS_Context::systemConf()->allow_referrer) { ?>
|
||||
<meta name="referrer" content="never" />
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
<?php } ?>
|
||||
<?= FreshRSS_View::headTitle() ?>
|
||||
<?php if ($this->rss_url != ''): ?>
|
||||
<link rel="alternate" type="application/rss+xml" title="<?= $this->rss_title ?>" href="<?= $this->rss_url ?>" />
|
||||
<?php endif; ?>
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -30,7 +46,7 @@
|
|||
<div class="app-layout app-layout-simple">
|
||||
<div class="header">
|
||||
<div class="item title">
|
||||
<a href="<?= _url('index', 'index') ?>">
|
||||
<a href="<?= Minz_Url::display(['c' => 'index', 'a' => 'index'], 'html', 'root') ?>">
|
||||
<?php if (FreshRSS_Context::systemConf()->logo_html == '') { ?>
|
||||
<img class="logo" src="<?= _i('FreshRSS-logo', FreshRSS_Themes::ICON_URL) ?>" alt="FreshRSS" loading="lazy" />
|
||||
<?php
|
||||
|
@ -43,14 +59,20 @@
|
|||
|
||||
<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') ?>
|
||||
<?php if (FreshRSS_Auth::accessNeedsAction()): ?>
|
||||
<div class="item configure">
|
||||
<?php if (FreshRSS_Auth::hasAccess()): ?>
|
||||
<a class="signout" href="<?= Minz_Url::display(['c' => 'auth', 'a' => 'logout'], 'html', 'root') ?>">
|
||||
<?= _i('logout') ?><?= _t('gen.auth.logout') ?>
|
||||
(<?= htmlspecialchars(Minz_User::name() ?? '', ENT_NOQUOTES, 'UTF-8') ?>)
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<a class="signin" href="<?= Minz_Url::display(['c' => 'auth', 'a' => 'login'], 'html', 'root') ?>">
|
||||
<?= _i('login') ?><?= _t('gen.auth.login') ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php $this->render(); ?>
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
<div class="box-title">
|
||||
<a class="configure open-slider" href="<?= _url('configure', 'query', 'id', '' . $key) ?>"><?= _i('configure') ?></a><h2><?= $query->getName() ?></h2>
|
||||
<input type="hidden" id="queries_<?= $key ?>_name" name="queries[<?= $key ?>][name]" value="<?= $query->getName() ?>"/>
|
||||
<input type="hidden" id="queries_<?= $key ?>_token" name="queries[<?= $key ?>][token]" value="<?= $query->getToken() ?>"/>
|
||||
<input type="hidden" id="queries_<?= $key ?>_shareRss" name="queries[<?= $key ?>][token]" value="<?= $query->shareRss() ?>"/>
|
||||
<input type="hidden" id="queries_<?= $key ?>_shareOpml" name="queries[<?= $key ?>][token]" value="<?= $query->shareOpml() ?>"/>
|
||||
<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="<?= urlencode($query->getSearch()->getRawInput()) ?>"/>
|
||||
<input type="hidden" id="queries_<?= $key ?>_state" name="queries[<?= $key ?>][state]" value="<?= $query->getState() ?>"/>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
?>
|
||||
<div class="post">
|
||||
<h2><?= $this->query->getName() ?></h2>
|
||||
|
||||
<div>
|
||||
<a href="<?= $this->query->getUrl() ?>"><?= _i('link') ?> <?= _t('gen.action.filter') ?></a>
|
||||
</div>
|
||||
|
@ -18,15 +17,53 @@
|
|||
<div class="form-group">
|
||||
<label class="group-name" for="name"><?= _t('conf.query.name') ?></label>
|
||||
<div class="group-controls">
|
||||
<input type="text" name="name" id="name" value="<?= $this->query->getName() ?>" />
|
||||
<input type="text" name="name" id="name" value="<?= $this->query->getName() ?>" />
|
||||
<input type="hidden" name="query[token]" id="query_token" value="<?= $this->query->getToken() ?>" />
|
||||
</div>
|
||||
</div>
|
||||
<legend><?= _t('conf.query.filter') ?></legend>
|
||||
|
||||
<legend><?= _t('conf.query.share') ?></legend>
|
||||
<div class="form-group">
|
||||
<div class="group-controls">
|
||||
<label class="checkbox" for="shareRss">
|
||||
<input type="checkbox" name="query[shareRss]" id="shareRss" value="1" <?= $this->query->shareRss() ? 'checked="checked"' : ''?> />
|
||||
<?= _t('conf.query.filter.shareRss') ?>
|
||||
</label>
|
||||
<?php if ($this->query->sharedUrlRss() !== ''): ?>
|
||||
<ul>
|
||||
<li><a href="<?= $this->query->sharedUrlHtml() ?>"><?= _i('link') ?> <?= _t('conf.query.share.html') ?></a></li>
|
||||
<li><a href="<?= $this->query->sharedUrlRss() ?>"><?= _i('link') ?> <?= _t('conf.query.share.rss') ?></a></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="group-controls">
|
||||
<label class="checkbox" for="shareOpml">
|
||||
<input type="checkbox" name="query[shareOpml]" id="shareOpml" value="1" <?= $this->query->shareOpml() && $this->query->safeForOpml() ? 'checked="checked"' : '' ?>
|
||||
<?= $this->query->safeForOpml() ? '' : 'disabled="disabled"' ?> />
|
||||
<?= _t('conf.query.filter.shareOpml') ?>
|
||||
</label>
|
||||
<?php if ($this->query->sharedUrlOpml() !== ''): ?>
|
||||
<ul>
|
||||
<li><a href="<?= $this->query->sharedUrlOpml() ?>"><?= _i('link') ?> <?= _t('conf.query.share.opml') ?></a></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<p class="help"><?= _i('help') ?> <?= _t('conf.query.share.help') ?></a></p>
|
||||
<p class="help"><?= _i('help') ?> <?= _t('conf.query.help') ?></a></p>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-actions">
|
||||
<div class="group-controls">
|
||||
<button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<legend><?= _t('conf.query.filter') ?></legend>
|
||||
<div class="form-group">
|
||||
<label class="group-name" for=""><?= _t('conf.query.filter.search') ?></label>
|
||||
<div class="group-controls">
|
||||
<input type="text" id="query_search" name="query[search]" value="<?= htmlspecialchars($this->query->getSearch()->getRawInput(), ENT_COMPAT, 'UTF-8') ?>"/>
|
||||
<p class="help"><?= _i('help') ?> <?= _t('gen.menu.search_help') ?></a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -58,22 +95,24 @@
|
|||
<label class="group-name" for="query_get"><?= _t('conf.query.filter.type') ?></label>
|
||||
<div class="group-controls">
|
||||
<select name="query[get]" id="query_get" size="10">
|
||||
<option value=""></option>
|
||||
<option value="s" <?= 's' === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= _t('conf.query.get_favorite') ?></option>
|
||||
<option value="a" <?= in_array($this->query->getGet(), ['', 'a'], true) ? 'selected="selected"' : '' ?>><?= _t('index.feed.title') ?></option>
|
||||
<option value="i" <?= 'i' === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= _t('index.menu.important') ?></option>
|
||||
<option value="s" <?= 's' === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= _t('index.feed.title_fav') ?></option>
|
||||
<option value="T" <?= 'T' === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= _t('index.menu.tags') ?></option>
|
||||
<optgroup label="<?= _t('conf.query.filter.tags') ?>">
|
||||
<?php foreach ($this->tags as $tag): ?>
|
||||
<option value="t_<?= $tag->id() ?>" <?= "t_{$tag->id()}" === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= $tag->name() ?></option>
|
||||
<?php endforeach?>
|
||||
</optgroup>
|
||||
<optgroup label="<?= _t('conf.query.filter.categories') ?>">
|
||||
<?php foreach ($this->categories as $category): ?>
|
||||
<option value="c_<?= $category->id() ?>" <?= "c_{$category->id()}" === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= $category->name() ?></option>
|
||||
<?php endforeach?>
|
||||
</optgroup>
|
||||
<optgroup label="<?= _t('conf.query.filter.feeds') ?>">
|
||||
<?php foreach ($this->feeds as $feed): ?>
|
||||
<option value="f_<?= $feed->id() ?>" <?= "f_{$feed->id()}" === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= $feed->name() ?></option>
|
||||
<?php endforeach?>
|
||||
</optgroup>
|
||||
<optgroup label="<?= _t('conf.query.filter.tags') ?>">
|
||||
<?php foreach ($this->tags as $tag): ?>
|
||||
<option value="t_<?= $tag->id() ?>" <?= "t_{$tag->id()}" === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= $tag->name() ?></option>
|
||||
<?php endforeach?>
|
||||
<?php foreach ($this->feeds as $feed): ?>
|
||||
<option value="f_<?= $feed->id() ?>" <?= "f_{$feed->id()}" === $this->query->getGet() ? 'selected="selected"' : '' ?>><?= $feed->name() ?></option>
|
||||
<?php endforeach?>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -83,8 +122,8 @@
|
|||
<div class="group-controls">
|
||||
<select name="query[order]" id="query_order">
|
||||
<option value=""></option>
|
||||
<option value="ASC" <?= 'ASC' === $this->query->getOrder() ? 'selected="selected"' : '' ?>><?= _t('conf.query.order_asc') ?></option>
|
||||
<option value="DESC" <?= 'DESC' === $this->query->getOrder() ? 'selected="selected"' : '' ?>><?= _t('conf.query.order_desc') ?></option>
|
||||
<option value="ASC" <?= 'ASC' === $this->query->getOrder() ? 'selected="selected"' : '' ?>><?= _t('conf.query.order_asc') ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,7 +24,7 @@ foreach ($this->entries as $entry) {
|
|||
continue;
|
||||
}
|
||||
|
||||
$feed = $this->feed ?? FreshRSS_CategoryDAO::findFeed($this->categories, $entry->feedId());
|
||||
$feed = $this->feed ?? FreshRSS_Category::findFeed($this->categories, $entry->feedId());
|
||||
$entry->_feed($feed);
|
||||
|
||||
$article = $entry->toGReader('freshrss', $this->entryIdsTagNames['e_' . $entry->id()] ?? []);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
if ($this->feed === null) {
|
||||
throw new FreshRSS_Context_Exception('Feed not initialised!');
|
||||
}
|
||||
?>
|
||||
<div class="post" id="feed_update">
|
||||
<h1><?= $this->feed->name() ?></h1>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
?>
|
||||
<nav class="nav-pagination nav-list">
|
||||
<ul class="pagination">
|
||||
<?php if (FreshRSS_Context::$offset > 0): ?>
|
||||
<li class="item pager-first">
|
||||
<a href="<?= $this->userQuery->sharedUrlHtml() . '&nb=' . FreshRSS_Context::$number ?>">« <?= _t('conf.logs.pagination.first') ?></a>
|
||||
</li>
|
||||
<li class="item pager-previous">
|
||||
<a href="<?= $this->userQuery->sharedUrlHtml() . '&nb=' . FreshRSS_Context::$number .
|
||||
'&offset=' . max(0, FreshRSS_Context::$offset - FreshRSS_Context::$number) ?>">‹ <?= _t('conf.logs.pagination.previous') ?></a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li class="item pager-next">
|
||||
<a href="<?= $this->userQuery->sharedUrlHtml() . '&nb=' . FreshRSS_Context::$number .
|
||||
'&offset=' . (FreshRSS_Context::$offset + FreshRSS_Context::$number) ?>"><?= _t('conf.logs.pagination.next') ?> ›</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
$entry = $this->entry;
|
||||
$feed = $this->feed;
|
||||
?>
|
||||
<article class="flux_content" dir="auto">
|
||||
<div class="content <?= FreshRSS_Context::userConf()->content_width ?>">
|
||||
<header>
|
||||
<?php
|
||||
$favoriteUrl = ['c' => 'entry', 'a' => 'bookmark', 'params' => ['id' => $entry->id()]];
|
||||
if ($entry->isFavorite()) {
|
||||
$favoriteUrl['params']['is_favorite'] = 0;
|
||||
}
|
||||
$readUrl = ['c' => 'entry', 'a' => 'read', 'params' => ['id' => $entry->id()]];
|
||||
if ($entry->isRead()) {
|
||||
$readUrl['params']['is_read'] = 0;
|
||||
}
|
||||
?>
|
||||
<div class="article-header-topline">
|
||||
<?php if (FreshRSS_Auth::hasAccess()) { ?>
|
||||
<a class="read" href="<?= Minz_Url::display($readUrl) ?>" title="<?= _t('conf.shortcut.mark_read') ?>"><?= _i($entry->isRead() ? 'read' : 'unread') ?></a>
|
||||
<a class="bookmark" href="<?= Minz_Url::display($favoriteUrl) ?>" title="<?= _t('conf.shortcut.mark_favorite') ?>"><?= _i($entry->isFavorite() ? 'starred' : 'non-starred') ?></a>
|
||||
<?php } ?>
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 't') { ?>
|
||||
<a class="website" href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
<?php if (FreshRSS_Context::userConf()->show_favicons): ?>
|
||||
<img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php
|
||||
endif; ?><span><?= $feed->name() ?></span></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if (in_array(FreshRSS_Context::userConf()->show_tags, ['b', 'h'], true)) {
|
||||
$this->renderHelper('index/tags');
|
||||
}
|
||||
?>
|
||||
|
||||
<h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $entry->link() ?>"><?= $entry->title() ?></a></h1>
|
||||
<?php if (FreshRSS_Context::userConf()->show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?>
|
||||
<div class="subtitle">
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?>
|
||||
<div class="website"><a href="<?= $this->internal_rendering ? $feed->website() : _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
<?php if (FreshRSS_Context::userConf()->show_favicons): ?>
|
||||
<img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php
|
||||
endif; ?><span><?= $feed->name() ?></span></a></div>
|
||||
<?php } ?>
|
||||
<div class="author"><?php
|
||||
$authors = $entry->authors();
|
||||
if (is_array($authors)) {
|
||||
if ($this->internal_rendering):
|
||||
foreach ($authors as $author): ?>
|
||||
<?= $author ?>
|
||||
<?php endforeach;
|
||||
else:
|
||||
foreach ($authors as $author): ?>
|
||||
<a href="<?= Minz_Url::display(Minz_Request::modifiedCurrentRequest(['search' => 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))])) ?>">
|
||||
<?= $author ?>
|
||||
</a>
|
||||
<?php endforeach;
|
||||
endif;
|
||||
} ?>
|
||||
</div>
|
||||
<div class="date">
|
||||
<time datetime="<?= $entry->machineReadableDate() ?>"><?= $entry->date() ?></time>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</header>
|
||||
|
||||
<div class="text">
|
||||
<?= $entry->content(true) ?>
|
||||
</div>
|
||||
<?php
|
||||
$display_authors_date = in_array(FreshRSS_Context::userConf()->show_author_date, ['b', 'f'], true);
|
||||
$display_tags = in_array(FreshRSS_Context::userConf()->show_tags, ['b', 'f'], true);
|
||||
|
||||
if ($display_authors_date || $display_tags) {
|
||||
?>
|
||||
<footer>
|
||||
<?php if ($display_authors_date) { ?>
|
||||
<div class="subtitle">
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?>
|
||||
<div class="website"><a href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
<?php if (FreshRSS_Context::userConf()->show_favicons): ?>
|
||||
<img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php
|
||||
endif; ?><span><?= $feed->name() ?></span></a></div>
|
||||
<?php } ?>
|
||||
<div class="author"><?php
|
||||
$authors = $entry->authors();
|
||||
if (is_array($authors)) {
|
||||
foreach ($authors as $author) {
|
||||
?>
|
||||
<a href="<?= Minz_Url::display(Minz_Request::modifiedCurrentRequest(['search' => 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))])) ?>">
|
||||
<?= $author ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="date">
|
||||
<time datetime="<?= $entry->machineReadableDate() ?>"><?= $entry->date() ?></time>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ($display_tags) {
|
||||
$this->renderHelper('index/tags');
|
||||
}
|
||||
?>
|
||||
</footer>
|
||||
<?php
|
||||
} ?>
|
||||
</div>
|
||||
</article>
|
|
@ -1,9 +1,6 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
if ($this->feed === null) {
|
||||
throw new FreshRSS_Context_Exception('Feed not initialised!');
|
||||
}
|
||||
$topline_read = FreshRSS_Context::userConf()->topline_read;
|
||||
$topline_favorite = FreshRSS_Context::userConf()->topline_favorite;
|
||||
$topline_website = FreshRSS_Context::userConf()->topline_website;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
[$firstTags,$remainingTags] = $this->entry->tagsFormattingHelper();
|
||||
?>
|
||||
<div class="tags">
|
||||
<?php if (!empty($firstTags)): ?>
|
||||
<?= _i('tag') ?><ul class="list-tags">
|
||||
<?php if (Minz_Request::controllerName() === 'index'): ?>
|
||||
<?php foreach ($firstTags as $tag): ?>
|
||||
<li class="item tag"><a class="link-tag" href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
<?php else: // API public access ?>
|
||||
<?php foreach ($firstTags as $tag): ?>
|
||||
<li class="item tag"><a class="link-tag" href="<?= $this->html_url . '&search=%23' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES)) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($remainingTags)): // more than 7 tags: show dropdown menu ?>
|
||||
<li class="item tag">
|
||||
<div class="dropdown">
|
||||
<div id="dropdown-tags2-<?= $this->entry->id() ?>" class="dropdown-target"></div>
|
||||
<a class="dropdown-toggle" href="#dropdown-tags2-<?= $this->entry->id() ?>"><?= _i('down') ?></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-header"><?= _t('index.tag.related') ?></li>
|
||||
<?php if (Minz_Request::controllerName() === 'index'): ?>
|
||||
<?php foreach ($remainingTags as $tag): ?>
|
||||
<li class="item"><a href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<?php foreach ($remainingTags as $tag): ?>
|
||||
<li class="item tag"><a class="link-tag" href="<?= $this->html_url . '&search=%23' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES)) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<a class="dropdown-close" href="#close">❌</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/** @var FreshRSS_View $this */
|
||||
// Override some layout preferences for the public API output
|
||||
FreshRSS_Context::userConf()->content_width = 'large';
|
||||
FreshRSS_Context::userConf()->show_author_date = FreshRSS_UserConfiguration::default()->show_author_date;
|
||||
FreshRSS_Context::userConf()->show_favicons = FreshRSS_UserConfiguration::default()->show_favicons;
|
||||
FreshRSS_Context::userConf()->show_feed_name = FreshRSS_UserConfiguration::default()->show_feed_name;
|
||||
FreshRSS_Context::userConf()->show_tags = FreshRSS_UserConfiguration::default()->show_tags;
|
||||
FreshRSS_Context::userConf()->show_tags_max = FreshRSS_UserConfiguration::default()->show_tags_max;
|
||||
?>
|
||||
<?php $this->renderHelper('htmlPagination'); ?>
|
||||
<main id="stream" class="reader api">
|
||||
<h2>
|
||||
<a href="<?= $this->html_url ?>"><?= FreshRSS_View::title() ?></a> ·
|
||||
<a class="view-rss" href="<?= $this->rss_url ?>" title="<?= _t('index.menu.rss_view') ?>">
|
||||
<?= _i('rss') ?>
|
||||
</a>
|
||||
</h2>
|
||||
<?php
|
||||
foreach ($this->entries as $entry):
|
||||
$this->entry = $entry;
|
||||
$this->feed = $this->feeds[$entry->feedId()] ??
|
||||
FreshRSS_Category::findFeed($this->categories, $entry->feedId()) ??
|
||||
FreshRSS_Feed::default();
|
||||
?>
|
||||
<div class="flux">
|
||||
<?php $this->renderHelper('index/article'); ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</main>
|
||||
<?php $this->renderHelper('htmlPagination'); ?>
|
|
@ -11,23 +11,18 @@ call_user_func($this->callbackBeforeEntries, $this);
|
|||
$display_today = true;
|
||||
$display_yesterday = true;
|
||||
$display_others = true;
|
||||
$hidePosts = !FreshRSS_Context::userConf()->display_posts;
|
||||
$lazyload = FreshRSS_Context::userConf()->lazyload;
|
||||
$content_width = FreshRSS_Context::userConf()->content_width;
|
||||
$MAX_TAGS_DISPLAYED = (int)FreshRSS_Context::userConf()->show_tags_max;
|
||||
$useKeepUnreadImportant = !FreshRSS_Context::isImportant() && !FreshRSS_Context::isFeed();
|
||||
|
||||
$today = @strtotime('today');
|
||||
?>
|
||||
|
||||
<main id="stream" class="normal<?= $hidePosts ? ' hide_posts' : '' ?>">
|
||||
<main id="stream" class="normal<?= FreshRSS_Context::userConf()->display_posts ? '' : ' hide_posts' ?>">
|
||||
<h1 class="title_hidden"><?= _t('conf.reading.view.normal') ?></h1>
|
||||
<div id="new-article">
|
||||
<a href="<?= Minz_Url::display(Minz_Request::currentRequest()) ?>"><?= _t('gen.js.new_article'); /* TODO: move string in JS*/ ?></a>
|
||||
</div><?php
|
||||
$lastEntry = null;
|
||||
$nbEntries = 0;
|
||||
/** @var FreshRSS_Entry */
|
||||
foreach ($this->entries as $item):
|
||||
$lastEntry = $item;
|
||||
$nbEntries++;
|
||||
|
@ -40,8 +35,8 @@ $today = @strtotime('today');
|
|||
$this->entry = $item;
|
||||
|
||||
// We most likely already have the feed object in cache, otherwise make a request
|
||||
$this->feed = FreshRSS_CategoryDAO::findFeed($this->categories, $this->entry->feedId()) ??
|
||||
$this->entry->feed() ?? FreshRSS_Feed::example();
|
||||
$this->feed = FreshRSS_Category::findFeed($this->categories, $this->entry->feedId()) ??
|
||||
$this->entry->feed() ?? FreshRSS_Feed::default();
|
||||
|
||||
if ($display_today && $this->entry->isDay(FreshRSS_Days::TODAY, $today)) {
|
||||
?><div class="day" id="day_today"><?php
|
||||
|
@ -74,27 +69,8 @@ $today = @strtotime('today');
|
|||
?>" data-priority="<?= $this->feed->priority()
|
||||
?>"><?php
|
||||
$this->renderHelper('index/normal/entry_header');
|
||||
if ($this->feed === null) {
|
||||
throw new FreshRSS_Context_Exception('Feed not initialised!');
|
||||
}
|
||||
|
||||
$tags = null;
|
||||
$firstTags = array();
|
||||
$remainingTags = array();
|
||||
|
||||
if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'f' || FreshRSS_Context::userConf()->show_tags === 'b') {
|
||||
$tags = $this->entry->tags();
|
||||
if (!empty($tags)) {
|
||||
if ($MAX_TAGS_DISPLAYED > 0) {
|
||||
$firstTags = array_slice($tags, 0, $MAX_TAGS_DISPLAYED);
|
||||
$remainingTags = array_slice($tags, $MAX_TAGS_DISPLAYED);
|
||||
} else {
|
||||
$firstTags = $tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
?><article class="flux_content" dir="auto">
|
||||
<div class="content <?= $content_width ?>">
|
||||
<div class="content <?= FreshRSS_Context::userConf()->content_width ?>">
|
||||
<header>
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 't') { ?>
|
||||
<div class="website"><a href="<?= _url('index', 'index', 'get', 'f_' . $this->feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
|
@ -103,36 +79,8 @@ $today = @strtotime('today');
|
|||
endif; ?><span><?= $this->feed->name() ?></span></a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') { ?>
|
||||
<div class="tags">
|
||||
<?php
|
||||
if (!empty($tags)) {
|
||||
?><?= _i('tag') ?><ul class="list-tags"><?php
|
||||
foreach ($firstTags as $tag) {
|
||||
?><li class="item tag"><a class="link-tag" href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li><?php
|
||||
}
|
||||
|
||||
if (!empty($remainingTags)) { // more than 7 tags: show dropdown menu ?>
|
||||
<li class="item tag">
|
||||
<div class="dropdown">
|
||||
<div id="dropdown-tags2-<?= $this->entry->id() ?>" class="dropdown-target"></div>
|
||||
<a class="dropdown-toggle" href="#dropdown-tags2-<?= $this->entry->id() ?>"><?= _i('down') ?></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-header"><?= _t('index.tag.related') ?></li>
|
||||
<?php
|
||||
foreach ($remainingTags as $tag) {
|
||||
?><li class="item"><a href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>"><?= $tag ?></a></li><?php
|
||||
} ?>
|
||||
</ul>
|
||||
<a class="dropdown-close" href="#close">❌</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php
|
||||
} ?>
|
||||
</ul><?php
|
||||
} ?>
|
||||
</div>
|
||||
<?php
|
||||
<?php if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') {
|
||||
$this->renderHelper('index/tags');
|
||||
} ?>
|
||||
<h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $this->entry->link() ?>" title="<?= _t('conf.shortcut.see_on_website')?>"><?= $this->entry->title() ?></a></h1>
|
||||
<?php if (FreshRSS_Context::userConf()->show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?>
|
||||
|
@ -163,8 +111,8 @@ $today = @strtotime('today');
|
|||
</div>
|
||||
<?php } ?>
|
||||
</header>
|
||||
<div class="text"><?php
|
||||
echo $lazyload && $hidePosts ? lazyimg($this->entry->content(true)) : $this->entry->content(true);
|
||||
<div class="text"><?=
|
||||
FreshRSS_Context::userConf()->lazyload && !FreshRSS_Context::userConf()->display_posts ? lazyimg($this->entry->content(true)) : $this->entry->content(true)
|
||||
?></div>
|
||||
<?php
|
||||
$display_authors_date = FreshRSS_Context::userConf()->show_author_date === 'f' || FreshRSS_Context::userConf()->show_author_date === 'b';
|
||||
|
@ -201,36 +149,10 @@ $today = @strtotime('today');
|
|||
</div>
|
||||
<?php
|
||||
}
|
||||
if ($display_tags) { ?>
|
||||
<div class="tags">
|
||||
<?php
|
||||
if (!empty($tags)) {
|
||||
?><?= _i('tag') ?><ul class="list-tags"><?php
|
||||
foreach ($firstTags as $tag) {
|
||||
?><li class="item tag"><a class="link-tag" href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li><?php
|
||||
}
|
||||
if (!empty($remainingTags)) { ?>
|
||||
<li class="item tag">
|
||||
<div class="dropdown">
|
||||
<div id="dropdown-tags3-<?= $this->entry->id() ?>" class="dropdown-target"></div>
|
||||
<a class="dropdown-toggle" href="#dropdown-tags3-<?= $this->entry->id() ?>"><?= _i('down') ?></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-header"><?= _t('index.tag.related') ?></li>
|
||||
<?php
|
||||
foreach ($remainingTags as $tag) {
|
||||
?><li class="item"><a href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>"><?= $tag ?></a></li><?php
|
||||
} ?>
|
||||
</ul>
|
||||
<a class="dropdown-close" href="#close">❌</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php
|
||||
} ?>
|
||||
</ul><?php
|
||||
} ?>
|
||||
</div>
|
||||
<?php
|
||||
} ?>
|
||||
if ($display_tags) {
|
||||
$this->renderHelper('index/tags');
|
||||
}
|
||||
?>
|
||||
</footer>
|
||||
<?php
|
||||
} ?>
|
||||
|
|
|
@ -9,8 +9,6 @@ if (!Minz_Request::paramBoolean('ajax')) {
|
|||
call_user_func($this->callbackBeforeEntries, $this);
|
||||
|
||||
$lazyload = FreshRSS_Context::userConf()->lazyload;
|
||||
$content_width = FreshRSS_Context::userConf()->content_width;
|
||||
$MAX_TAGS_DISPLAYED = (int)FreshRSS_Context::userConf()->show_tags_max;
|
||||
?>
|
||||
<main id="stream" class="reader">
|
||||
<h1 class="title_hidden"><?= _t('conf.reading.view.reader') ?></h1>
|
||||
|
@ -19,197 +17,21 @@ $MAX_TAGS_DISPLAYED = (int)FreshRSS_Context::userConf()->show_tags_max;
|
|||
</div><?php
|
||||
$lastEntry = null;
|
||||
$nbEntries = 0;
|
||||
/** @var FreshRSS_Entry */
|
||||
foreach ($this->entries as $item):
|
||||
$lastEntry = $item;
|
||||
foreach ($this->entries as $entry):
|
||||
$lastEntry = $entry;
|
||||
$nbEntries++;
|
||||
ob_flush();
|
||||
/** @var FreshRSS_Entry */
|
||||
$item = Minz_ExtensionManager::callHook('entry_before_display', $item);
|
||||
if ($item == null) {
|
||||
$entry = Minz_ExtensionManager::callHook('entry_before_display', $entry);
|
||||
if ($entry == null) {
|
||||
continue;
|
||||
}
|
||||
$this->entry = $item;
|
||||
|
||||
$tags = null;
|
||||
$firstTags = array();
|
||||
$remainingTags = array();
|
||||
|
||||
if (FreshRSS_Context::userConf()->show_tags == 'h' || FreshRSS_Context::userConf()->show_tags == 'f' || FreshRSS_Context::userConf()->show_tags == 'b') {
|
||||
$tags = $this->entry->tags();
|
||||
if (!empty($tags)) {
|
||||
if ($MAX_TAGS_DISPLAYED > 0) {
|
||||
$firstTags = array_slice($tags, 0, $MAX_TAGS_DISPLAYED);
|
||||
$remainingTags = array_slice($tags, $MAX_TAGS_DISPLAYED);
|
||||
} else {
|
||||
$firstTags = $tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->entry = $entry;
|
||||
|
||||
//We most likely already have the feed object in cache, otherwise make a request
|
||||
$feed = FreshRSS_CategoryDAO::findFeed($this->categories, $item->feedId()) ?? $item->feed() ?? FreshRSS_Feed::example();
|
||||
?><div class="flux<?= !$item->isRead() ? ' not_read' : '' ?><?= $item->isFavorite() ? ' favorite' : '' ?>" id="flux_<?= $item->id() ?>" data-priority="<?= $feed->priority() ?>">
|
||||
<article class="flux_content" dir="auto">
|
||||
|
||||
<div class="content <?= $content_width ?>">
|
||||
<header>
|
||||
<?php
|
||||
$favoriteUrl = array('c' => 'entry', 'a' => 'bookmark', 'params' => array('id' => $item->id()));
|
||||
if ($item->isFavorite()) {
|
||||
$favoriteUrl['params']['is_favorite'] = 0;
|
||||
}
|
||||
$readUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('id' => $item->id()));
|
||||
if ($item->isRead()) {
|
||||
$readUrl['params']['is_read'] = 0;
|
||||
}
|
||||
?>
|
||||
<div class="article-header-topline">
|
||||
<?php if (FreshRSS_Auth::hasAccess()) { ?>
|
||||
<a class="read" href="<?= Minz_Url::display($readUrl) ?>" title="<?= _t('conf.shortcut.mark_read') ?>"><?= _i($item->isRead() ? 'read' : 'unread') ?></a>
|
||||
<a class="bookmark" href="<?= Minz_Url::display($favoriteUrl) ?>" title="<?= _t('conf.shortcut.mark_favorite') ?>"><?= _i($item->isFavorite() ? 'starred' : 'non-starred') ?></a>
|
||||
<?php } ?>
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 't') { ?>
|
||||
<a class="website" href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
<?php if (FreshRSS_Context::userConf()->show_favicons): ?>
|
||||
<img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php
|
||||
endif; ?><span><?= $feed->name() ?></span></a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<?php if (FreshRSS_Context::userConf()->show_tags === 'h' || FreshRSS_Context::userConf()->show_tags === 'b') { ?>
|
||||
<div class="tags">
|
||||
<?php
|
||||
if (!empty($tags)) {
|
||||
?><?= _i('tag') ?><ul class="list-tags"><?php
|
||||
foreach ($firstTags as $tag) {
|
||||
?><li class="item tag"><a class="link-tag" href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li><?php
|
||||
}
|
||||
|
||||
if (!empty($remainingTags)) { // more than 7 tags: show dropdown menu ?>
|
||||
<li class="item tag">
|
||||
<div class="dropdown">
|
||||
<div id="dropdown-tags-<?= $this->entry->id() ?>" class="dropdown-target"></div>
|
||||
<a class="dropdown-toggle" href="#dropdown-tags-<?= $this->entry->id() ?>"><?= _i('down') ?></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-header"><?= _t('index.tag.related') ?></li>
|
||||
<?php
|
||||
foreach ($remainingTags as $tag) {
|
||||
?><li class="item"><a href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>"><?= $tag ?></a></li><?php
|
||||
} ?>
|
||||
</ul>
|
||||
<a class="dropdown-close" href="#close">❌</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php
|
||||
} ?>
|
||||
</ul><?php
|
||||
} ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<h1 class="title"><a target="_blank" rel="noreferrer" class="go_website" href="<?= $item->link() ?>"><?= $item->title() ?></a></h1>
|
||||
<?php if (FreshRSS_Context::userConf()->show_author_date === 'h' || FreshRSS_Context::userConf()->show_author_date === 'b') { ?>
|
||||
<div class="subtitle">
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?>
|
||||
<div class="website"><a href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
<?php if (FreshRSS_Context::userConf()->show_favicons): ?>
|
||||
<img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php
|
||||
endif; ?><span><?= $feed->name() ?></span></a></div>
|
||||
<?php } ?>
|
||||
<div class="author"><?php
|
||||
$authors = $item->authors();
|
||||
if (is_array($authors)) {
|
||||
foreach ($authors as $author) {
|
||||
?>
|
||||
<a href="<?= Minz_Url::display(Minz_Request::modifiedCurrentRequest(['search' => 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))])) ?>">
|
||||
<?= $author ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="date">
|
||||
<time datetime="<?= $item->machineReadableDate() ?>"><?= $item->date() ?></time>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</header>
|
||||
|
||||
<div class="text">
|
||||
<?= $item->content(true) ?>
|
||||
</div>
|
||||
<?php
|
||||
$display_authors_date = FreshRSS_Context::userConf()->show_author_date === 'f' || FreshRSS_Context::userConf()->show_author_date === 'b';
|
||||
$display_tags = FreshRSS_Context::userConf()->show_tags === 'f' || FreshRSS_Context::userConf()->show_tags === 'b';
|
||||
|
||||
if ($display_authors_date || $display_tags) {
|
||||
?>
|
||||
<footer>
|
||||
<?php if ($display_authors_date) { ?>
|
||||
<div class="subtitle">
|
||||
<?php if (FreshRSS_Context::userConf()->show_feed_name === 'a') { ?>
|
||||
<div class="website"><a href="<?= _url('index', 'reader', 'get', 'f_' . $feed->id()) ?>" title="<?= _t('gen.action.filter') ?>">
|
||||
<?php if (FreshRSS_Context::userConf()->show_favicons): ?>
|
||||
<img class="favicon" src="<?= $feed->favicon() ?>" alt="✇" loading="lazy" /><?php
|
||||
endif; ?><span><?= $feed->name() ?></span></a></div>
|
||||
<?php } ?>
|
||||
<div class="author"><?php
|
||||
$authors = $item->authors();
|
||||
if (is_array($authors)) {
|
||||
foreach ($authors as $author) {
|
||||
?>
|
||||
<a href="<?= Minz_Url::display(Minz_Request::modifiedCurrentRequest(['search' => 'author:' . str_replace(' ', '+', htmlspecialchars_decode($author, ENT_QUOTES))])) ?>">
|
||||
<?= $author ?>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="date">
|
||||
<time datetime="<?= $item->machineReadableDate() ?>"><?= $item->date() ?></time>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ($display_tags) { ?>
|
||||
<div class="tags">
|
||||
<?php
|
||||
if (!empty($tags)) {
|
||||
?><?= _i('tag') ?><ul class="list-tags"><?php
|
||||
foreach ($firstTags as $tag) {
|
||||
?><li class="item tag"><a class="link-tag" href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>">#<?= $tag ?></a></li><?php
|
||||
}
|
||||
|
||||
if (!empty($remainingTags)) { // more than 7 tags: show dropdown menu ?>
|
||||
<li class="item tag">
|
||||
<div class="dropdown">
|
||||
<div id="dropdown-tags2-<?= $this->entry->id() ?>" class="dropdown-target"></div>
|
||||
<a class="dropdown-toggle" href="#dropdown-tags2-<?= $this->entry->id() ?>"><?= _i('down') ?></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-header"><?= _t('index.tag.related') ?></li>
|
||||
<?php
|
||||
foreach ($remainingTags as $tag) {
|
||||
?><li class="item"><a href="<?= _url('index', 'index', 'search', '#' . str_replace(' ', '+', htmlspecialchars_decode($tag, ENT_QUOTES))) ?>" title="<?= _t('gen.action.filter') ?>"><?= $tag ?></a></li><?php
|
||||
} ?>
|
||||
</ul>
|
||||
<a class="dropdown-close" href="#close">❌</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php
|
||||
} ?>
|
||||
</ul><?php
|
||||
} ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</footer>
|
||||
<?php
|
||||
} ?>
|
||||
</div>
|
||||
</article>
|
||||
$this->feed = FreshRSS_Category::findFeed($this->categories, $entry->feedId()) ?? $entry->feed() ?? FreshRSS_Feed::default();
|
||||
?><div class="flux<?= !$entry->isRead() ? ' not_read' : '' ?><?= $entry->isFavorite() ? ' favorite' : '' ?>" id="flux_<?= $entry->id() ?>" data-priority="<?= $this->feed->priority() ?>">
|
||||
<?php $this->renderHelper('index/article'); ?>
|
||||
</div><?php
|
||||
endforeach;
|
||||
|
||||
|
|
|
@ -8,14 +8,12 @@
|
|||
>
|
||||
<channel>
|
||||
<title><?= $this->rss_title ?></title>
|
||||
<link><?= $this->internal_rendering ? htmlspecialchars($this->rss_url, ENT_NOQUOTES, 'UTF-8') : Minz_Url::display('', 'html', true) ?></link>
|
||||
<link><?= $this->html_url ?></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="<?= $this->internal_rendering ? htmlspecialchars($this->rss_url, ENT_COMPAT, 'UTF-8') :
|
||||
Minz_Url::display($this->rss_url, 'html', true) ?>" rel="self" type="application/rss+xml" />
|
||||
<atom:link href="<?= $this->rss_url ?>" rel="self" type="application/rss+xml" />
|
||||
<?php
|
||||
/** @var FreshRSS_Entry */
|
||||
foreach ($this->entries as $item) {
|
||||
if (!$this->internal_rendering) {
|
||||
/** @var FreshRSS_Entry */
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
<p class="help"><?= _i('help') ?> <?= _t('admin.auth.token_help') ?></p>
|
||||
<kbd><?= Minz_Url::display(array('a' => 'rss', 'params' => array('user' => Minz_User::name(),
|
||||
'token' => $token, 'hours' => FreshRSS_Context::userConf()->since_hours_posts_per_rss)), 'html', true) ?></kbd>
|
||||
<p class="help"><?= _i('help') ?> <?= _t('conf.query.help') ?></a></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
|
|
@ -36,10 +36,10 @@ return array (
|
|||
'auto_load_more' => true,
|
||||
'display_posts' => false,
|
||||
'display_categories' => 'active', //{ active, remember, all, none }
|
||||
'show_tags' => '0',
|
||||
'show_tags' => 'f', // {0 => none, b => both, f => footer, h => header}
|
||||
'show_tags_max' => 7,
|
||||
'show_author_date' => 'h',
|
||||
'show_feed_name' => 'a',
|
||||
'show_author_date' => 'h', // {0 => none, b => both, f => footer, h => header}
|
||||
'show_feed_name' => 'a', // {0 => none, a => with authors, t => above title}
|
||||
'hide_read_feeds' => true,
|
||||
'onread_jump_next' => true,
|
||||
'lazyload' => true,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
|
@ -38,3 +38,4 @@ Reader view will display a feed will all articles already open for reading. Feed
|
|||
Read more:
|
||||
* [Refreshing the feeds](./09_refreshing_feeds.md)
|
||||
* [Filter the feeds and search](./10_filter.md)
|
||||
* [User queries](./user_queries.md)
|
||||
|
|
|
@ -167,10 +167,7 @@ This means that if you assign a shortcut to more than one action, you’ll end u
|
|||
|
||||
# User queries
|
||||
|
||||
You can configure your [user queries](./03_Main_view.md) in that section. There is not much to say here as it is pretty straightforward.
|
||||
You can only change user query titles or drop them.
|
||||
|
||||
At the moment, there is no helper to build a user query from here.
|
||||
You can configure your [user queries](./user_queries.md) in that section.
|
||||
|
||||
# Profile
|
||||
|
||||
|
|
|
@ -119,34 +119,18 @@ Finally, parentheses may be used to express more complex queries, with basic neg
|
|||
|
||||
You can change the sort order by clicking the toggle button available in the header.
|
||||
|
||||
## Store your filters
|
||||
## Bookmark the current query
|
||||
|
||||
Once you came up with your perfect filter, it would be a shame if you need to recreate it every time you need to use it.
|
||||
Once you came up with your perfect filter, it would be a shame if you had to recreate it every time you need to use it.
|
||||
|
||||
Hopefully, there is a way to bookmark them for later use.
|
||||
We call them *user queries*.
|
||||
Luckily, there is a way to bookmark them for later use.
|
||||
We call them [*user queries*](./user_queries.md).
|
||||
You can create as many as you want, the only limit is how they will be displayed on your screen.
|
||||
|
||||
### Bookmark the current query
|
||||
|
||||
Display the user queries drop-down by clicking the button next to the state buttons.
|
||||
![User queries drop-down](../img/users/user.queries.drop-down.empty.png)
|
||||
|
||||
Then click on the bookmark action.
|
||||
|
||||
Congratulations, you’re done!
|
||||
|
||||
### Using a bookmarked query
|
||||
|
||||
Display the user queries drop-down by clicking the button next to the state buttons.
|
||||
![User queries drop-down](../img/users/user.queries.drop-down.not.empty.png)
|
||||
|
||||
Then click on the bookmarked query, the previously stored query will be applied.
|
||||
|
||||
> Note that only the query is stored, not the articles.
|
||||
> The results you are seeing now could be different from the results on the day you've created the query.
|
||||
Read more about [*user queries*](./user_queries.md) to learn how to create them, use them, and even reshare them via HTML / RSS / OPML.
|
||||
|
||||
---
|
||||
Read more:
|
||||
* [Normal, Global and Reader view](./03_Main_view.md)
|
||||
* [Refreshing the feeds](./09_refreshing_feeds.md)
|
||||
* [User queries](./user_queries.md)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
# User queries
|
||||
|
||||
*User queries* are a way to store any FreshRSS search query.
|
||||
|
||||
Read about [the filters](./10_filter.md) to learn the different ways to search and filter
|
||||
articles in FreshRSS.
|
||||
|
||||
## Bookmark the current query
|
||||
|
||||
Once you have a search query with a filter, it can be saved.
|
||||
|
||||
To do so, display the user queries drop-down menu by clicking the button next to the state buttons:
|
||||
|
||||
![User queries drop-down](../img/users/user.queries.drop-down.empty.png)
|
||||
|
||||
Then click on the bookmark action.
|
||||
|
||||
## Using a bookmarked query
|
||||
|
||||
Display the user queries drop-down menu by clicking the button next to the state buttons:
|
||||
|
||||
![User queries drop-down](../img/users/user.queries.drop-down.not.empty.png)
|
||||
|
||||
Then click on the bookmarked query, the previously stored query will be applied.
|
||||
|
||||
> ℹ️ Note that only the search query is stored, not the articles.
|
||||
> So the results you are seeing one day might be different another day.
|
||||
|
||||
## Share your user queries
|
||||
|
||||
A prerequisite is that the FreshRSS API(s) must be enabled in FreshRSS authentication settings.
|
||||
|
||||
From the configuration page of the user queries,
|
||||
it is possible to share the output of the user queries with external users,
|
||||
in the formats HTML, RSS, and OPML:
|
||||
|
||||
![Share user query](../img/users/user-query-share.png)
|
||||
|
||||
> ℹ️ Note that the sharing as OPML is only available for user queries based on all feeds, a category, or a feed.
|
||||
> Sharing by OPML is **not** available for queries based on user labels or favourites or important feeds,
|
||||
> to avoid leaking some feed details in an unintended manner.
|
||||
|
||||
### Additional parameters for shared user queries
|
||||
|
||||
Some parameters can be manually added to the URL:
|
||||
|
||||
* `f`: Format of output. Can be `html`, `rss` (`atom` is an alias), or `opml`.
|
||||
* `hours`: Show only the articles newer than this number of hours.
|
||||
* `nb`: Number of articles to return. Limited by `max_posts_per_rss` in the user configuration. Can be used in combination with `offset` for pagination.
|
||||
* `offset`: Skip a number of articles. Used in particular by the HTML view for pagination.
|
||||
* `order`: Show the newest articles at the top with `DESC`, or the oldest articles at the top with `ASC`. By default, will use the sort order defined by the user query.
|
||||
|
||||
## Sharing with a master token (deprecated)
|
||||
|
||||
Before FreshRSS 1.24, the only option to reshare an RSS output was by using a master token,
|
||||
like `https://freshrss.example.net/?a=rss&user=alice&token1234`
|
||||
|
||||
This was mostly intended for sharing between systems controlled by the same user, and not for sharing publicly.
|
||||
|
||||
This method **is not advised anymore** as it is not safe to use the same master token for multiple outputs,
|
||||
especially not when shared with other persons.
|
||||
|
||||
Now, sharing RSS outputs via user queries is the recommended approach for all scenarios.
|
|
@ -162,11 +162,11 @@ class Minz_Request {
|
|||
* Setteurs
|
||||
*/
|
||||
public static function _controllerName(string $controller_name): void {
|
||||
self::$controller_name = $controller_name;
|
||||
self::$controller_name = ctype_alnum($controller_name) ? $controller_name : '';
|
||||
}
|
||||
|
||||
public static function _actionName(string $action_name): void {
|
||||
self::$action_name = $action_name;
|
||||
self::$action_name = ctype_alnum($action_name) ? $action_name : '';
|
||||
}
|
||||
|
||||
/** @param array<string,string> $params */
|
||||
|
@ -187,6 +187,7 @@ class Minz_Request {
|
|||
* Initialise la Request
|
||||
*/
|
||||
public static function init(): void {
|
||||
self::_params($_GET);
|
||||
self::initJSON();
|
||||
}
|
||||
|
||||
|
|
|
@ -572,7 +572,7 @@ final class GReaderAPI {
|
|||
continue;
|
||||
}
|
||||
|
||||
$feed = FreshRSS_CategoryDAO::findFeed($categories, $entry->feedId());
|
||||
$feed = FreshRSS_Category::findFeed($categories, $entry->feedId());
|
||||
if ($feed === null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -694,7 +694,7 @@ final class GReaderAPI {
|
|||
}
|
||||
|
||||
$entryDAO = FreshRSS_Factory::createEntryDao();
|
||||
$entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, $searches);
|
||||
$entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, 0, $continuation, $searches);
|
||||
$entries = iterator_to_array($entries); //TODO: Improve
|
||||
|
||||
$items = self::entriesToArray($entries);
|
||||
|
@ -746,7 +746,7 @@ final class GReaderAPI {
|
|||
}
|
||||
|
||||
$entryDAO = FreshRSS_Factory::createEntryDao();
|
||||
$ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, $searches);
|
||||
$ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, 0, $continuation, $searches);
|
||||
if ($ids === null) {
|
||||
self::internalServerError();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
require(__DIR__ . '/../../constants.php');
|
||||
require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
|
||||
|
||||
Minz_Request::init();
|
||||
|
||||
$token = Minz_Request::paramString('t');
|
||||
if (!ctype_alnum($token)) {
|
||||
header('HTTP/1.1 422 Unprocessable Entity');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
die('Invalid token `t`!' . $token);
|
||||
}
|
||||
|
||||
$format = Minz_Request::paramString('f');
|
||||
if (!in_array($format, ['atom', 'html', 'opml', 'rss'], true)) {
|
||||
header('HTTP/1.1 422 Unprocessable Entity');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
die('Invalid format `f`!');
|
||||
}
|
||||
|
||||
$user = Minz_Request::paramString('user');
|
||||
if (!FreshRSS_user_Controller::checkUsername($user)) {
|
||||
header('HTTP/1.1 422 Unprocessable Entity');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
die('Invalid user!');
|
||||
}
|
||||
|
||||
Minz_Session::init('FreshRSS', true);
|
||||
|
||||
FreshRSS_Context::initSystem();
|
||||
if (!FreshRSS_Context::hasSystemConf() || !FreshRSS_Context::systemConf()->api_enabled) {
|
||||
header('HTTP/1.1 503 Service Unavailable');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
die('Service Unavailable!');
|
||||
}
|
||||
|
||||
FreshRSS_Context::initUser($user);
|
||||
if (!FreshRSS_Context::hasUserConf()) {
|
||||
usleep(rand(100, 10000)); //Primitive mitigation of scanning for users
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
die('User not found!');
|
||||
} else {
|
||||
usleep(rand(20, 200));
|
||||
}
|
||||
|
||||
if (!file_exists(DATA_PATH . '/no-cache.txt')) {
|
||||
require(LIB_PATH . '/http-conditional.php');
|
||||
// TODO: Consider taking advantage of $feedMode, only for monotonous queries {all, categories, feeds} and not dynamic ones {read/unread, favourites, user labels}
|
||||
if (httpConditional(FreshRSS_UserDAO::mtime($user) ?: time(), 0, 0, false, PHP_COMPRESSION, false)) {
|
||||
exit(); //No need to send anything
|
||||
}
|
||||
}
|
||||
|
||||
Minz_Translate::init(FreshRSS_Context::userConf()->language);
|
||||
Minz_ExtensionManager::init();
|
||||
Minz_ExtensionManager::enableByList(FreshRSS_Context::userConf()->extensions_enabled, 'user');
|
||||
|
||||
$query = null;
|
||||
$userSearch = null;
|
||||
foreach (FreshRSS_Context::userConf()->queries as $raw_query) {
|
||||
if (!empty($raw_query['token']) && $raw_query['token'] === $token) {
|
||||
switch ($format) {
|
||||
case 'atom':
|
||||
case 'html':
|
||||
case 'rss':
|
||||
if (empty($raw_query['shareRss'])) {
|
||||
continue 2;
|
||||
}
|
||||
break;
|
||||
case 'opml':
|
||||
if (empty($raw_query['shareOpml'])) {
|
||||
continue 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue 2;
|
||||
}
|
||||
$query = new FreshRSS_UserQuery($raw_query, FreshRSS_Context::categories(), FreshRSS_Context::labels());
|
||||
Minz_Request::_param('get', $query->getGet());
|
||||
if (Minz_Request::paramString('order') === '') {
|
||||
Minz_Request::_param('order', $query->getOrder());
|
||||
}
|
||||
Minz_Request::_param('state', $query->getState());
|
||||
|
||||
$search = $query->getSearch()->getRawInput();
|
||||
// Note: we disallow references to user queries in public user search to avoid sniffing internal user queries
|
||||
$userSearch = new FreshRSS_BooleanSearch(Minz_Request::paramString('search'), 0, 'AND', false);
|
||||
if ($userSearch->getRawInput() !== '') {
|
||||
if ($search === '') {
|
||||
$search = $userSearch->getRawInput();
|
||||
} else {
|
||||
$search .= ' (' . $userSearch->getRawInput() . ')';
|
||||
}
|
||||
}
|
||||
Minz_Request::_param('search', $search);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($query === null || $userSearch === null) {
|
||||
usleep(rand(100, 10000));
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
die('User query not found!');
|
||||
}
|
||||
|
||||
$view = new FreshRSS_View();
|
||||
|
||||
try {
|
||||
FreshRSS_Context::updateUsingRequest(false);
|
||||
Minz_Request::_param('search', $userSearch->getRawInput()); // Restore user search
|
||||
$view->entries = FreshRSS_index_Controller::listEntriesByContext();
|
||||
} catch (Minz_Exception $e) {
|
||||
Minz_Error::error(400, 'Bad user query!');
|
||||
die();
|
||||
}
|
||||
|
||||
$get = FreshRSS_Context::currentGet(true);
|
||||
$type = (string)$get[0];
|
||||
$id = (int)$get[1];
|
||||
|
||||
switch ($type) {
|
||||
case 'c': // Category
|
||||
$cat = FreshRSS_Context::categories()[$id] ?? null;
|
||||
if ($cat === null) {
|
||||
Minz_Error::error(404, "Category {$id} not found!");
|
||||
die();
|
||||
}
|
||||
$view->categories = [ $cat->id() => $cat ];
|
||||
break;
|
||||
case 'f': // Feed
|
||||
$feed = FreshRSS_Category::findFeed(FreshRSS_Context::categories(), $id);
|
||||
if ($feed === null) {
|
||||
Minz_Error::error(404, "Feed {$id} not found!");
|
||||
die();
|
||||
}
|
||||
$view->feeds = [ $feed->id() => $feed ];
|
||||
$view->categories = [];
|
||||
break;
|
||||
default:
|
||||
$view->categories = FreshRSS_Context::categories();
|
||||
break;
|
||||
}
|
||||
|
||||
$view->disable_aside = true;
|
||||
$view->excludeMutedFeeds = true;
|
||||
$view->internal_rendering = true;
|
||||
$view->userQuery = $query;
|
||||
$view->html_url = $query->sharedUrlHtml();
|
||||
$view->rss_url = $query->sharedUrlRss();
|
||||
$view->rss_title = $query->getName();
|
||||
if ($query->getName() != '') {
|
||||
FreshRSS_View::_title($query->getName());
|
||||
}
|
||||
FreshRSS_Context::systemConf()->allow_anonymous = true;
|
||||
|
||||
if (in_array($format, ['rss', 'atom'], true)) {
|
||||
header('Content-Type: application/rss+xml; charset=utf-8');
|
||||
$view->_layout(null);
|
||||
$view->_path('index/rss.phtml');
|
||||
} elseif ($format === 'opml') {
|
||||
if (!$query->safeForOpml()) {
|
||||
Minz_Error::error(404, 'OPML not allowed for this user query!');
|
||||
die();
|
||||
}
|
||||
header('Content-Type: application/xml; charset=utf-8');
|
||||
$view->_layout(null);
|
||||
$view->_path('index/opml.phtml');
|
||||
} else {
|
||||
$view->_layout('layout');
|
||||
$view->_path('index/html.phtml');
|
||||
}
|
||||
|
||||
$view->build();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue