mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-10-31 15:38:48 -04:00
convertJsonToRss() collected item attachments into
$rssItem['attachments'], but FreshRSS_Entry::fromArray() ignores that
key, so attachments from JSON Feed and JSON dot notation feeds never
reached the generated RSS and were lost. Store them in the entry's
`enclosures` attribute instead, HTML-escaped like the other fields.
- Attachments of JSON Feed and JSON dot notation feeds (`itemAttachment`, `itemAttachmentUrl`, `itemAttachmentType`, `itemAttachmentLength`) were collected by `convertJsonToRss()` into `$rssItem['attachments']`, but `FreshRSS_Entry::fromArray()` never reads that key. So the generated RSS had no `<media:content>` for them and the enclosures were silently dropped. For example, a JSON Feed podcast item with `"attachments": [{"url": "https://example.net/1.mp3", "mime_type": "audio/mpeg"}]` showed up without its audio file.
- The attachments are now stored in the entry's `enclosures` attribute, which is what `rss.phtml` renders. The URL and type are HTML-escaped like the other fields, the length is kept only when numeric, and attachments without a URL are skipped.
- Unit test converting a JSON item with an attachment whose URL contains `&`, asserting the `<media:content url="…&…" type="audio/mpeg" length="1234">` element and valid XML. It fails without the fix.
109 lines
3.3 KiB
PHP
109 lines
3.3 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
|
|
final class dotNotationUtilTest extends \PHPUnit\Framework\TestCase {
|
|
|
|
/**
|
|
* @return Traversable<array{array<string,mixed>,string,string}>
|
|
*/
|
|
public static function provideJsonDots(): Traversable {
|
|
$json = <<<json
|
|
{
|
|
"hello": "world",
|
|
"deeper": {
|
|
"hello": "again"
|
|
},
|
|
"items": [
|
|
{
|
|
"meta": {"title": "first"}
|
|
},
|
|
{
|
|
"meta": {"title": "second"}
|
|
}
|
|
]
|
|
}
|
|
json;
|
|
$array = json_decode($json, true);
|
|
|
|
yield [$array, 'hello', 'world'];
|
|
yield [$array, 'deeper.hello', 'again'];
|
|
yield [$array, 'items.0.meta.title', 'first'];
|
|
yield [$array, 'items[0].meta.title', 'first'];
|
|
yield [$array, 'items.1.meta.title', 'second'];
|
|
yield [$array, 'items[1].meta.title', 'second'];
|
|
yield [$array, '"Hello " & hello & \'!\'', 'Hello world!'];
|
|
yield [$array, '"Hello & goodbye " & hello & \'!\'', 'Hello & goodbye world!'];
|
|
yield [$array, '"Hello " & hello & deeper.hello & "!"', 'Hello worldagain!'];
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $array
|
|
*/
|
|
#[DataProvider('provideJsonDots')]
|
|
public static function testJsonDots(array $array, string $key, string $expected): void {
|
|
$value = FreshRSS_dotNotation_Util::get($array, $key);
|
|
self::assertSame($expected, $value);
|
|
}
|
|
|
|
public static function testConvertJsonToRssEscapesCategories(): void {
|
|
FreshRSS_Context::initSystem();
|
|
Minz_Translate::init('en');
|
|
$json = <<<json
|
|
{
|
|
"items": [
|
|
{"title": "First", "url": "https://example.net/1", "categories": ["News & Politics", "Arts & Culture"]},
|
|
{"title": "Second", "url": "https://example.net/2", "categories": "Science & Tech"}
|
|
]
|
|
}
|
|
json;
|
|
$jf = json_decode($json, true);
|
|
self::assertIsArray($jf);
|
|
$rss = FreshRSS_dotNotation_Util::convertJsonToRss($jf, 'https://example.net/', [
|
|
'item' => 'items',
|
|
'itemTitle' => 'title',
|
|
'itemUri' => 'url',
|
|
'itemCategories' => 'categories',
|
|
], 'Test');
|
|
self::assertIsString($rss);
|
|
self::assertStringContainsString('<category>News & Politics</category>', $rss);
|
|
self::assertStringContainsString('<category>Arts & Culture</category>', $rss);
|
|
self::assertStringContainsString('<category>Science & Tech</category>', $rss);
|
|
self::assertNotFalse(simplexml_load_string($rss), 'Output must be valid XML');
|
|
}
|
|
|
|
public static function testConvertJsonToRssKeepsAttachments(): void {
|
|
FreshRSS_Context::initSystem();
|
|
Minz_Translate::init('en');
|
|
$json = <<<json
|
|
{
|
|
"items": [
|
|
{
|
|
"title": "Episode 1",
|
|
"url": "https://example.net/1",
|
|
"attachments": [
|
|
{"url": "https://example.net/1.mp3?a=1&b=2", "mime_type": "audio/mpeg", "size_in_bytes": 1234}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
json;
|
|
$jf = json_decode($json, true);
|
|
self::assertIsArray($jf);
|
|
$rss = FreshRSS_dotNotation_Util::convertJsonToRss($jf, 'https://example.net/', [
|
|
'item' => 'items',
|
|
'itemTitle' => 'title',
|
|
'itemUri' => 'url',
|
|
'itemAttachment' => 'attachments',
|
|
'itemAttachmentUrl' => 'url',
|
|
'itemAttachmentType' => 'mime_type',
|
|
'itemAttachmentLength' => 'size_in_bytes',
|
|
], 'Test');
|
|
self::assertIsString($rss);
|
|
self::assertStringContainsString(
|
|
'<media:content url="https://example.net/1.mp3?a=1&b=2" type="audio/mpeg" length="1234">', $rss);
|
|
self::assertNotFalse(simplexml_load_string($rss), 'Output must be valid XML');
|
|
}
|
|
}
|