From 9c97d8ca729e3cfb067445c0d3c9ad8284132aeb Mon Sep 17 00:00:00 2001 From: eta-orionis <3466670+eta-orionis@users.noreply.github.com> Date: Wed, 10 Jan 2024 08:23:45 +0100 Subject: JSONFeeds, JSON scraping, and POST requests for feeds (#5662) * allow POST requests for feeds * added json dotpath and jsonfeed subscriptions. No translation strings yet * debug and fix jsonfeed parser * bugfix params saved when editing feed * added translations for JSON features * Update docs for web scraping * make fix-all and revert unrelated changes, plus a few manual fixes, but there are still several type errors * Fix some i18n * refactor json parsing for both feed types * cleanup unnecessary comment * refactored generation of SimplePie for XPath and JSON feeds * Fix merge error * Update to newer FreshRSS code * A bit of refactoring * doc, whitespace * JSON Feed is in two words * Add support for array syntax * Whitespace * Add OPML export/import * Work on i18n * Accept application/feed+json * Rework POST * Fix update * OPML for cURL options * Fix types * Fix Typos --------- Co-authored-by: Erion Elmasllari Co-authored-by: Alexandre Alapetite --- app/Controllers/feedController.php | 60 +++++++++ app/Controllers/subscriptionController.php | 51 +++++++ app/Models/Feed.php | 81 ++++++++++- app/Services/ExportService.php | 2 + app/Services/ImportService.php | 78 ++++++++++- app/Utils/dotpathUtil.php | 207 +++++++++++++++++++++++++++++ app/i18n/cz/sub.php | 44 ++++++ app/i18n/de/sub.php | 44 ++++++ app/i18n/el/sub.php | 44 ++++++ app/i18n/en-us/sub.php | 44 ++++++ app/i18n/en/sub.php | 44 ++++++ app/i18n/es/sub.php | 46 ++++++- app/i18n/fa/sub.php | 44 ++++++ app/i18n/fr/sub.php | 46 ++++++- app/i18n/he/sub.php | 44 ++++++ app/i18n/hu/sub.php | 44 ++++++ app/i18n/id/sub.php | 44 ++++++ app/i18n/it/sub.php | 44 ++++++ app/i18n/ja/sub.php | 44 ++++++ app/i18n/ko/sub.php | 44 ++++++ app/i18n/lv/sub.php | 44 ++++++ app/i18n/nl/sub.php | 44 ++++++ app/i18n/oc/sub.php | 44 ++++++ app/i18n/pl/sub.php | 44 ++++++ app/i18n/pt-br/sub.php | 44 ++++++ app/i18n/ru/sub.php | 44 ++++++ app/i18n/sk/sub.php | 44 ++++++ app/i18n/tr/sub.php | 44 ++++++ app/i18n/zh-cn/sub.php | 44 ++++++ app/i18n/zh-tw/sub.php | 44 ++++++ app/views/helpers/export/opml.phtml | 64 +++++++-- app/views/helpers/feed/update.phtml | 109 +++++++++++++++ app/views/subscription/add.phtml | 105 +++++++++++++++ docs/en/developers/OPML.md | 38 ++++++ docs/en/users/11_website_scraping.md | 41 +++++- lib/lib_rss.php | 4 +- tests/app/Utils/dotpathUtilTest.php | 44 ++++++ 37 files changed, 1919 insertions(+), 25 deletions(-) create mode 100644 app/Utils/dotpathUtil.php create mode 100644 tests/app/Utils/dotpathUtilTest.php diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index aa1b30182..018bdb382 100644 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -178,6 +178,9 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { $useragent = Minz_Request::paramString('curl_params_useragent'); $proxy_address = Minz_Request::paramString('curl_params'); $proxy_type = Minz_Request::paramString('proxy_type'); + $request_method = Minz_Request::paramString('curl_method'); + $request_fields = Minz_Request::paramString('curl_fields', true); + $opts = []; if ($proxy_type !== '') { $opts[CURLOPT_PROXY] = $proxy_address; @@ -198,6 +201,15 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { if ($useragent !== '') { $opts[CURLOPT_USERAGENT] = $useragent; } + if ($request_method === 'POST') { + $opts[CURLOPT_POST] = true; + if ($request_fields !== '') { + $opts[CURLOPT_POSTFIELDS] = $request_fields; + if (json_decode($request_fields, true) !== null) { + $opts[CURLOPT_HTTPHEADER] = ['Content-Type: application/json']; + } + } + } $attributes = [ 'curl_params' => empty($opts) ? null : $opts, @@ -245,6 +257,44 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { if (!empty($xPathSettings)) { $attributes['xpath'] = $xPathSettings; } + } elseif ($feed_kind === FreshRSS_Feed::KIND_JSON_DOTPATH) { + $jsonSettings = []; + if (Minz_Request::paramString('jsonFeedTitle') !== '') { + $jsonSettings['feedTitle'] = Minz_Request::paramString('jsonFeedTitle', true); + } + if (Minz_Request::paramString('jsonItem') !== '') { + $jsonSettings['item'] = Minz_Request::paramString('jsonItem', true); + } + if (Minz_Request::paramString('jsonItemTitle') !== '') { + $jsonSettings['itemTitle'] = Minz_Request::paramString('jsonItemTitle', true); + } + if (Minz_Request::paramString('jsonItemContent') !== '') { + $jsonSettings['itemContent'] = Minz_Request::paramString('jsonItemContent', true); + } + if (Minz_Request::paramString('jsonItemUri') !== '') { + $jsonSettings['itemUri'] = Minz_Request::paramString('jsonItemUri', true); + } + if (Minz_Request::paramString('jsonItemAuthor') !== '') { + $jsonSettings['itemAuthor'] = Minz_Request::paramString('jsonItemAuthor', true); + } + if (Minz_Request::paramString('jsonItemTimestamp') !== '') { + $jsonSettings['itemTimestamp'] = Minz_Request::paramString('jsonItemTimestamp', true); + } + if (Minz_Request::paramString('jsonItemTimeFormat') !== '') { + $jsonSettings['itemTimeFormat'] = Minz_Request::paramString('jsonItemTimeFormat', true); + } + if (Minz_Request::paramString('jsonItemThumbnail') !== '') { + $jsonSettings['itemThumbnail'] = Minz_Request::paramString('jsonItemThumbnail', true); + } + if (Minz_Request::paramString('jsonItemCategories') !== '') { + $jsonSettings['itemCategories'] = Minz_Request::paramString('jsonItemCategories', true); + } + if (Minz_Request::paramString('jsonItemUid') !== '') { + $jsonSettings['itemUid'] = Minz_Request::paramString('jsonItemUid', true); + } + if (!empty($jsonSettings)) { + $attributes['json_dotpath'] = $jsonSettings; + } } try { @@ -445,6 +495,16 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController { if ($simplePie === null) { throw new FreshRSS_Feed_Exception('XML+XPath parsing failed for [' . $feed->url(false) . ']'); } + } elseif ($feed->kind() === FreshRSS_Feed::KIND_JSON_DOTPATH) { + $simplePie = $feed->loadJson(); + if ($simplePie === null) { + throw new FreshRSS_Feed_Exception('JSON dotpath parsing failed for [' . $feed->url(false) . ']'); + } + } elseif ($feed->kind() === FreshRSS_Feed::KIND_JSONFEED) { + $simplePie = $feed->loadJson(); + if ($simplePie === null) { + throw new FreshRSS_Feed_Exception('JSON Feed parsing failed for [' . $feed->url(false) . ']'); + } } else { $simplePie = $feed->load(false, $feedIsNew); } diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php index 07e0fdcf8..cf23c018e 100644 --- a/app/Controllers/subscriptionController.php +++ b/app/Controllers/subscriptionController.php @@ -143,6 +143,8 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { $useragent = Minz_Request::paramString('curl_params_useragent'); $proxy_address = Minz_Request::paramString('curl_params'); $proxy_type = Minz_Request::paramString('proxy_type'); + $request_method = Minz_Request::paramString('curl_method'); + $request_fields = Minz_Request::paramString('curl_fields', true); $opts = []; if ($proxy_type !== '') { $opts[CURLOPT_PROXY] = $proxy_address; @@ -163,6 +165,17 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { if ($useragent !== '') { $opts[CURLOPT_USERAGENT] = $useragent; } + + if ($request_method === 'POST') { + $opts[CURLOPT_POST] = true; + if ($request_fields !== '') { + $opts[CURLOPT_POSTFIELDS] = $request_fields; + if (json_decode($request_fields, true) !== null) { + $opts[CURLOPT_HTTPHEADER] = ['Content-Type: application/json']; + } + } + } + $feed->_attribute('curl_params', empty($opts) ? null : $opts); $feed->_attribute('content_action', Minz_Request::paramString('content_action', true) ?: 'replace'); @@ -224,6 +237,44 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController { $xPathSettings['itemUid'] = Minz_Request::paramString('xPathItemUid', true); if (!empty($xPathSettings)) $feed->_attribute('xpath', $xPathSettings); + } elseif ($feed->kind() === FreshRSS_Feed::KIND_JSON_DOTPATH) { + $jsonSettings = []; + if (Minz_Request::paramString('jsonFeedTitle') !== '') { + $jsonSettings['feedTitle'] = Minz_Request::paramString('jsonFeedTitle', true); + } + if (Minz_Request::paramString('jsonItem') !== '') { + $jsonSettings['item'] = Minz_Request::paramString('jsonItem', true); + } + if (Minz_Request::paramString('jsonItemTitle') !== '') { + $jsonSettings['itemTitle'] = Minz_Request::paramString('jsonItemTitle', true); + } + if (Minz_Request::paramString('jsonItemContent') !== '') { + $jsonSettings['itemContent'] = Minz_Request::paramString('jsonItemContent', true); + } + if (Minz_Request::paramString('jsonItemUri') !== '') { + $jsonSettings['itemUri'] = Minz_Request::paramString('jsonItemUri', true); + } + if (Minz_Request::paramString('jsonItemAuthor') !== '') { + $jsonSettings['itemAuthor'] = Minz_Request::paramString('jsonItemAuthor', true); + } + if (Minz_Request::paramString('jsonItemTimestamp') !== '') { + $jsonSettings['itemTimestamp'] = Minz_Request::paramString('jsonItemTimestamp', true); + } + if (Minz_Request::paramString('jsonItemTimeFormat') !== '') { + $jsonSettings['itemTimeFormat'] = Minz_Request::paramString('jsonItemTimeFormat', true); + } + if (Minz_Request::paramString('jsonItemThumbnail') !== '') { + $jsonSettings['itemThumbnail'] = Minz_Request::paramString('jsonItemThumbnail', true); + } + if (Minz_Request::paramString('jsonItemCategories') !== '') { + $jsonSettings['itemCategories'] = Minz_Request::paramString('jsonItemCategories', true); + } + if (Minz_Request::paramString('jsonItemUid') !== '') { + $jsonSettings['itemUid'] = Minz_Request::paramString('jsonItemUid', true); + } + if (!empty($jsonSettings)) { + $feed->_attribute('json_dotpath', $jsonSettings); + } } $feed->_attribute('path_entries_filter', Minz_Request::paramString('path_entries_filter', true)); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 024a7841a..40e78bfeb 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -30,6 +30,9 @@ class FreshRSS_Feed extends Minz_Model { */ public const KIND_JSON_XPATH = 20; + public const KIND_JSONFEED = 25; + public const KIND_JSON_DOTPATH = 30; + public const PRIORITY_IMPORTANT = 20; public const PRIORITY_MAIN_STREAM = 10; public const PRIORITY_CATEGORY = 0; @@ -579,6 +582,78 @@ class FreshRSS_Feed extends Minz_Model { } } + /** + * Given a feed content generated from a FreshRSS_View + * returns a SimplePie initialized already with that content + * @param string $feedContent the content of the feed, typically generated via FreshRSS_View::renderToString() + */ + private function simplePieFromContent(string $feedContent): SimplePie { + $simplePie = customSimplePie(); + $simplePie->set_raw_data($feedContent); + $simplePie->init(); + return $simplePie; + } + + /** @return array */ + private function dotPathsForStandardJsonFeed(): array { + return [ + 'feedTitle' => 'title', + 'item' => 'items', + 'itemTitle' => 'title', + 'itemContent' => 'content_text', + 'itemContentHTML' => 'content_html', + 'itemUri' => 'url', + 'itemTimestamp' => 'date_published', + 'itemTimeFormat' => DateTimeInterface::RFC3339_EXTENDED, + 'itemThumbnail' => 'image', + 'itemCategories' => 'tags', + 'itemUid' => 'id', + 'itemAttachment' => 'attachments', + 'itemAttachmentUrl' => 'url', + 'itemAttachmentType' => 'mime_type', + 'itemAttachmentLength' => 'size_in_bytes', + ]; + } + + /** + * @throws FreshRSS_Context_Exception + */ + public function loadJson(): ?SimplePie { + if ($this->url == '') { + return null; + } + $feedSourceUrl = htmlspecialchars_decode($this->url, ENT_QUOTES); + if ($this->httpAuth != '') { + $feedSourceUrl = preg_replace('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $feedSourceUrl); + } + if ($feedSourceUrl == null) { + return null; + } + + $cachePath = FreshRSS_Feed::cacheFilename($feedSourceUrl, $this->attributes(), $this->kind()); + $httpAccept = 'json'; + $json = httpGet($feedSourceUrl, $cachePath, $httpAccept, $this->attributes()); + if (strlen($json) <= 0) { + return null; + } + + //check if the content is actual JSON + $jf = json_decode($json, true); + if (json_last_error() !== JSON_ERROR_NONE) { + return null; + } + + /** @var array $json_dotpath */ + $json_dotpath = $this->attributeArray('json_dotpath') ?? []; + $dotPaths = $this->kind() === FreshRSS_Feed::KIND_JSONFEED ? $this->dotPathsForStandardJsonFeed() : $json_dotpath; + + $feedContent = FreshRSS_dotpath_Util::convertJsonToRss($jf, $feedSourceUrl, $dotPaths, $this->name()); + if ($feedContent == null) { + return null; + } + return $this->simplePieFromContent($feedContent); + } + /** * @throws FreshRSS_Context_Exception */ @@ -719,11 +794,7 @@ class FreshRSS_Feed extends Minz_Model { Minz_Log::warning($ex->getMessage()); return null; } - - $simplePie = customSimplePie(); - $simplePie->set_raw_data($view->renderToString()); - $simplePie->init(); - return $simplePie; + return $this->simplePieFromContent($view->renderToString()); } /** diff --git a/app/Services/ExportService.php b/app/Services/ExportService.php index 0dbe1c8f5..7f027441b 100644 --- a/app/Services/ExportService.php +++ b/app/Services/ExportService.php @@ -20,6 +20,8 @@ class FreshRSS_Export_Service { public const TYPE_HTML_XPATH = 'HTML+XPath'; public const TYPE_XML_XPATH = 'XML+XPath'; public const TYPE_RSS_ATOM = 'rss'; + public const TYPE_JSON_DOTPATH = 'JSON+DotPath'; + public const TYPE_JSONFEED = 'JSONFeed'; /** * Initialize the service for the given user. diff --git a/app/Services/ImportService.php b/app/Services/ImportService.php index dace91a22..2c7d64f80 100644 --- a/app/Services/ImportService.php +++ b/app/Services/ImportService.php @@ -161,7 +161,12 @@ class FreshRSS_Import_Service { case strtolower(FreshRSS_Export_Service::TYPE_XML_XPATH): $feed->_kind(FreshRSS_Feed::KIND_XML_XPATH); break; - case strtolower(FreshRSS_Export_Service::TYPE_RSS_ATOM): + case strtolower(FreshRSS_Export_Service::TYPE_JSON_DOTPATH): + $feed->_kind(FreshRSS_Feed::KIND_JSON_DOTPATH); + break; + case strtolower(FreshRSS_Export_Service::TYPE_JSONFEED): + $feed->_kind(FreshRSS_Feed::KIND_JSONFEED); + break; default: $feed->_kind(FreshRSS_Feed::KIND_RSS); break; @@ -213,11 +218,80 @@ class FreshRSS_Import_Service { if (isset($feed_elt['frss:xPathItemUid'])) { $xPathSettings['itemUid'] = $feed_elt['frss:xPathItemUid']; } - if (!empty($xPathSettings)) { $feed->_attribute('xpath', $xPathSettings); } + $jsonSettings = []; + if (isset($feed_elt['frss:jsonItem'])) { + $jsonSettings['item'] = $feed_elt['frss:jsonItem']; + } + if (isset($feed_elt['frss:jsonItemTitle'])) { + $jsonSettings['itemTitle'] = $feed_elt['frss:jsonItemTitle']; + } + if (isset($feed_elt['frss:jsonItemContent'])) { + $jsonSettings['itemContent'] = $feed_elt['frss:jsonItemContent']; + } + if (isset($feed_elt['frss:jsonItemUri'])) { + $jsonSettings['itemUri'] = $feed_elt['frss:jsonItemUri']; + } + if (isset($feed_elt['frss:jsonItemAuthor'])) { + $jsonSettings['itemAuthor'] = $feed_elt['frss:jsonItemAuthor']; + } + if (isset($feed_elt['frss:jsonItemTimestamp'])) { + $jsonSettings['itemTimestamp'] = $feed_elt['frss:jsonItemTimestamp']; + } + if (isset($feed_elt['frss:jsonItemTimeFormat'])) { + $jsonSettings['itemTimeFormat'] = $feed_elt['frss:jsonItemTimeFormat']; + } + if (isset($feed_elt['frss:jsonItemThumbnail'])) { + $jsonSettings['itemThumbnail'] = $feed_elt['frss:jsonItemThumbnail']; + } + if (isset($feed_elt['frss:jsonItemCategories'])) { + $jsonSettings['itemCategories'] = $feed_elt['frss:jsonItemCategories']; + } + if (isset($feed_elt['frss:jsonItemUid'])) { + $jsonSettings['itemUid'] = $feed_elt['frss:jsonItemUid']; + } + if (!empty($jsonSettings)) { + $feed->_attribute('json_dotpath', $jsonSettings); + } + + $curl_params = []; + if (isset($feed_elt['frss:CURLOPT_COOKIE'])) { + $curl_params[CURLOPT_COOKIE] = $feed_elt['frss:CURLOPT_COOKIE']; + } + if (isset($feed_elt['frss:CURLOPT_COOKIEFILE'])) { + $curl_params[CURLOPT_COOKIEFILE] = $feed_elt['frss:CURLOPT_COOKIEFILE']; + } + if (isset($feed_elt['frss:CURLOPT_FOLLOWLOCATION'])) { + $curl_params[CURLOPT_FOLLOWLOCATION] = (bool)$feed_elt['frss:CURLOPT_FOLLOWLOCATION']; + } + if (isset($feed_elt['frss:CURLOPT_HTTPHEADER'])) { + $curl_params[CURLOPT_HTTPHEADER] = preg_split('/\R/', $feed_elt['frss:CURLOPT_HTTPHEADER']) ?: []; + } + if (isset($feed_elt['frss:CURLOPT_MAXREDIRS'])) { + $curl_params[CURLOPT_MAXREDIRS] = (int)$feed_elt['frss:CURLOPT_MAXREDIRS']; + } + if (isset($feed_elt['frss:CURLOPT_POST'])) { + $curl_params[CURLOPT_POST] = (bool)$feed_elt['frss:CURLOPT_POST']; + } + if (isset($feed_elt['frss:CURLOPT_POSTFIELDS'])) { + $curl_params[CURLOPT_POSTFIELDS] = $feed_elt['frss:CURLOPT_POSTFIELDS']; + } + if (isset($feed_elt['frss:CURLOPT_PROXY'])) { + $curl_params[CURLOPT_PROXY] = $feed_elt['frss:CURLOPT_PROXY']; + } + if (isset($feed_elt['frss:CURLOPT_PROXYTYPE'])) { + $curl_params[CURLOPT_PROXYTYPE] = $feed_elt['frss:CURLOPT_PROXYTYPE']; + } + if (isset($feed_elt['frss:CURLOPT_USERAGENT'])) { + $curl_params[CURLOPT_USERAGENT] = $feed_elt['frss:CURLOPT_USERAGENT']; + } + if (!empty($curl_params)) { + $feed->_attribute('curl_params', $curl_params); + } + // Call the extension hook /** @var FreshRSS_Feed|null */ $feed = Minz_ExtensionManager::callHook('feed_before_insert', $feed); diff --git a/app/Utils/dotpathUtil.php b/app/Utils/dotpathUtil.php new file mode 100644 index 000000000..aaeb82daa --- /dev/null +++ b/app/Utils/dotpathUtil.php @@ -0,0 +1,207 @@ +|array|mixed $array + * @param string|null $key + * @param mixed $default + * @return mixed + */ + public static function get($array, ?string $key, mixed $default = null) { + if (!static::accessible($array)) { + return static::value($default); + } + /** @var \ArrayAccess|array $array */ + if ($key === null || $key === '') { + return $array; + } + + // Compatibility with brackets path such as `items[0].value` + $key = preg_replace('/\[(\d+)\]/', '.$1', $key); + if ($key === null) { + return null; + } + + if (static::exists($array, $key)) { + return $array[$key]; + } + if (strpos($key, '.') === false) { + return $array[$key] ?? static::value($default); + } + foreach (explode('.', $key) as $segment) { + if (static::accessible($array) && static::exists($array, $segment)) { + $array = $array[$segment]; + } else { + return static::value($default); + } + } + return $array; + } + + /** + * Get a string from an array using "dot" notation. + * + * @param \ArrayAccess|array|mixed $array + * @param string|null $key + */ + public static function getString($array, ?string $key): ?string { + $result = self::get($array, $key, null); + return is_string($result) ? $result : null; + } + + /** + * Determine whether the given value is array accessible. + * + * @param mixed $value + * @return bool + */ + private static function accessible(mixed $value): bool { + return is_array($value) || $value instanceof \ArrayAccess; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array|mixed $array + * @param string $key + * @return bool + */ + private static function exists($array, string $key): bool { + if ($array instanceof \ArrayAccess) { + return $array->offsetExists($key); + } + if (is_array($array)) { + return array_key_exists($key, $array); + } + return false; + } + + private static function value(mixed $value): mixed { + return $value instanceof Closure ? $value() : $value; + } + + /** + * Convert a JSON object to a RSS document + * mapping fields from the JSON object into RSS equivalents + * according to the dot-separated paths + * + * @param array $jf json feed + * @param string $feedSourceUrl the source URL for the feed + * @param array $dotPaths dot paths to map JSON into RSS + * @param string $defaultRssTitle Default title of the RSS feed, if not already provided in dotPath `feedTitle` + */ + public static function convertJsonToRss(array $jf, string $feedSourceUrl, array $dotPaths, string $defaultRssTitle = ''): ?string { + if (!isset($dotPaths['item']) || $dotPaths['item'] === '') { + return null; //no definition of item path, but we can't scrape anything without knowing this + } + + $view = new FreshRSS_View(); + $view->_path('index/rss.phtml'); + $view->internal_rendering = true; + $view->rss_url = $feedSourceUrl; + $view->entries = []; + + try { + $view->rss_title = isset($dotPaths['feedTitle']) + ? (htmlspecialchars(FreshRSS_dotpath_Util::getString($jf, $dotPaths['feedTitle']) ?? '', ENT_COMPAT, 'UTF-8') ?: $defaultRssTitle) + : $defaultRssTitle; + + $jsonItems = FreshRSS_dotpath_Util::get($jf, $dotPaths['item']); + if (!is_array($jsonItems) || count($jsonItems) === 0) { + return null; + } + + foreach ($jsonItems as $jsonItem) { + $rssItem = []; + $rssItem['link'] = isset($dotPaths['itemUri']) ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemUri']) ?? '' : ''; + if (empty($rssItem['link'])) { + continue; + } + $rssItem['title'] = isset($dotPaths['itemTitle']) ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemTitle']) ?? '' : ''; + $rssItem['author'] = isset($dotPaths['itemAuthor']) ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemAuthor']) ?? '' : ''; + $rssItem['timestamp'] = isset($dotPaths['itemTimestamp']) ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemTimestamp']) ?? '' : ''; + + //get simple content, but if a path for HTML content has been provided, replace the simple content with HTML content + $rssItem['content'] = isset($dotPaths['itemContent']) ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemContent']) ?? '' : ''; + $rssItem['content'] = isset($dotPaths['itemContentHTML']) + ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemContentHTML']) ?? '' + : $rssItem['content']; + + if (isset($dotPaths['itemTimeFormat']) && is_string($dotPaths['itemTimeFormat'])) { + $dateTime = DateTime::createFromFormat($dotPaths['itemTimeFormat'], $rssItem['timestamp']); + if ($dateTime != false) { + $rssItem['timestamp'] = $dateTime->format(DateTime::ATOM); + } + } + + if (isset($dotPaths['itemCategories'])) { + $jsonItemCategories = FreshRSS_dotpath_Util::get($jsonItem, $dotPaths['itemCategories']); + if (is_string($jsonItemCategories) && $jsonItemCategories !== '') { + $rssItem['tags'] = [$jsonItemCategories]; + } elseif (is_array($jsonItemCategories) && count($jsonItemCategories) > 0) { + $rssItem['tags'] = []; + foreach ($jsonItemCategories as $jsonItemCategory) { + if (is_string($jsonItemCategory)) { + $rssItem['tags'][] = $jsonItemCategory; + } + } + } + } + + $rssItem['thumbnail'] = isset($dotPaths['itemThumbnail']) ? FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemThumbnail']) ?? '' : ''; + + //Enclosures? + if (isset($dotPaths['itemAttachment'])) { + $jsonItemAttachments = FreshRSS_dotpath_Util::get($jsonItem, $dotPaths['itemAttachment']); + if (is_array($jsonItemAttachments) && count($jsonItemAttachments) > 0) { + $rssItem['attachments'] = []; + foreach ($jsonItemAttachments as $attachment) { + $rssAttachment = []; + $rssAttachment['url'] = isset($dotPaths['itemAttachmentUrl']) + ? FreshRSS_dotpath_Util::getString($attachment, $dotPaths['itemAttachmentUrl']) + : ''; + $rssAttachment['type'] = isset($dotPaths['itemAttachmentType']) + ? FreshRSS_dotpath_Util::getString($attachment, $dotPaths['itemAttachmentType']) + : ''; + $rssAttachment['length'] = isset($dotPaths['itemAttachmentLength']) + ? FreshRSS_dotpath_Util::get($attachment, $dotPaths['itemAttachmentLength']) + : ''; + $rssItem['attachments'][] = $rssAttachment; + } + } + } + + if (isset($dotPaths['itemUid'])) { + $rssItem['guid'] = FreshRSS_dotpath_Util::getString($jsonItem, $dotPaths['itemUid']); + } + + if (empty($rssItem['guid'])) { + $rssItem['guid'] = 'urn:sha1:' . sha1($rssItem['title'] . $rssItem['content'] . $rssItem['link']); + } + + if ($rssItem['title'] != '' || $rssItem['content'] != '' || $rssItem['link'] != '') { + // HTML-encoding/escaping of the relevant fields (all except 'content') + foreach (['author', 'guid', 'link', 'thumbnail', 'timestamp', 'tags', 'title'] as $key) { + if (!empty($rssItem[$key]) && is_string($rssItem[$key])) { + $rssItem[$key] = Minz_Helper::htmlspecialchars_utf8($rssItem[$key]); + } + } + $view->entries[] = FreshRSS_Entry::fromArray($rssItem); + } + } + } catch (Exception $ex) { + Minz_Log::warning($ex->getMessage()); + return null; + } + + return $view->renderToString(); + } +} diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php index 199766ad9..db398a593 100644 --- a/app/i18n/cz/sub.php +++ b/app/i18n/cz/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (vzhledem k položce) pro:', 'xpath' => 'XPath pro:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (výchozí)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Maximální počet přesměrování HTTP', 'max_http_redir_help' => 'Nastavte na 0 nebo nechte prázdné pro zakázání, -1 pro neomezené přesměrování.', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Když odstraníte kategorii, její kanály jsou automaticky přesunuty do %s.', 'mute' => 'ztlumit', 'no_selected' => 'Nejsou vybrány žádné kanály.', diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php index 51d9669f9..ba6309757 100644 --- a/app/i18n/de/sub.php +++ b/app/i18n/de/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relativ zum Artikel) für:', 'xpath' => 'XPath für:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (Standard)', 'xml_xpath' => 'XML + XPath', // IGNORE ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP Umleitungen', 'max_http_redir_help' => '0 oder leeres Feld = deaktiviert; -1 für unendlich viele Umleitungen', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Wenn Sie eine Kategorie entfernen, werden deren Feeds automatisch in die Kategorie %s eingefügt.', 'mute' => 'Stumm schalten', 'no_selected' => 'Kein Feed ausgewählt.', diff --git a/app/i18n/el/sub.php b/app/i18n/el/sub.php index c101600b4..8de9a77fd 100644 --- a/app/i18n/el/sub.php +++ b/app/i18n/el/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relative to item) for:', // TODO 'xpath' => 'XPath for:', // TODO ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (default)', // TODO 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redirects', // TODO 'max_http_redir_help' => 'Set to 0 or leave blank to disable, -1 for unlimited redirects', // TODO + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'When you delete a category, its feeds are automatically classified under %s.', // TODO 'mute' => 'mute', // TODO 'no_selected' => 'No feed selected.', // TODO diff --git a/app/i18n/en-us/sub.php b/app/i18n/en-us/sub.php index 591c52dec..e6eae591c 100644 --- a/app/i18n/en-us/sub.php +++ b/app/i18n/en-us/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relative to item) for:', // IGNORE 'xpath' => 'XPath for:', // IGNORE ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // IGNORE + 'feed_title' => array( + '_' => 'feed title', // IGNORE + 'help' => 'Example: meta.title or a static string: "My custom feed"', // IGNORE + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // IGNORE + 'item' => array( + '_' => 'finding news items
(most important)', // IGNORE + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // IGNORE + ), + 'item_author' => 'item author', // IGNORE + 'item_categories' => 'item tags', // IGNORE + 'item_content' => array( + '_' => 'item content', // IGNORE + 'help' => 'Key under which the content is found, e.g. content', // IGNORE + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // IGNORE + 'help' => 'Example: image', // IGNORE + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // IGNORE + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // IGNORE + ), + 'item_timestamp' => array( + '_' => 'item date', // IGNORE + 'help' => 'The result will be parsed by strtotime()', // IGNORE + ), + 'item_title' => 'item title', // IGNORE + 'item_uid' => 'item unique ID', // IGNORE + 'item_uri' => array( + '_' => 'item link (URL)', // IGNORE + 'help' => 'Example: permalink', // IGNORE + ), + 'json' => 'Dotted Path for:', // IGNORE + 'relative' => 'Dotted Path (relative to item) for:', // IGNORE + ), + 'jsonfeed' => 'JSON Feed', // IGNORE 'rss' => 'RSS / Atom (default)', // IGNORE 'xml_xpath' => 'XML + XPath', // IGNORE ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redirects', // IGNORE 'max_http_redir_help' => 'Set to 0 or leave blank to disable, -1 for unlimited redirects', // IGNORE + 'method' => array( + '_' => 'HTTP Method', // IGNORE + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // IGNORE + 'method_postparams' => 'Payload for POST', // IGNORE 'moved_category_deleted' => 'When you delete a category, its feeds are automatically classified under %s.', // IGNORE 'mute' => 'mute', // IGNORE 'no_selected' => 'No feed selected.', // IGNORE diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php index f6aebca4b..e76051212 100644 --- a/app/i18n/en/sub.php +++ b/app/i18n/en/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relative to item) for:', 'xpath' => 'XPath for:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', + 'feed_title' => array( + '_' => 'feed title', + 'help' => 'Example: meta.title or a static string: "My custom feed"', + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', + 'item' => array( + '_' => 'finding news items
(most important)', + 'help' => 'JSON path to the array containing the items, e.g. newsItems', + ), + 'item_author' => 'item author', + 'item_categories' => 'item tags', + 'item_content' => array( + '_' => 'item content', + 'help' => 'Key under which the content is found, e.g. content', + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', + 'help' => 'Example: image', + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', + ), + 'item_timestamp' => array( + '_' => 'item date', + 'help' => 'The result will be parsed by strtotime()', + ), + 'item_title' => 'item title', + 'item_uid' => 'item unique ID', + 'item_uri' => array( + '_' => 'item link (URL)', + 'help' => 'Example: permalink', + ), + 'json' => 'Dotted Path for:', + 'relative' => 'Dotted Path (relative to item) for:', + ), + 'jsonfeed' => 'JSON Feed', 'rss' => 'RSS / Atom (default)', 'xml_xpath' => 'XML + XPath', ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redirects', 'max_http_redir_help' => 'Set to 0 or leave blank to disable, -1 for unlimited redirects', + 'method' => array( + '_' => 'HTTP Method', + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', + 'method_postparams' => 'Payload for POST', 'moved_category_deleted' => 'When you delete a category, its feeds are automatically classified under %s.', 'mute' => 'mute', 'no_selected' => 'No feed selected.', diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php index 6fc8031fb..e466472ca 100644 --- a/app/i18n/es/sub.php +++ b/app/i18n/es/sub.php @@ -26,7 +26,7 @@ return array( 'archiving' => 'Archivo', 'dynamic_opml' => array( '_' => 'OPML dinámico', - 'help' => 'Provee la URL a un archivo OPML para llenar dinámicamente esta categoría con feeds', + 'help' => 'Provee la URL a un archivo OPML para llenar dinámicamente esta categoría con feeds', ), 'empty' => 'Vaciar categoría', 'information' => 'Información', @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relativo al elemento) para:', 'xpath' => 'XPath para:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (por defecto)', 'xml_xpath' => 'XML + XPath', // IGNORE ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Máximas redirecciones HTTP', 'max_http_redir_help' => 'Escribir 0 o dejarlo en blanco para deshabilitarlo, -1 para redirecciones ilimitadas', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Al borrar una categoría todas sus fuentes pasan automáticamente a la categoría %s.', 'mute' => 'silenciar', 'no_selected' => 'No hay funentes seleccionadas.', diff --git a/app/i18n/fa/sub.php b/app/i18n/fa/sub.php index eb520f54f..d6f267e20 100644 --- a/app/i18n/fa/sub.php +++ b/app/i18n/fa/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (نسبت به مورد) برای:', 'xpath' => ' XPath برای:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => ' RSS / Atom (پیش‌فرض)', 'xml_xpath' => ' XML + XPath', ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => ' حداکثر تغییر مسیر HTTP', 'max_http_redir_help' => ' روی 0 تنظیم کنید یا برای غیرفعال کردن آن را خالی بگذارید', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => ' هنگامی که یک دسته را حذف می کنید', 'mute' => ' بی صدا', 'no_selected' => ' هیچ خوراکی انتخاب نشده است.', diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php index c2ecfc6a9..f9c78c583 100644 --- a/app/i18n/fr/sub.php +++ b/app/i18n/fr/sub.php @@ -82,7 +82,7 @@ return array( ), 'help' => 'XPath 1.0 est un langage de requête pour les utilisateurs avancés, supporté par FreshRSS pour le moissonnage du Web (Web scraping).', 'item' => array( - '_' => 'trouver les articles', + '_' => 'trouver les articles
(c’est le plus important)', 'help' => 'Exemple : //div[@class="article"]', ), 'item_author' => array( @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relatif à l’article) pour :', 'xpath' => 'XPath pour :', ), + 'json_dotpath' => array( + '_' => 'JSON (Chemin)', + 'feed_title' => array( + '_' => 'titre de flux', + 'help' => 'Exemple : meta.title ou un texte statique : "Mon flux personnalisé"', + ), + 'help' => 'Un chemin JSON utilise le point comme séparateur objet, et des crochets pour un tableau : (ex : data.items[0].title)', + 'item' => array( + '_' => 'trouver les articles
(c’est le plus important)', + 'help' => 'Chemin vers le tableau contenant les articles, par exemple newsItems', + ), + 'item_author' => 'auteur de l’article', + 'item_categories' => 'catégories (tags) de l’article', + 'item_content' => array( + '_' => 'contenu de l’article', + 'help' => 'Chemin JSON pour le contenu, par exemple content', + ), + 'item_thumbnail' => array( + '_' => 'miniature de l’article', + 'help' => 'Exemple : image', + ), + 'item_timeFormat' => array( + '_' => 'Format personnalisé pour interpréter la date', + 'help' => 'Optionnel. Un format supporté par DateTime::createFromFormat() comme d-m-Y H:i:s', + ), + 'item_timestamp' => array( + '_' => 'date de l’article', + 'help' => 'Le résultat sera passé à la fonction strtotime()', + ), + 'item_title' => 'titre de l’article', + 'item_uid' => 'identifiant unique de l’article', + 'item_uri' => array( + '_' => 'lien (URL) de l’article', + 'help' => 'Exemple : permalink', + ), + 'json' => 'Chemin JSON pour :', + 'relative' => 'Chemin relatif à l’article pour :', + ), + 'jsonfeed' => 'JSON Feed', // IGNORE 'rss' => 'RSS / Atom (par défaut)', 'xml_xpath' => 'XML + XPath', // IGNORE ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Maximum de redirections HTTP', 'max_http_redir_help' => 'Mettre à 0 ou vide pour désactiver, -1 pour un nombre illimité de redirections', + 'method' => array( + '_' => 'Méthode HTTP', + ), + 'method_help' => 'Les données POST supportent automatiquement application/x-www-form-urlencoded et application/json', + 'method_postparams' => 'Données pour POST', 'moved_category_deleted' => 'Lors de la suppression d’une catégorie, ses flux seront automatiquement classés dans %s.', 'mute' => 'désactivé', 'no_selected' => 'Aucun flux sélectionné.', diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php index 70f34e3be..83e0f2e95 100644 --- a/app/i18n/he/sub.php +++ b/app/i18n/he/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relative to item) for:', // TODO 'xpath' => 'XPath for:', // TODO ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (default)', // TODO 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redirects', // TODO 'max_http_redir_help' => 'Set to 0 or leave blank to disable, -1 for unlimited redirects', // TODO + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'כאשר הקטגוריה נמחקת ההזנות שבתוכה אוטומטית מקוטלגות תחת %s.', 'mute' => 'mute', // TODO 'no_selected' => 'אף הזנה לא נבחרה.', diff --git a/app/i18n/hu/sub.php b/app/i18n/hu/sub.php index d618b9346..1934b0f06 100644 --- a/app/i18n/hu/sub.php +++ b/app/i18n/hu/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (az elemhez viszonyítva) ehhez:', 'xpath' => 'XPath ehhez:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (alapértelmezett)', 'xml_xpath' => 'XML + XPath', // IGNORE ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP átirányítás', 'max_http_redir_help' => '0 vagy üresen hagyva kikapcsolt, -1 a végtelen átirányításhoz', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Ha kitörölsz egy kategóriát, az alá tartozó hírforrások automatikusan ide kerülnek %s.', 'mute' => 'némítás', 'no_selected' => 'Nincsen hírforrás kiválasztva.', diff --git a/app/i18n/id/sub.php b/app/i18n/id/sub.php index 51e6c5704..d08746ba1 100644 --- a/app/i18n/id/sub.php +++ b/app/i18n/id/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relative to item) for:', // TODO 'xpath' => 'XPath for:', // TODO ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (default)', // TODO 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redirects', // TODO 'max_http_redir_help' => 'Set to 0 or leave blank to disable, -1 for unlimited redirects', // TODO + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'When you delete a category, its feeds are automatically classified under %s.', // TODO 'mute' => 'mute', // TODO 'no_selected' => 'No feed selected.', // TODO diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php index 0e5a05e19..4b953903b 100644 --- a/app/i18n/it/sub.php +++ b/app/i18n/it/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relativo all’oggetto) per:', 'xpath' => 'XPath per:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (predefinito)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Numero massimo di redirect HTTP', 'max_http_redir_help' => 'Imposta a 0 o lascia in bianco per disabilitare, -1 per impostare un numero illimitato di redirect', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Cancellando una categoria i feed al suo interno verranno classificati automaticamente come %s.', 'mute' => 'muta', 'no_selected' => 'Nessun feed selezionato.', diff --git a/app/i18n/ja/sub.php b/app/i18n/ja/sub.php index 2a8cfbc21..188f3f25a 100644 --- a/app/i18n/ja/sub.php +++ b/app/i18n/ja/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (関連する項目):', 'xpath' => 'XPathは:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (標準)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'HTTPのリダイレクトの上限', 'max_http_redir_help' => '0を設定するか、空白のままにすると無効になり、-1を設定するとリダイレクト数が無制限になります。', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'カテゴリを削除したとき、フィードは自動的に%s下に分類されます。', 'mute' => 'ミュート', 'no_selected' => 'どのフィードも選択されていません', diff --git a/app/i18n/ko/sub.php b/app/i18n/ko/sub.php index 8509235fe..762922c80 100644 --- a/app/i18n/ko/sub.php +++ b/app/i18n/ko/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => '다음의 (기사와 관련된) XPath:', 'xpath' => '다음의 XPath:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (기본값)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => '최대 HTTP 리다이렉션', 'max_http_redir_help' => '값을 비워두거나 0으로 설정하면 비활성화하며, -1으로 설정하면 무제한 리다이렉션합니다', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => '카테고리를 삭제하면, 해당 카테고리 아래에 있던 피드들은 자동적으로 %s 아래로 분류됩니다.', 'mute' => '무기한 새로고침 금지', 'no_selected' => '선택된 피드가 없습니다.', diff --git a/app/i18n/lv/sub.php b/app/i18n/lv/sub.php index e2079d2ce..c10ac166f 100644 --- a/app/i18n/lv/sub.php +++ b/app/i18n/lv/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relatīvs rakstam) priekš:', 'xpath' => 'XPath priekš:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (noklusējums)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Maksimālais HTTP novirzījumu skaits', 'max_http_redir_help' => 'Iestatiet 0 vai atstājiet tukšu, lai atspējotu, -1 neierobežotai novirzīšanai', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Kad dzēšat kategoriju, tās plūsmas automātiski tiek automātiski klasificētas kategorijā %s.', 'mute' => 'klusināt', 'no_selected' => 'Barotne nav izvēlēta.', diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php index 78991614f..87c7b3e4b 100644 --- a/app/i18n/nl/sub.php +++ b/app/i18n/nl/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relatief naar bericht) voor:', 'xpath' => 'XPath voor:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (standaard)', 'xml_xpath' => 'XML + XPath', // IGNORE ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redirects', // IGNORE 'max_http_redir_help' => 'Stel in op 0 of laat leeg om uit te schakelen, -1 voor ongelimiteerde redirects', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Als u een categorie verwijderd, worden de feeds automatisch geclassificeerd onder %s.', 'mute' => 'demp', 'no_selected' => 'Geen feed geselecteerd.', diff --git a/app/i18n/oc/sub.php b/app/i18n/oc/sub.php index 0845ef54c..9931519f8 100644 --- a/app/i18n/oc/sub.php +++ b/app/i18n/oc/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relatiu a l’element) per :', 'xpath' => 'XPath per :', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (defaut)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP redireccions', 'max_http_redir_help' => 'Definir a 0 o daissar void per lo desactivar, -1 per de redireccions illimitadas', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Quand escafatz una categoria, sos fluxes son automaticament classats dins %s.', 'mute' => 'mut', 'no_selected' => 'Cap de flux pas seleccionat.', diff --git a/app/i18n/pl/sub.php b/app/i18n/pl/sub.php index b2928f2ed..01521fe5b 100644 --- a/app/i18n/pl/sub.php +++ b/app/i18n/pl/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (względem wiadomości) dla:', 'xpath' => 'XPath dla:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (domyślne)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Limit przekierowań HTTP', 'max_http_redir_help' => 'Ustaw na 0, albo pozostaw puste, by zabronić przekierowywania. Wartość -1 wyłącza limit.', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Po usunięciu kategorii znajdujące się w niej kanały zostaną automatycznie przeniesione do %s.', 'mute' => 'wycisz', 'no_selected' => 'Brak kanałów.', diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php index 202b559a7..2777ab2e4 100644 --- a/app/i18n/pt-br/sub.php +++ b/app/i18n/pt-br/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relativo do item) para:', 'xpath' => 'XPath para:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (padrão)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Quantidade máxima de redirecionamentos HTTP', 'max_http_redir_help' => 'Defina como 0 ou deixe em branco para desabilitar, -1 para redirecionamentos ilimitados', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Quando você deleta uma categoria, seus feeds são automaticamente classificados como %s.', 'mute' => 'silenciar', 'no_selected' => 'Nenhum feed selecionado.', diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php index e3b98c536..e9090b997 100644 --- a/app/i18n/ru/sub.php +++ b/app/i18n/ru/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (относительно элемента) для:', 'xpath' => 'XPath для:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (по умолчанию)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Максимум HTTP переводов', 'max_http_redir_help' => 'Установите 0 или оставьте пустым, чтобы отключить, -1 для бесконечных переводов', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Когда вы удаляете категорию, ленты категории автоматически попадают в категорию %s.', 'mute' => 'заглушить', 'no_selected' => 'Ленты не выбраны.', diff --git a/app/i18n/sk/sub.php b/app/i18n/sk/sub.php index 71d52995c..029ed4e75 100644 --- a/app/i18n/sk/sub.php +++ b/app/i18n/sk/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (relatívne k položke) pre:', 'xpath' => 'XPath pre:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (prednastavené)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Max HTTP presmerovaní', 'max_http_redir_help' => 'Nastavte na 0 alebo nechajte prázdne na zakázanie, -1 pre neobmedzené množstvo presmerovaní', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Keď vymažete kategóriu, jej kanály sa automaticky zaradia pod %s.', 'mute' => 'stíšiť', 'no_selected' => 'Nevybrali ste kanál.', diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php index 538770d03..b461d83f4 100644 --- a/app/i18n/tr/sub.php +++ b/app/i18n/tr/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath (nesneye ait):', 'xpath' => 'XPath:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (varsayılan)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => 'Maksimum HTTP yönlendirme sayısı', 'max_http_redir_help' => 'Devre dışı bırakmak için boş bırakın ya da 0 olarak bırakın. Sınırsız yönlendirme için -1 olarak tanımlayın', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => 'Bir kategoriyi silerseniz, içerisindeki akışlar %s içerisine yerleşir.', 'mute' => 'sessize al', 'no_selected' => 'Hiçbir akış seçilmedi.', diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php index 63de97950..cf6922f4c 100644 --- a/app/i18n/zh-cn/sub.php +++ b/app/i18n/zh-cn/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath(文章):', 'xpath' => 'XPath 定位:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (默认)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => '最大 HTTP 重定向', 'max_http_redir_help' => '设置为 0 或留空以禁用,-1 表示无限重定向', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => '删除分类时,其中的订阅源会自动归类到 %s', 'mute' => '暂停', 'no_selected' => '未选择订阅源', diff --git a/app/i18n/zh-tw/sub.php b/app/i18n/zh-tw/sub.php index 3e5e03823..2b5847343 100644 --- a/app/i18n/zh-tw/sub.php +++ b/app/i18n/zh-tw/sub.php @@ -121,6 +121,45 @@ return array( 'relative' => 'XPath(文章):', 'xpath' => 'XPath 定位:', ), + 'json_dotpath' => array( + '_' => 'JSON (Dotted paths)', // TODO + 'feed_title' => array( + '_' => 'feed title', // TODO + 'help' => 'Example: meta.title or a static string: "My custom feed"', // TODO + ), + 'help' => 'A JSON dotted path uses dots between objects and brackets for arrays (e.g. data.items[0].title)', // TODO + 'item' => array( + '_' => 'finding news items
(most important)', // TODO + 'help' => 'JSON path to the array containing the items, e.g. newsItems', // TODO + ), + 'item_author' => 'item author', // TODO + 'item_categories' => 'item tags', // TODO + 'item_content' => array( + '_' => 'item content', // TODO + 'help' => 'Key under which the content is found, e.g. content', // TODO + ), + 'item_thumbnail' => array( + '_' => 'item thumbnail', // TODO + 'help' => 'Example: image', // TODO + ), + 'item_timeFormat' => array( + '_' => 'Custom date/time format', // TODO + 'help' => 'Optional. A format supported by DateTime::createFromFormat() such as d-m-Y H:i:s', // TODO + ), + 'item_timestamp' => array( + '_' => 'item date', // TODO + 'help' => 'The result will be parsed by strtotime()', // TODO + ), + 'item_title' => 'item title', // TODO + 'item_uid' => 'item unique ID', // TODO + 'item_uri' => array( + '_' => 'item link (URL)', // TODO + 'help' => 'Example: permalink', // TODO + ), + 'json' => 'Dotted Path for:', // TODO + 'relative' => 'Dotted Path (relative to item) for:', // TODO + ), + 'jsonfeed' => 'JSON Feed', // TODO 'rss' => 'RSS / Atom (默認)', 'xml_xpath' => 'XML + XPath', // TODO ), @@ -133,6 +172,11 @@ return array( ), 'max_http_redir' => '最大 HTTP 重定向', 'max_http_redir_help' => '設置為 0 或留空以禁用,-1 表示無限重定向', + 'method' => array( + '_' => 'HTTP Method', // TODO + ), + 'method_help' => 'The POST payload has automatic support for application/x-www-form-urlencoded and application/json', // TODO + 'method_postparams' => 'Payload for POST', // TODO 'moved_category_deleted' => '刪除分類時,其中的訂閱源會自動歸類到 %s', 'mute' => '暫停', 'no_selected' => '未選擇訂閱源', diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml index ce53bfc02..736854f46 100644 --- a/app/views/helpers/export/opml.phtml +++ b/app/views/helpers/export/opml.phtml @@ -3,7 +3,7 @@ declare(strict_types=1); /** * @param array $feeds - * @return array> + * @return array> */ function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array { $outlines = []; @@ -20,15 +20,22 @@ function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array { 'description' => htmlspecialchars_decode($feed->description(), ENT_QUOTES), ]; + switch ($feed->kind()) { + case FreshRSS_Feed::KIND_HTML_XPATH: + $outline['type'] = FreshRSS_Export_Service::TYPE_HTML_XPATH; + break; + case FreshRSS_Feed::KIND_XML_XPATH: + $outline['type'] = FreshRSS_Export_Service::TYPE_XML_XPATH; + break; + case FreshRSS_Feed::KIND_JSON_DOTPATH: + $outline['type'] = FreshRSS_Export_Service::TYPE_JSON_DOTPATH; + break; + case FreshRSS_Feed::KIND_JSONFEED: + $outline['type'] = FreshRSS_Export_Service::TYPE_JSONFEED; + break; + } + if ($feed->kind() === FreshRSS_Feed::KIND_HTML_XPATH || $feed->kind() === FreshRSS_Feed::KIND_XML_XPATH) { - switch ($feed->kind()) { - case FreshRSS_Feed::KIND_HTML_XPATH: - $outline['type'] = FreshRSS_Export_Service::TYPE_HTML_XPATH; - break; - case FreshRSS_Feed::KIND_XML_XPATH: - $outline['type'] = FreshRSS_Export_Service::TYPE_XML_XPATH; - break; - } /** @var array */ $xPathSettings = $feed->attributeArray('xpath') ?? []; $outline['frss:xPathItem'] = $xPathSettings['item'] ?? null; @@ -41,6 +48,19 @@ function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array { $outline['frss:xPathItemThumbnail'] = $xPathSettings['itemThumbnail'] ?? null; $outline['frss:xPathItemCategories'] = $xPathSettings['itemCategories'] ?? null; $outline['frss:xPathItemUid'] = $xPathSettings['itemUid'] ?? null; + } elseif ($feed->kind() === FreshRSS_Feed::KIND_JSON_DOTPATH) { + /** @var array */ + $jsonSettings = $feed->attributeArray('json_dotpath') ?? []; + $outline['frss:jsonItem'] = $jsonSettings['item'] ?? null; + $outline['frss:jsonItemTitle'] = $jsonSettings['itemTitle'] ?? null; + $outline['frss:jsonItemContent'] = $jsonSettings['itemContent'] ?? null; + $outline['frss:jsonItemUri'] = $jsonSettings['itemUri'] ?? null; + $outline['frss:jsonItemAuthor'] = $jsonSettings['itemAuthor'] ?? null; + $outline['frss:jsonItemTimestamp'] = $jsonSettings['itemTimestamp'] ?? null; + $outline['frss:jsonItemTimeformat'] = $jsonSettings['itemTimeformat'] ?? null; + $outline['frss:jsonItemThumbnail'] = $jsonSettings['itemThumbnail'] ?? null; + $outline['frss:jsonItemCategories'] = $jsonSettings['itemCategories'] ?? null; + $outline['frss:jsonItemUid'] = $jsonSettings['itemUid'] ?? null; } if (!empty($feed->filtersAction('read'))) { @@ -60,8 +80,30 @@ function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array { $outline['frss:cssFullContentFilter'] = $feed->attributeString('path_entries_filter'); } - // Remove null attributes - $outline = array_filter($outline, static function (?string $value) { return $value !== null; }); + $curl_params = $feed->attributeArray('curl_params'); + if (!empty($curl_params)) { + $outline['frss:CURLOPT_COOKIE'] = $curl_params[CURLOPT_COOKIE] ?? null; + $outline['frss:CURLOPT_COOKIEFILE'] = $curl_params[CURLOPT_COOKIEFILE] ?? null; + $outline['frss:CURLOPT_FOLLOWLOCATION'] = $curl_params[CURLOPT_FOLLOWLOCATION] ?? null; + $outline['frss:CURLOPT_MAXREDIRS'] = $curl_params[CURLOPT_MAXREDIRS] ?? null; + $outline['frss:CURLOPT_POST'] = $curl_params[CURLOPT_POST] ?? null; + $outline['frss:CURLOPT_POSTFIELDS'] = $curl_params[CURLOPT_POSTFIELDS] ?? null; + $outline['frss:CURLOPT_PROXY'] = $curl_params[CURLOPT_PROXY] ?? null; + $outline['frss:CURLOPT_PROXYTYPE'] = $curl_params[CURLOPT_PROXYTYPE] ?? null; + $outline['frss:CURLOPT_USERAGENT'] = $curl_params[CURLOPT_USERAGENT] ?? null; + + if (!empty($curl_params[CURLOPT_HTTPHEADER]) && is_array($curl_params[CURLOPT_HTTPHEADER])) { + $headers = ''; + foreach ($curl_params[CURLOPT_HTTPHEADER] as $header) { + $headers .= $header . "\n"; + } + $headers = trim($headers); + $outline['frss:CURLOPT_HTTPHEADER'] = $headers; + } + } + + // Remove null or invalid attributes + $outline = array_filter($outline, static function (mixed $value) { return (is_string($value) || is_int($value) || is_bool($value)) && $value !== ''; }); $outlines[] = $outline; } diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml index 5d4f1cc4b..13a751c09 100644 --- a/app/views/helpers/feed/update.phtml +++ b/app/views/helpers/feed/update.phtml @@ -407,6 +407,8 @@ + + @@ -505,6 +507,90 @@ + +
+ feed->attributeArray('json_dotpath') ?? []); + ?> +

+
+ +
+ +

+
+
+
+ +
+ +
+
+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +
+
+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
@@ -613,6 +699,29 @@
+
+ +
+ +
+ +
+

+
+
+
diff --git a/app/views/subscription/add.phtml b/app/views/subscription/add.phtml index 2dfc8c705..fad1ee0ff 100644 --- a/app/views/subscription/add.phtml +++ b/app/views/subscription/add.phtml @@ -71,6 +71,8 @@ + +
@@ -164,6 +166,93 @@ +
+

+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +
+
+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +
+
+
+ +
+ +

+
+
+
+ +
+ +

+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
@@ -231,6 +320,22 @@ +
+ +
+ +
+ +
+

+
+
+
diff --git a/docs/en/developers/OPML.md b/docs/en/developers/OPML.md index 5191592a8..0cc7cabbf 100644 --- a/docs/en/developers/OPML.md +++ b/docs/en/developers/OPML.md @@ -44,6 +44,44 @@ The following attributes are using similar naming conventions than [RSS-Bridge]( * `frss:xPathItemCategories`: XPath expression for extracting a list of categories (tags) from the item context. * `frss:xPathItemUid`: XPath expression for extracting an item’s unique ID from the item context. If left empty, a hash is computed automatically. +### JSON+DotPath + +* `,string,mixed}> + */ + public function provideJsonDots(): Traversable { + $json = << $array + */ + public function testJsonDots(array $array, string $key, mixed $expected): void { + $value = FreshRSS_dotpath_Util::get($array, $key); + self::assertEquals($expected, $value); + } +} -- cgit v1.2.3