aboutsummaryrefslogtreecommitdiff
path: root/app/views/helpers/export/opml.phtml
blob: cc89edb49b1719e96f85601350d0b11372afa2d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<?php
declare(strict_types=1);

/**
 * @param array<FreshRSS_Feed> $feeds
 * @return list<array<string,string|bool|int>>
 */
function feedsToOutlines(array $feeds, bool $excludeMutedFeeds = false): array {
	$outlines = [];
	foreach ($feeds as $feed) {
		if ($feed->mute() && $excludeMutedFeeds) {
			continue;
		}

		$outline = [
			'text' => htmlspecialchars_decode($feed->name(), ENT_QUOTES),
			'type' => FreshRSS_Export_Service::TYPE_RSS_ATOM,
			'xmlUrl' => htmlspecialchars_decode($feed->url(), ENT_QUOTES),
			'htmlUrl' => htmlspecialchars_decode($feed->website(), ENT_QUOTES),
			'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_DOTNOTATION:
				$outline['type'] = FreshRSS_Export_Service::TYPE_JSON_DOTNOTATION;
				break;
			case FreshRSS_Feed::KIND_JSONFEED:
				$outline['type'] = FreshRSS_Export_Service::TYPE_JSONFEED;
				break;
			case FreshRSS_Feed::KIND_HTML_XPATH_JSON_DOTNOTATION:
				$outline['type'] = FreshRSS_Export_Service::TYPE_HTML_XPATH_JSON_DOTNOTATION;
				break;
		}

		$outline['frss:priority'] = match ($feed->priority()) {
			FreshRSS_Feed::PRIORITY_IMPORTANT => FreshRSS_Export_Service::PRIORITY_IMPORTANT,
			FreshRSS_Feed::PRIORITY_MAIN_STREAM => null,	// Default
			FreshRSS_Feed::PRIORITY_CATEGORY => FreshRSS_Export_Service::PRIORITY_CATEGORY,
			FreshRSS_Feed::PRIORITY_FEED => FreshRSS_Export_Service::PRIORITY_FEED,
			FreshRSS_Feed::PRIORITY_HIDDEN => FreshRSS_Export_Service::PRIORITY_HIDDEN,
			default => null,
		};

		if ($feed->attributeString('unicityCriteria') != '' && $feed->attributeString('unicityCriteria') !== 'id') {
			$outline['frss:unicityCriteria'] = $feed->attributeString('unicityCriteria');
		}

		if ($feed->attributeBoolean('unicityCriteriaForced')) {
			$outline['frss:unicityCriteriaForced'] = 'true';
		}

		if ($feed->kind() === FreshRSS_Feed::KIND_HTML_XPATH || $feed->kind() === FreshRSS_Feed::KIND_XML_XPATH) {
			/** @var array<string,string> */
			$xPathSettings = $feed->attributeArray('xpath') ?? [];
			$outline['frss:xPathItem'] = $xPathSettings['item'] ?? null;
			$outline['frss:xPathItemTitle'] = $xPathSettings['itemTitle'] ?? null;
			$outline['frss:xPathItemContent'] = $xPathSettings['itemContent'] ?? null;
			$outline['frss:xPathItemUri'] = $xPathSettings['itemUri'] ?? null;
			$outline['frss:xPathItemAuthor'] = $xPathSettings['itemAuthor'] ?? null;
			$outline['frss:xPathItemTimestamp'] = $xPathSettings['itemTimestamp'] ?? null;
			$outline['frss:xPathItemTimeFormat'] = $xPathSettings['itemTimeFormat'] ?? null;
			$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_DOTNOTATION || $feed->kind() === FreshRSS_Feed::KIND_HTML_XPATH_JSON_DOTNOTATION) {
			/** @var array<string,string> */
			$jsonSettings = $feed->attributeArray('json_dotnotation') ?? [];
			$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 ($feed->kind() === FreshRSS_Feed::KIND_HTML_XPATH_JSON_DOTNOTATION) {
				$outline['frss:xPathToJson'] = $feed->attributeString('xPathToJson');
			}
		}

		if (!empty($feed->filtersAction('read'))) {
			$filters = '';
			foreach ($feed->filtersAction('read') as $filterRead) {
				$filters .= $filterRead->toString(expandUserQueries: false) . "\n";
			}
			$filters = trim($filters);
			$outline['frss:filtersActionRead'] = $filters;
		}

		if ($feed->pathEntries() != '') {
			$outline['frss:cssFullContent'] = htmlspecialchars_decode($feed->pathEntries(), ENT_QUOTES);
		}

		if (!empty($feed->attributeArray('path_entries_conditions'))) {
			$conditions = '';
			foreach ($feed->attributeArray('path_entries_conditions') as $condition) {
				if (is_string($condition)) {
					$conditions .= $condition . "\n";
				}
			}
			$conditions = trim($conditions);
			$outline['frss:cssFullContentConditions'] = $conditions;
		}

		if ($feed->attributeString('path_entries_filter') != '') {
			$outline['frss:cssContentFilter'] = $feed->attributeString('path_entries_filter');
		}

		$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;
			if ($outline['frss:CURLOPT_PROXYTYPE'] === 3) {	// Legacy for NONE
				$outline['frss:CURLOPT_PROXYTYPE'] = -1;
			}
			$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) {
					if (is_string($header)) {
						$headers .= $header . "\n";
					}
				}
				$headers = trim($headers);
				$outline['frss:CURLOPT_HTTPHEADER'] = $headers;
			}
		}

		// Remove null or invalid attributes
		$outline = array_filter($outline, static function ($value) { return (is_string($value) || is_int($value) || is_bool($value)) && $value !== ''; });

		$outlines[] = $outline;
	}

	return $outlines;
}

/** @var FreshRSS_View $this */

$opml_array = [
	'namespaces' => [
		'frss' => FreshRSS_Export_Service::FRSS_NAMESPACE,
	],
	'head' => [
		'title' => FreshRSS_Context::systemConf()->title,
		'dateCreated' => new DateTime(),
	],
	'body' => [],
];

if (!empty($this->categories)) {
	foreach ($this->categories as $cat) {
		$outline = [
			'text' => htmlspecialchars_decode($cat->name(), ENT_QUOTES),
			'@outlines' => feedsToOutlines($cat->feeds(), $this->excludeMutedFeeds),
		];

		if ($cat->kind() === FreshRSS_Category::KIND_DYNAMIC_OPML) {
			$outline['frss:opmlUrl'] = $cat->attributeString('opml_url');
		}

		$opml_array['body'][] = $outline;
	}
}

if (!empty($this->feeds)) {
	$opml_array['body'] = array_merge($opml_array['body'], feedsToOutlines($this->feeds, $this->excludeMutedFeeds));
}

$libopml = new \marienfressinaud\LibOpml\LibOpml(true);
$opml = $libopml->render($opml_array);
/** @var string $opml */
echo $opml;