aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2022-10-09 15:43:30 +0200
committerGravatar GitHub <noreply@github.com> 2022-10-09 15:43:30 +0200
commit648a876d77a7591eac6c4ad5f14b30988e18ce20 (patch)
tree5832c068fd7d4d0a1fa80096849dde18a25887d4
parent72265c1eca41424f761246abc5a7fe84fd8bc0f9 (diff)
Add support for custom XPath date/time format (#4703)
* Add support for custom XPath date/time format #fix https://github.com/FreshRSS/FreshRSS/issues/4701 Improvement of https://github.com/FreshRSS/FreshRSS/pull/4220 * Format is not XPath * Remove TODOs in en-GB
-rwxr-xr-xapp/Controllers/feedController.php1
-rw-r--r--app/Controllers/subscriptionController.php1
-rw-r--r--app/Models/Feed.php7
-rw-r--r--app/Services/ImportService.php1
-rw-r--r--app/i18n/cz/sub.php4
-rw-r--r--app/i18n/de/sub.php4
-rw-r--r--app/i18n/en-us/sub.php4
-rw-r--r--app/i18n/en/conf.php44
-rw-r--r--app/i18n/en/gen.php6
-rw-r--r--app/i18n/en/sub.php18
-rwxr-xr-xapp/i18n/es/sub.php4
-rw-r--r--app/i18n/fr/sub.php4
-rw-r--r--app/i18n/he/sub.php4
-rw-r--r--app/i18n/it/sub.php4
-rw-r--r--app/i18n/ja/sub.php4
-rw-r--r--app/i18n/ko/sub.php4
-rw-r--r--app/i18n/nl/sub.php4
-rw-r--r--app/i18n/oc/sub.php4
-rw-r--r--app/i18n/pl/sub.php4
-rw-r--r--app/i18n/pt-br/sub.php4
-rw-r--r--app/i18n/ru/sub.php4
-rw-r--r--app/i18n/sk/sub.php4
-rw-r--r--app/i18n/tr/sub.php4
-rw-r--r--app/i18n/zh-cn/sub.php4
-rw-r--r--app/i18n/zh-tw/sub.php4
-rw-r--r--app/views/helpers/export/opml.phtml1
-rw-r--r--app/views/helpers/feed/update.phtml9
-rw-r--r--app/views/subscription/add.phtml8
-rw-r--r--docs/en/developers/OPML.md1
29 files changed, 137 insertions, 32 deletions
diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index 7030c27c1..09b5ed88c 100755
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -211,6 +211,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
if (Minz_Request::param('xPathItemUri', '') != '') $xPathSettings['itemUri'] = Minz_Request::param('xPathItemUri', '', true);
if (Minz_Request::param('xPathItemAuthor', '') != '') $xPathSettings['itemAuthor'] = Minz_Request::param('xPathItemAuthor', '', true);
if (Minz_Request::param('xPathItemTimestamp', '') != '') $xPathSettings['itemTimestamp'] = Minz_Request::param('xPathItemTimestamp', '', true);
+ if (Minz_Request::param('xPathItemTimeFormat', '') != '') $xPathSettings['itemTimeFormat'] = Minz_Request::param('xPathItemTimeFormat', '', true);
if (Minz_Request::param('xPathItemThumbnail', '') != '') $xPathSettings['itemThumbnail'] = Minz_Request::param('xPathItemThumbnail', '', true);
if (Minz_Request::param('xPathItemCategories', '') != '') $xPathSettings['itemCategories'] = Minz_Request::param('xPathItemCategories', '', true);
if (Minz_Request::param('xPathItemUid', '') != '') $xPathSettings['itemUid'] = Minz_Request::param('xPathItemUid', '', true);
diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php
index eab45db26..a3a55293a 100644
--- a/app/Controllers/subscriptionController.php
+++ b/app/Controllers/subscriptionController.php
@@ -213,6 +213,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
if (Minz_Request::param('xPathItemUri', '') != '') $xPathSettings['itemUri'] = Minz_Request::param('xPathItemUri', '', true);
if (Minz_Request::param('xPathItemAuthor', '') != '') $xPathSettings['itemAuthor'] = Minz_Request::param('xPathItemAuthor', '', true);
if (Minz_Request::param('xPathItemTimestamp', '') != '') $xPathSettings['itemTimestamp'] = Minz_Request::param('xPathItemTimestamp', '', true);
+ if (Minz_Request::param('xPathItemTimeFormat', '') != '') $xPathSettings['itemTimeFormat'] = Minz_Request::param('xPathItemTimeFormat', '', true);
if (Minz_Request::param('xPathItemThumbnail', '') != '') $xPathSettings['itemThumbnail'] = Minz_Request::param('xPathItemThumbnail', '', true);
if (Minz_Request::param('xPathItemCategories', '') != '') $xPathSettings['itemCategories'] = Minz_Request::param('xPathItemCategories', '', true);
if (Minz_Request::param('xPathItemUid', '') != '') $xPathSettings['itemUid'] = Minz_Request::param('xPathItemUid', '', true);
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index a81f1c561..5efe55006 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -612,6 +612,7 @@ class FreshRSS_Feed extends Minz_Model {
$xPathItemUri = $xPathSettings['itemUri'] ?? '';
$xPathItemAuthor = $xPathSettings['itemAuthor'] ?? '';
$xPathItemTimestamp = $xPathSettings['itemTimestamp'] ?? '';
+ $xPathItemTimeFormat = $xPathSettings['itemTimeFormat'] ?? '';
$xPathItemThumbnail = $xPathSettings['itemThumbnail'] ?? '';
$xPathItemCategories = $xPathSettings['itemCategories'] ?? '';
$xPathItemUid = $xPathSettings['itemUid'] ?? '';
@@ -652,6 +653,12 @@ class FreshRSS_Feed extends Minz_Model {
$item['link'] = $xPathItemUri == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemUri . ')', $node);
$item['author'] = $xPathItemAuthor == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemAuthor . ')', $node);
$item['timestamp'] = $xPathItemTimestamp == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemTimestamp . ')', $node);
+ if ($xPathItemTimeFormat != '') {
+ $dateTime = DateTime::createFromFormat($xPathItemTimeFormat, $item['timestamp'] ?? '');
+ if ($dateTime != false) {
+ $item['timestamp'] = $dateTime->format(DateTime::ATOM);
+ }
+ }
$item['thumbnail'] = $xPathItemThumbnail == '' ? '' : @$xpath->evaluate('normalize-space(' . $xPathItemThumbnail . ')', $node);
if ($xPathItemCategories != '') {
$itemCategories = @$xpath->query($xPathItemCategories, $node);
diff --git a/app/Services/ImportService.php b/app/Services/ImportService.php
index 4910e9cc4..a4ef547ba 100644
--- a/app/Services/ImportService.php
+++ b/app/Services/ImportService.php
@@ -174,6 +174,7 @@ class FreshRSS_Import_Service {
case 'xPathItemUri': $xPathSettings['itemUri'] = $value['value']; break;
case 'xPathItemAuthor': $xPathSettings['itemAuthor'] = $value['value']; break;
case 'xPathItemTimestamp': $xPathSettings['itemTimestamp'] = $value['value']; break;
+ case 'xPathItemTimeFormat': $xPathSettings['itemTimeFormat'] = $value['value']; break;
case 'xPathItemThumbnail': $xPathSettings['itemThumbnail'] = $value['value']; break;
case 'xPathItemCategories': $xPathSettings['itemCategories'] = $value['value']; break;
case 'xPathItemUid': $xPathSettings['itemUid'] = $value['value']; break;
diff --git a/app/i18n/cz/sub.php b/app/i18n/cz/sub.php
index 8734a559f..e5c2b1f1c 100644
--- a/app/i18n/cz/sub.php
+++ b/app/i18n/cz/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'náhled položky',
'help' => 'Příklad: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'datum položky',
'help' => 'Výsledek bude zpracován pomocí <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php
index f45c8f021..a2af5bc03 100644
--- a/app/i18n/de/sub.php
+++ b/app/i18n/de/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'Artikel-Vorschaubild',
'help' => 'Beispiel: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'Artikel-Datum',
'help' => 'Das Ergebnis wird durch <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a> geparst',
diff --git a/app/i18n/en-us/sub.php b/app/i18n/en-us/sub.php
index b0c6c8a1b..8b7e17a7e 100644
--- a/app/i18n/en-us/sub.php
+++ b/app/i18n/en-us/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'item thumbnail', // IGNORE
'help' => 'Example: <code>descendant::img/@src</code>', // IGNORE
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // IGNORE
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // IGNORE
+ ),
'item_timestamp' => array(
'_' => 'item date', // IGNORE
'help' => 'The result will be parsed by <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>', // IGNORE
diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php
index adc35d0c7..c8ee78947 100644
--- a/app/i18n/en/conf.php
+++ b/app/i18n/en/conf.php
@@ -68,9 +68,9 @@ return array(
),
'logs' => array(
'loglist' => array(
- 'level' => 'Log Level', // TODO
- 'message' => 'Log Message', // TODO
- 'timestamp' => 'Timestamp', // TODO
+ 'level' => 'Log Level',
+ 'message' => 'Log Message',
+ 'timestamp' => 'Timestamp',
),
'pagination' => array(
'first' => 'First',
@@ -139,28 +139,28 @@ return array(
'always_show_favorites' => 'Show all articles in favourites by default',
'article' => array(
'authors_date' => array(
- '_' => 'Authors and date', // TODO
- 'both' => 'In header and footer', // TODO
- 'footer' => 'In footer', // TODO
- 'header' => 'In header', // TODO
- 'none' => 'None', // TODO
+ '_' => 'Authors and date',
+ 'both' => 'In header and footer',
+ 'footer' => 'In footer',
+ 'header' => 'In header',
+ 'none' => 'None',
),
'feed_name' => array(
- 'above_title' => 'Above title/tags', // TODO
- 'none' => 'None', // TODO
- 'with_authors' => 'In authors and date row', // TODO
+ 'above_title' => 'Above title/tags',
+ 'none' => 'None',
+ 'with_authors' => 'In authors and date row',
),
- 'feed_title' => 'Feed title', // TODO
+ 'feed_title' => 'Feed title',
'tags' => array(
- '_' => 'Tags', // TODO
- 'both' => 'In header and footer', // TODO
- 'footer' => 'In footer', // TODO
- 'header' => 'In header', // TODO
- 'none' => 'None', // TODO
+ '_' => 'Tags',
+ 'both' => 'In header and footer',
+ 'footer' => 'In footer',
+ 'header' => 'In header',
+ 'none' => 'None',
),
'tags_max' => array(
- '_' => 'Max number of tags shown', // TODO
- 'help' => '0 means: show all tags and do not collapse them', // TODO
+ '_' => 'Max number of tags shown',
+ 'help' => '0 means: show all tags and do not collapse them',
),
),
'articles_per_page' => 'Number of articles per page',
@@ -171,7 +171,7 @@ return array(
'display_categories_unfolded' => 'Categories to unfold',
'headline' => array(
'articles' => 'Articles: Open/Close',
- 'articles_header_footer' => 'Articles: header/footer', // TODO
+ 'articles_header_footer' => 'Articles: header/footer',
'categories' => 'Left navigation: Categories',
'mark_as_read' => 'Mark article as read',
'misc' => 'Miscellaneous',
@@ -187,7 +187,7 @@ return array(
'article_viewed' => 'when the article is viewed',
'keep_max_n_unread' => 'Max number of articles to keep unread',
'scroll' => 'while scrolling',
- 'upon_gone' => 'when it is no longer in the upstream news feed', // TODO
+ 'upon_gone' => 'when it is no longer in the upstream news feed',
'upon_reception' => 'upon receiving the article',
'when' => 'Mark an article as read…',
'when_same_title' => 'if an identical title already exists in the top <i>n</i> newest articles',
@@ -222,7 +222,7 @@ return array(
'_' => 'Sharing',
'add' => 'Add a sharing method',
'blogotext' => 'Blogotext',
- 'deprecated' => 'This service is deprecated and will be removed from FreshRSS in a <a href="https://freshrss.github.io/FreshRSS/en/users/08_sharing_services.html" title="Open documentation for more information" target="_blank">future release</a>.', // TODO
+ 'deprecated' => 'This service is deprecated and will be removed from FreshRSS in a <a href="https://freshrss.github.io/FreshRSS/en/users/08_sharing_services.html" title="Open documentation for more information" target="_blank">future release</a>.',
'diaspora' => 'Diaspora*',
'email' => 'Email',
'facebook' => 'Facebook',
diff --git a/app/i18n/en/gen.php b/app/i18n/en/gen.php
index 5a6cbc9b9..c052f17fd 100644
--- a/app/i18n/en/gen.php
+++ b/app/i18n/en/gen.php
@@ -18,7 +18,7 @@ return array(
'back_to_rss_feeds' => '← Go back to your RSS feeds',
'cancel' => 'Cancel',
'create' => 'Create',
- 'delete_muted_feeds' => 'Delete muted feeds', // TODO
+ 'delete_muted_feeds' => 'Delete muted feeds',
'demote' => 'Demote',
'disable' => 'Disable',
'empty' => 'Empty',
@@ -32,7 +32,7 @@ return array(
'open_url' => 'Open URL',
'promote' => 'Promote',
'purge' => 'Purge',
- 'refresh_opml' => 'Refresh OPML', // TODO
+ 'refresh_opml' => 'Refresh OPML',
'remove' => 'Remove',
'rename' => 'Rename',
'see_website' => 'See website',
@@ -130,7 +130,7 @@ return array(
'confirm_action_feed_cat' => 'Are you sure you want to perform this action? You will lose related favourites and user queries. It cannot be cancelled!',
'feedback' => array(
'body_new_articles' => 'There are %%d new articles to read on FreshRSS.',
- 'body_unread_articles' => '(unread: %%d)', // TODO
+ 'body_unread_articles' => '(unread: %%d)',
'request_failed' => 'A request has failed, it may have been caused by internet connection problems.',
'title_new_articles' => 'FreshRSS: new articles!',
),
diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php
index c5c15a435..e9b11383e 100644
--- a/app/i18n/en/sub.php
+++ b/app/i18n/en/sub.php
@@ -25,12 +25,12 @@ return array(
'add' => 'Add a category',
'archiving' => 'Archiving',
'dynamic_opml' => array(
- '_' => 'Dynamic OPML', // TODO
- 'help' => 'Provide the URL to an <a href=http://opml.org/ target=_blank>OPML file</a> to dynamically populate this category with feeds', // TODO
+ '_' => 'Dynamic OPML',
+ 'help' => 'Provide the URL to an <a href=http://opml.org/ target=_blank>OPML file</a> to dynamically populate this category with feeds',
),
'empty' => 'Empty category',
'information' => 'Information',
- 'opml_url' => 'OPML URL', // TODO
+ 'opml_url' => 'OPML URL',
'position' => 'Display position',
'position_help' => 'To control category sort order',
'title' => 'Title',
@@ -61,7 +61,7 @@ return array(
'css_path' => 'Article CSS selector on original website',
'css_path_filter' => array(
'_' => 'CSS selector of the elements to remove',
- 'help' => 'A CSS selector may be a list such as: <kbd>.footer, .aside</kbd>', // TODO
+ 'help' => 'A CSS selector may be a list such as: <kbd>.footer, .aside</kbd>',
),
'description' => 'Description',
'empty' => 'This feed is empty. Please verify that it is still maintained.',
@@ -98,6 +98,10 @@ return array(
'_' => 'item thumbnail',
'help' => 'Example: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format',
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>',
+ ),
'item_timestamp' => array(
'_' => 'item date',
'help' => 'The result will be parsed by <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
@@ -107,8 +111,8 @@ return array(
'help' => 'Use in particular the <a href="https://developer.mozilla.org/docs/Web/XPath/Axes" target="_blank">XPath axis</a> <code>descendant::</code> like <code>descendant::h2</code>',
),
'item_uid' => array(
- '_' => 'item unique ID', // TODO
- 'help' => 'Optional. Example: <code>descendant::div/@data-uri</code>', // TODO
+ '_' => 'item unique ID',
+ 'help' => 'Optional. Example: <code>descendant::div/@data-uri</code>',
),
'item_uri' => array(
'_' => 'item link (URL)',
@@ -198,7 +202,7 @@ return array(
'_' => 'Subscription management',
'add' => 'Add a feed or category',
'add_category' => 'Add a category',
- 'add_dynamic_opml' => 'Add dynamic OPML', // TODO
+ 'add_dynamic_opml' => 'Add dynamic OPML',
'add_feed' => 'Add a feed',
'add_label' => 'Add a label',
'delete_label' => 'Delete a label',
diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php
index 7918a9437..75bceed61 100755
--- a/app/i18n/es/sub.php
+++ b/app/i18n/es/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'item thumbnail', // TODO
'help' => 'Example: <code>descendant::img/@src</code>', // TODO
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'item date', // TODO
'help' => 'The result will be parsed by <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>', // TODO
diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php
index f199fefbd..1d6233808 100644
--- a/app/i18n/fr/sub.php
+++ b/app/i18n/fr/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'miniature de l’article',
'help' => 'Exemple : <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Format personnalisé pour interpréter la date',
+ 'help' => 'Optionnel. Un format supporté par <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> comme <code>d-m-Y H:i:s</code>',
+ ),
'item_timestamp' => array(
'_' => 'date de l’article',
'help' => 'Le résultat sera passé à la fonction <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php
index 033a0a7e5..748f13f0b 100644
--- a/app/i18n/he/sub.php
+++ b/app/i18n/he/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'item thumbnail', // TODO
'help' => 'Example: <code>descendant::img/@src</code>', // TODO
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'item date', // TODO
'help' => 'The result will be parsed by <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>', // TODO
diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php
index 93988aa9d..b33c9c237 100644
--- a/app/i18n/it/sub.php
+++ b/app/i18n/it/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'oggetto miniatura',
'help' => 'Esempio: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'oggetto data',
'help' => 'Il risultato verrà analizzato da <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/ja/sub.php b/app/i18n/ja/sub.php
index a6cf81bc3..9de8f07f7 100644
--- a/app/i18n/ja/sub.php
+++ b/app/i18n/ja/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => '項目のサムネイル',
'help' => '例: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => '項目の日付',
'help' => '結果は<a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>によってパースされます',
diff --git a/app/i18n/ko/sub.php b/app/i18n/ko/sub.php
index ef21d9ca1..2ff0347df 100644
--- a/app/i18n/ko/sub.php
+++ b/app/i18n/ko/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => '기사 섬네일',
'help' => '예제: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => '기사 날짜',
'help' => '결과 값은 <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>에서 파싱한 값을 이용합니다.',
diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php
index 85a01a97a..f4f05131e 100644
--- a/app/i18n/nl/sub.php
+++ b/app/i18n/nl/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'miniatuur van bericht',
'help' => 'Voorbeeld: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'datum van bericht',
'help' => 'Het resultaat zal worden geparset door <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/oc/sub.php b/app/i18n/oc/sub.php
index b1869f983..58bbe9ca8 100644
--- a/app/i18n/oc/sub.php
+++ b/app/i18n/oc/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'item vinheta',
'help' => 'Exemple : <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'item data',
'help' => 'Lo resultats serà formatat per la foncion <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/pl/sub.php b/app/i18n/pl/sub.php
index 54c69eb00..1abc85a94 100644
--- a/app/i18n/pl/sub.php
+++ b/app/i18n/pl/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'miniaturki',
'help' => 'Przykład: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'daty',
'help' => 'Wynik zostanie przetworzony za pomocą funkcji <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php
index d8e146b77..b24f76a3b 100644
--- a/app/i18n/pt-br/sub.php
+++ b/app/i18n/pt-br/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'Miniatura do item',
'help' => 'Exemplo: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'Data do Item',
'help' => 'O resultado será parecido com: <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php
index 347162296..929da0d2b 100644
--- a/app/i18n/ru/sub.php
+++ b/app/i18n/ru/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'эскиза элемента',
'help' => 'Пример: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'даты элемента',
'help' => 'Результат будет распарсен с <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>',
diff --git a/app/i18n/sk/sub.php b/app/i18n/sk/sub.php
index 914d6315c..e6324d800 100644
--- a/app/i18n/sk/sub.php
+++ b/app/i18n/sk/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'položka miniatúra',
'help' => 'Príklad: <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'položka dátum',
'help' => 'Výsledok spracuje <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>', // DIRTY
diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php
index 3fb1ff520..a77f60f0e 100644
--- a/app/i18n/tr/sub.php
+++ b/app/i18n/tr/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => 'item thumbnail', // TODO
'help' => 'Example: <code>descendant::img/@src</code>', // TODO
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => 'item date', // TODO
'help' => 'The result will be parsed by <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a>', // TODO
diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php
index ad6307472..89b36ebc5 100644
--- a/app/i18n/zh-cn/sub.php
+++ b/app/i18n/zh-cn/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => '文章缩略图',
'help' => '例如 <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => '文章日期:',
'help' => '结果将被 <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a> 解析',
diff --git a/app/i18n/zh-tw/sub.php b/app/i18n/zh-tw/sub.php
index 8b448cfd8..8afc67716 100644
--- a/app/i18n/zh-tw/sub.php
+++ b/app/i18n/zh-tw/sub.php
@@ -98,6 +98,10 @@ return array(
'_' => '文章縮圖',
'help' => '例如 <code>descendant::img/@src</code>',
),
+ 'item_timeFormat' => array(
+ '_' => 'Custom date/time format', // TODO
+ 'help' => 'Optional. A format supported by <a href="https://php.net/datetime.createfromformat" target="_blank"><code>DateTime::createFromFormat()</code></a> such as <code>d-m-Y H:i:s</code>', // TODO
+ ),
'item_timestamp' => array(
'_' => '文章日期:',
'help' => '結果將被 <a href="https://php.net/strtotime" target="_blank"><code>strtotime()</code></a> 解析',
diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml
index 677bb03b5..1ee030cdf 100644
--- a/app/views/helpers/export/opml.phtml
+++ b/app/views/helpers/export/opml.phtml
@@ -26,6 +26,7 @@ function feedsToOutlines($feeds, $excludeMutedFeeds = false): array {
$outline['frss:xPathItemUri'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemUri'] ?? null];
$outline['frss:xPathItemAuthor'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemAuthor'] ?? null];
$outline['frss:xPathItemTimestamp'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemTimestamp'] ?? null];
+ $outline['frss:xPathItemTimeformat'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemTimeformat'] ?? null];
$outline['frss:xPathItemThumbnail'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemThumbnail'] ?? null];
$outline['frss:xPathItemCategories'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemCategories'] ?? null];
$outline['frss:xPathItemUid'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemUid'] ?? null];
diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml
index 80782fc57..12437d8f0 100644
--- a/app/views/helpers/feed/update.phtml
+++ b/app/views/helpers/feed/update.phtml
@@ -466,6 +466,15 @@
</div>
</div>
<div class="form-group">
+ <label class="group-name" for="xPathItemTimeFormat"><small>
+ <?= _t('sub.feed.kind.html_xpath.item_timeFormat') ?></label>
+ <div class="group-controls">
+ <textarea class="w100" name="xPathItemTimeFormat" id="xPathItemTimeFormat" rows="2" cols="64" spellcheck="false"
+ data-leave-validation="<?= $xpath['itemTimeFormat'] ?? '' ?>"><?= $xpath['itemTimeFormat'] ?? '' ?></textarea>
+ <p class="help"><?= _i('help') ?> <?= _t('sub.feed.kind.html_xpath.item_timeFormat.help') ?></p>
+ </div>
+ </div>
+ <div class="form-group">
<label class="group-name" for="xPathItemCategories"><small><?= _t('sub.feed.kind.html_xpath.relative') ?></small><br />
<?= _t('sub.feed.kind.html_xpath.item_categories') ?></label>
<div class="group-controls">
diff --git a/app/views/subscription/add.phtml b/app/views/subscription/add.phtml
index a2b7f1289..800093bed 100644
--- a/app/views/subscription/add.phtml
+++ b/app/views/subscription/add.phtml
@@ -141,6 +141,14 @@
</div>
</div>
<div class="form-group">
+ <label class="group-name" for="xPathItemTimeFormat">
+ <?= _t('sub.feed.kind.html_xpath.item_timeFormat') ?></label>
+ <div class="group-controls">
+ <textarea name="xPathItemTimeFormat" id="xPathItemTimeFormat" rows="2" cols="64" spellcheck="false"></textarea>
+ <p class="help"><?= _i('help') ?> <?= _t('sub.feed.kind.html_xpath.item_timeFormat.help') ?></p>
+ </div>
+ </div>
+ <div class="form-group">
<label class="group-name" for="xPathItemCategories"><small><?= _t('sub.feed.kind.html_xpath.relative') ?></small><br />
<?= _t('sub.feed.kind.html_xpath.item_categories') ?></label>
<div class="group-controls">
diff --git a/docs/en/developers/OPML.md b/docs/en/developers/OPML.md
index 859d99c72..2190a1de3 100644
--- a/docs/en/developers/OPML.md
+++ b/docs/en/developers/OPML.md
@@ -36,6 +36,7 @@ The following attributes are using similar naming conventions than [RSS-Bridge](
* `frss:xPathItemAuthor`: XPath expression for extracting an item author from the item context.
* Example: `"Anonymous"`
* `frss:xPathItemTimestamp`: XPath expression for extracting an item timestamp from the item context. The result will be parsed by [`strtotime()`](https://php.net/strtotime).
+* `frss:xPathItemTimeFormat`: Date/Time format to parse the timestamp, according to [`DateTime::createFromFormat()`](https://php.net/datetime.createfromformat).
* `frss:xPathItemThumbnail`: XPath expression for extracting an item’s thumbnail (image) URL from the item context.
* Example: `descendant::img/@src`
* `frss:xPathItemCategories`: XPath expression for extracting a list of categories (tags) from the item context.