Browse Source

Fix JSON export/import (#5626)

* Fix import with empty content
fix https://github.com/FreshRSS/FreshRSS/issues/5622
Cherry picks on https://github.com/FreshRSS/FreshRSS/pull/5584

* Fix export of tags / labels
Article-defined tags were wrongly exported as user-defined labels.

* Fix export of tags / labels
Article-defined tags were wrongly exported as user-defined labels.

* Fix bug with many labels

* Better typing

* Comments
Alexandre Alapetite 2 năm trước cách đây
mục cha
commit
1c7c1016f4

+ 8 - 5
app/Controllers/importExportController.php

@@ -309,7 +309,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 		$limits = FreshRSS_Context::$system_conf->limits;
 
 		// First, we check feeds of articles are in DB (and add them if needed).
-		foreach ($items as $item) {
+		foreach ($items as &$item) {
 			if (!isset($item['guid']) && isset($item['id'])) {
 				$item['guid'] = $item['id'];
 			}
@@ -382,7 +382,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 		// Then, articles are imported.
 		$newGuids = [];
 		$this->entryDAO->beginTransaction();
-		foreach ($items as $item) {
+		foreach ($items as &$item) {
 			if (empty($item['guid']) || empty($article_to_feed[$item['guid']])) {
 				// Related feed does not exist for this entry, do nothing.
 				continue;
@@ -427,14 +427,17 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 			} else {
 				$url = '';
 			}
+			if (!is_string($url)) {
+				$url = '';
+			}
 
 			$title = empty($item['title']) ? $url : $item['title'];
 
-			if (!empty($item['content']['content'])) {
+			if (isset($item['content']['content']) && is_string($item['content']['content'])) {
 				$content = $item['content']['content'];
-			} elseif (!empty($item['summary']['content'])) {
+			} elseif (isset($item['summary']['content']) && is_string($item['summary']['content'])) {
 				$content = $item['summary']['content'];
-			} elseif (!empty($item['content'])) {
+			} elseif (isset($item['content']) && is_string($item['content'])) {
 				$content = $item['content'];	//FeedBin
 			} else {
 				$content = '';

+ 6 - 3
app/Models/Entry.php

@@ -846,12 +846,12 @@ HTML;
 
 	/**
 	 * N.B.: To avoid expensive lookups, ensure to set `$entry->_feed($feed)` before calling this function.
-	 * N.B.: You might have to populate `$entry->_tags()` prior to calling this function.
 	 * @param string $mode Set to `'compat'` to use an alternative Unicode representation for problematic HTML special characters not decoded by some clients;
 	 * 	set to `'freshrss'` for using FreshRSS additions for internal use (e.g. export/import).
+	 * @param array<string> $labels List of labels associated to this entry.
 	 * @return array<string,mixed> A representation of this entry in a format compatible with Google Reader API
 	 */
-	public function toGReader(string $mode = ''): array {
+	public function toGReader(string $mode = '', array $labels = []): array {
 
 		$feed = $this->feed();
 		$category = $feed == null ? null : $feed->category();
@@ -935,8 +935,11 @@ HTML;
 		if ($this->isFavorite()) {
 			$item['categories'][] = 'user/-/state/com.google/starred';
 		}
+		foreach ($labels as $labelName) {
+			$item['categories'][] = 'user/-/label/' . htmlspecialchars_decode($labelName, ENT_QUOTES);
+		}
 		foreach ($this->tags() as $tagName) {
-			$item['categories'][] = 'user/-/label/' . htmlspecialchars_decode($tagName, ENT_QUOTES);
+			$item['categories'][] = htmlspecialchars_decode($tagName, ENT_QUOTES);
 		}
 		return $item;
 	}

+ 9 - 4
app/Models/TagDAO.php

@@ -383,7 +383,11 @@ SQL;
 				// Split a query with too many variables parameters
 				$idsChunks = array_chunk($entries, FreshRSS_DatabaseDAO::MAX_VARIABLE_NUMBER);
 				foreach ($idsChunks as $idsChunk) {
-					$values += $this->getTagsForEntries($idsChunk);
+					$valuesChunk = $this->getTagsForEntries($idsChunk);
+					if (!is_array($valuesChunk)) {
+						return false;
+					}
+					$values = array_merge($values, $valuesChunk);
 				}
 				return $values;
 			}
@@ -419,9 +423,10 @@ SQL;
 	}
 
 	/**
-	 * For API
-	 * @param array<FreshRSS_Entry|numeric-string> $entries
-	 * @return array<string,array<string>>
+	 * Produces an array: for each entry ID (prefixed by `e_`), associate a list of labels.
+	 * Used by API and by JSON export, to speed up queries (would be very expensive to perform a label look-up on each entry individually).
+	 * @param array<FreshRSS_Entry|numeric-string> $entries the list of entries for which to retrieve the labels.
+	 * @return array<string,array<string>> An array of the shape `[e_id_entry => ["label 1", "label 2"]]`
 	 */
 	public function getEntryIdsTagNames(array $entries): array {
 		$result = [];

+ 1 - 5
app/views/helpers/export/articles.phtml

@@ -26,11 +26,7 @@ foreach ($this->entries as $entry) {
 	$feed = $this->feed ?? FreshRSS_CategoryDAO::findFeed($this->categories, $entry->feedId());
 	$entry->_feed($feed);
 
-	if (isset($this->entryIdsTagNames['e_' . $entry->id()])) {
-		$entry->_tags($this->entryIdsTagNames['e_' . $entry->id()]);
-	}
-
-	$article = $entry->toGReader('freshrss');
+	$article = $entry->toGReader('freshrss', $this->entryIdsTagNames['e_' . $entry->id()] ?? []);
 
 	$line = json_encode($article, $options);
 	if ($line != '') {

+ 0 - 1
lib/lib_rss.php

@@ -330,7 +330,6 @@ function customSimplePie(array $attributes = array()): SimplePie {
 	return $simplePie;
 }
 
-/** @param string $data */
 function sanitizeHTML(string $data, string $base = '', ?int $maxLength = null): string {
 	if ($data === '' || ($maxLength !== null && $maxLength <= 0)) {
 		return '';

+ 1 - 5
p/api/greader.php

@@ -573,11 +573,7 @@ final class GReaderAPI {
 			}
 			$entry->_feed($feed);
 
-			if (isset($entryIdsTagNames['e_' . $entry->id()])) {
-				$entry->_tags($entryIdsTagNames['e_' . $entry->id()]);
-			}
-
-			$items[] = $entry->toGReader('compat');
+			$items[] = $entry->toGReader('compat', $entryIdsTagNames['e_' . $entry->id()] ?? []);
 		}
 		return $items;
 	}