diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php
index c016d3584..95bca33eb 100755
--- a/app/Controllers/feedController.php
+++ b/app/Controllers/feedController.php
@@ -198,6 +198,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
if (Minz_Request::param('xPathItemTimestamp', '') != '') $xPathSettings['itemTimestamp'] = Minz_Request::param('xPathItemTimestamp', '', 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);
if (!empty($xPathSettings)) {
$attributes['xpath'] = $xPathSettings;
}
diff --git a/app/Controllers/subscriptionController.php b/app/Controllers/subscriptionController.php
index 9e4eab8e2..bbefc2d68 100644
--- a/app/Controllers/subscriptionController.php
+++ b/app/Controllers/subscriptionController.php
@@ -204,6 +204,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
if (Minz_Request::param('xPathItemTimestamp', '') != '') $xPathSettings['itemTimestamp'] = Minz_Request::param('xPathItemTimestamp', '', 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);
if (!empty($xPathSettings)) {
$feed->_attributes('xpath', $xPathSettings);
}
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
index 4de61167b..710f8b557 100644
--- a/app/Models/Feed.php
+++ b/app/Models/Feed.php
@@ -611,6 +611,7 @@ class FreshRSS_Feed extends Minz_Model {
$xPathItemTimestamp = $xPathSettings['itemTimestamp'] ?? '';
$xPathItemThumbnail = $xPathSettings['itemThumbnail'] ?? '';
$xPathItemCategories = $xPathSettings['itemCategories'] ?? '';
+ $xPathItemUid = $xPathSettings['itemUid'] ?? '';
if ($xPathItem == '') {
return null;
}
@@ -657,8 +658,14 @@ class FreshRSS_Feed extends Minz_Model {
}
}
}
- if ($item['title'] . $item['content'] . $item['link'] != '') {
+ if ($xPathItemUid != '') {
+ $item['guid'] = @$xpath->evaluate('normalize-space(' . $xPathItemUid . ')', $node);
+ }
+ if (empty($item['guid'])) {
$item['guid'] = 'urn:sha1:' . sha1($item['title'] . $item['content'] . $item['link']);
+ }
+
+ if ($item['title'] . $item['content'] . $item['link'] != '') {
$item = Minz_Helper::htmlspecialchars_utf8($item);
$view->entries[] = FreshRSS_Entry::fromArray($item);
}
diff --git a/app/Services/ImportService.php b/app/Services/ImportService.php
index e09da1f15..780dcbd07 100644
--- a/app/Services/ImportService.php
+++ b/app/Services/ImportService.php
@@ -179,6 +179,7 @@ class FreshRSS_Import_Service {
case 'xPathItemTimestamp': $xPathSettings['itemTimestamp'] = $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 7867055c7..6cacf3e8b 100644
--- a/app/i18n/cz/sub.php
+++ b/app/i18n/cz/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/de/sub.php b/app/i18n/de/sub.php
index ff4f11ca3..c8a0c7961 100644
--- a/app/i18n/de/sub.php
+++ b/app/i18n/de/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/en-us/sub.php b/app/i18n/en-us/sub.php
index de5fd9340..648fdec9a 100644
--- a/app/i18n/en-us/sub.php
+++ b/app/i18n/en-us/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // IGNORE
'help' => 'Can also be a static string. Example: "Anonymous"', // IGNORE
),
- 'item_categories' => 'items tags', // IGNORE
+ 'item_categories' => 'item tags', // IGNORE
'item_content' => array(
'_' => 'item content', // IGNORE
'help' => 'Example to take the full item: .', // IGNORE
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // IGNORE
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // IGNORE
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // IGNORE
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // IGNORE
'help' => 'Example: descendant::a/@href', // IGNORE
diff --git a/app/i18n/en/sub.php b/app/i18n/en/sub.php
index 9cc9c61bf..202d89b3c 100644
--- a/app/i18n/en/sub.php
+++ b/app/i18n/en/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author',
'help' => 'Can also be a static string. Example: "Anonymous"',
),
- 'item_categories' => 'items tags',
+ 'item_categories' => 'item tags',
'item_content' => array(
'_' => 'item content',
'help' => 'Example to take the full item: .',
@@ -104,6 +104,10 @@ return array(
'_' => 'item title',
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2',
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)',
'help' => 'Example: descendant::a/@href',
diff --git a/app/i18n/es/sub.php b/app/i18n/es/sub.php
index 90e853f9a..dcf72697f 100755
--- a/app/i18n/es/sub.php
+++ b/app/i18n/es/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/fr/sub.php b/app/i18n/fr/sub.php
index 379bd6296..3faf379c2 100644
--- a/app/i18n/fr/sub.php
+++ b/app/i18n/fr/sub.php
@@ -104,6 +104,10 @@ return array(
'_' => 'titre de l’article',
'help' => 'Utiliser en particulier l’axe XPath descendant:: comme descendant::h2',
),
+ 'item_uid' => array(
+ '_' => 'identifiant unique de l’article',
+ 'help' => 'Optionnel. Exemple : descendant::div/@data-uri',
+ ),
'item_uri' => array(
'_' => 'lien (URL) de l’article',
'help' => 'Exemple : descendant::a/@href',
diff --git a/app/i18n/he/sub.php b/app/i18n/he/sub.php
index bcb540e6b..0d9f2ea96 100644
--- a/app/i18n/he/sub.php
+++ b/app/i18n/he/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/it/sub.php b/app/i18n/it/sub.php
index 1a3875892..c9f4823f0 100644
--- a/app/i18n/it/sub.php
+++ b/app/i18n/it/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/ja/sub.php b/app/i18n/ja/sub.php
index 6ea8fc375..9973e5bec 100644
--- a/app/i18n/ja/sub.php
+++ b/app/i18n/ja/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/ko/sub.php b/app/i18n/ko/sub.php
index 77e72aa0f..edb97842b 100644
--- a/app/i18n/ko/sub.php
+++ b/app/i18n/ko/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/nl/sub.php b/app/i18n/nl/sub.php
index b5442ea0e..b8585bcbb 100644
--- a/app/i18n/nl/sub.php
+++ b/app/i18n/nl/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/oc/sub.php b/app/i18n/oc/sub.php
index 68f9e1f88..28b0de33b 100644
--- a/app/i18n/oc/sub.php
+++ b/app/i18n/oc/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/pl/sub.php b/app/i18n/pl/sub.php
index 07cc07286..603fe8f4a 100644
--- a/app/i18n/pl/sub.php
+++ b/app/i18n/pl/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/pt-br/sub.php b/app/i18n/pt-br/sub.php
index 87b9b635b..82ae085ef 100644
--- a/app/i18n/pt-br/sub.php
+++ b/app/i18n/pt-br/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/ru/sub.php b/app/i18n/ru/sub.php
index dc0cf8eda..9bc64f05d 100644
--- a/app/i18n/ru/sub.php
+++ b/app/i18n/ru/sub.php
@@ -104,6 +104,10 @@ return array(
'_' => 'заголовка элемента',
'help' => 'Используйте, в частности, ось XPath descendant::, наподобие descendant::h2',
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'ссылки элемента (URL)',
'help' => 'Пример: descendant::a/@href',
diff --git a/app/i18n/sk/sub.php b/app/i18n/sk/sub.php
index 6660ff573..58999e5f3 100644
--- a/app/i18n/sk/sub.php
+++ b/app/i18n/sk/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/tr/sub.php b/app/i18n/tr/sub.php
index 34169aac3..e0af10d12 100644
--- a/app/i18n/tr/sub.php
+++ b/app/i18n/tr/sub.php
@@ -87,7 +87,7 @@ return array(
'_' => 'item author', // TODO
'help' => 'Can also be a static string. Example: "Anonymous"', // TODO
),
- 'item_categories' => 'items tags', // TODO
+ 'item_categories' => 'item tags', // TODO
'item_content' => array(
'_' => 'item content', // TODO
'help' => 'Example to take the full item: .', // TODO
@@ -104,6 +104,10 @@ return array(
'_' => 'item title', // TODO
'help' => 'Use in particular the XPath axis descendant:: like descendant::h2', // TODO
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => 'item link (URL)', // TODO
'help' => 'Example: descendant::a/@href', // TODO
diff --git a/app/i18n/zh-cn/sub.php b/app/i18n/zh-cn/sub.php
index 7ede19a3d..10211b99e 100644
--- a/app/i18n/zh-cn/sub.php
+++ b/app/i18n/zh-cn/sub.php
@@ -104,6 +104,10 @@ return array(
'_' => '文章标题',
'help' => '特别是用 XPath 轴 descendant:: 像 descendant::h2',
),
+ 'item_uid' => array(
+ '_' => 'item unique ID', // TODO
+ 'help' => 'Optional. Example: descendant::div/@data-uri', // TODO
+ ),
'item_uri' => array(
'_' => '文章链接 (URL)',
'help' => '例: descendant::a/@href',
diff --git a/app/views/helpers/export/opml.phtml b/app/views/helpers/export/opml.phtml
index c401b05bd..677bb03b5 100644
--- a/app/views/helpers/export/opml.phtml
+++ b/app/views/helpers/export/opml.phtml
@@ -28,6 +28,7 @@ function feedsToOutlines($feeds, $excludeMutedFeeds = false): array {
$outline['frss:xPathItemTimestamp'] = ['namespace' => FreshRSS_Export_Service::FRSS_NAMESPACE, 'value' => $xPathSettings['itemTimestamp'] ?? 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];
}
if (!empty($feed->filtersAction('read'))) {
$filters = '';
diff --git a/app/views/helpers/feed/update.phtml b/app/views/helpers/feed/update.phtml
index 0e4a34a76..02dcb29e6 100644
--- a/app/views/helpers/feed/update.phtml
+++ b/app/views/helpers/feed/update.phtml
@@ -475,6 +475,14 @@
data-leave-validation="= $xpath['itemCategories'] ?? '' ?>">= $xpath['itemCategories'] ?? '' ?>
+