]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/IO/News.php
Improve coding standards
[galette.git] / galette / lib / Galette / IO / News.php
1 <?php
2
3 /**
4 * Copyright © 2003-2024 The Galette Team
5 *
6 * This file is part of Galette (https://galette.eu).
7 *
8 * Galette is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Galette is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 namespace Galette\IO;
23
24 use Galette\Core\Galette;
25 use Galette\Features\Cacheable;
26 use Throwable;
27 use Analog\Analog;
28
29 /**
30 * News class from rss feed for galette
31 *
32 * @author Johan Cwiklinski <johan@x-tnd.be>
33 */
34 class News
35 {
36 use Cacheable;
37
38 protected string $cache_filename = '%feed.cache';
39 private int $show = 10;
40 private ?string $feed_url = null;
41 /** @var array<int, array<string, string>> */
42 private array $posts = [];
43 /** @var array<string, array<string, int|string>> */
44 private array $stream_opts = [
45 'http' => [
46 'timeout' => 5
47 ]
48 ];
49
50 /**
51 * Default constructor
52 *
53 * @param string $url Feed URL
54 * @param boolean $nocache Do not try to cache
55 */
56 public function __construct(string $url, bool $nocache = false)
57 {
58 $this->feed_url = $this->getFeedURL($url);
59
60 //only if cache should be used
61 if ($nocache === false && !Galette::isDebugEnabled()) {
62 $this->handleCache($nocache);
63 } else {
64 $this->parseFeed();
65 }
66 }
67
68 /**
69 * Get data to cache
70 *
71 * @return string
72 */
73 protected function getDataTocache(): string
74 {
75 return Galette::jsonEncode(
76 $this->posts
77 );
78 }
79
80 /**
81 * Called once cache has been loaded.
82 *
83 * @param mixed $contents Content from cache
84 *
85 * @return bool
86 */
87 protected function cacheLoaded(mixed $contents): bool
88 {
89 if (Galette::isSerialized($contents)) {
90 //legacy cache format
91 $this->posts = unserialize($contents);
92 } else {
93 $this->posts = Galette::jsonDecode($contents);
94 }
95
96 //check if posts were cached
97 if (count($this->posts) == 0) {
98 $this->parseFeed();
99 return false;
100 }
101
102 return true;
103 }
104
105 /**
106 * Complete path to cache file
107 *
108 * @return string
109 */
110 protected function getCacheFilename(): string
111 {
112 return GALETTE_CACHE_DIR . str_replace(
113 '%feed',
114 md5($this->feed_url),
115 $this->cache_filename
116 );
117 }
118
119 /**
120 * Parse feed
121 *
122 * @return void
123 */
124 private function parseFeed(): void
125 {
126 try {
127 if (!$this->allowURLFOpen()) {
128 throw new \RuntimeException(
129 'allow_url_fopen is set to false; cannot load news.'
130 );
131 }
132
133 $context = stream_context_create($this->stream_opts);
134 $data = file_get_contents($this->feed_url, false, $context);
135 if (!$data) {
136 throw new \Exception();
137 }
138
139 $xml = simplexml_load_string($data);
140 if (!$xml) {
141 throw new \Exception();
142 }
143
144 $posts = array();
145
146 if (isset($xml->entry)) {
147 //Reading an atom feed
148 foreach ($xml->entry as $post) {
149 $posts[] = array(
150 'title' => (string)$post->title,
151 'url' => (string)$post->link['href'],
152 'date' => (string)$post->published
153 );
154 if (count($posts) == $this->show) {
155 break;
156 }
157 }
158 } elseif (isset($xml->channel->item)) {
159 //Reading a RSS feed
160 foreach ($xml->channel->item as $post) {
161 $posts[] = array(
162 'title' => (string)$post->title,
163 'url' => (string)$post->link,
164 'date' => (string)$post->pubDate
165 );
166 if (count($posts) == $this->show) {
167 break;
168 }
169 }
170 } else {
171 throw new \RuntimeException(
172 'Unknown feed type!'
173 );
174 }
175 $this->posts = $posts;
176 } catch (Throwable $e) {
177 Analog::log(
178 'Unable to load feed from "' . $this->feed_url .
179 '" :( | ' . $e->getMessage(),
180 Analog::ERROR
181 );
182 }
183 }
184
185 /**
186 * Get posts
187 *
188 * @return array<int, array<string, string>>
189 */
190 public function getPosts(): array
191 {
192 return $this->posts;
193 }
194
195 /**
196 * Get feed url, handle Galette website to check available langs
197 *
198 * @param string $url Requested URL
199 *
200 * @return string
201 */
202 public function getFeedURL(string $url): string
203 {
204 global $i18n;
205
206 if (strpos($url, 'galette.eu') !== false || trim($url) == '') {
207 $url = 'https://galette.eu/site';
208 } elseif (strpos($url, 'localhost:4000') !== false) {
209 $url = 'http://localhost:4000/site';
210 } else {
211 return $url;
212 }
213
214 try {
215 $galette_website_langs = $url . '/langs.json';
216 $context = stream_context_create($this->stream_opts);
217 $langs = json_decode(file_get_contents($galette_website_langs, false, $context));
218
219 if ($i18n->getAbbrev() != 'en' && in_array($i18n->getAbbrev(), $langs)) {
220 $url .= '/' . $i18n->getAbbrev();
221 }
222 $url .= '/feed.xml';
223 } catch (Throwable $e) {
224 Analog::log(
225 'Unable to load feed languages from "' . $url .
226 '" :( | ' . $e->getMessage(),
227 Analog::ERROR
228 );
229 }
230
231 return $url;
232 }
233
234 /**
235 * Check if allow_url_fopen is enabled
236 *
237 * @return boolean
238 */
239 protected function allowURLFOpen(): bool
240 {
241 return ini_get('allow_url_fopen');
242 }
243
244 /**
245 * Ensure data to cache are present
246 *
247 * @return void
248 */
249 protected function prepareForCache(): void
250 {
251 $this->parseFeed();
252 }
253 }