PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / libraries / adapter / rss / reader.php

reader.php in VikBooking Hotel Booking Engine & PMS trunk, at libraries/adapter/rss/reader.php

304 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.rss
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.rss.feed');
15 JLoader::import('adapter.rss.optinexception');
16
17 /**
18 * Helper class used to read the RSS feed provided by VikWP.
19 *
20 * @since 10.1.31
21 */
22 class JRssReader
23 {
24 /**
25 * A list of instances.
26 *
27 * @var array
28 */
29 protected static $instances = array();
30
31 /**
32 * A list of feed channels (URL).
33 *
34 * @var array
35 */
36 protected $channels;
37
38 /**
39 * The name of the callee;
40 *
41 * @var string
42 */
43 protected $plugin;
44
45 /**
46 * Returns the singleton of this class.
47 *
48 * @param mixed $url Either the feed URL or an array.
49 * @param string $plugin The plugin that called this class. If not
50 * specified, it will be taken from the request.
51 *
52 * @return self The instance of this object.
53 */
54 public static function getInstance($url, $plugin = null)
55 {
56 if (!$plugin)
57 {
58 // try to recover plugin name from request if not specified
59 $plugin = JFactory::getApplication()->input->get('option', 'vikplugin');
60 }
61
62 // serialize arguments
63 $sign = serialize(func_get_args());
64
65 // check whether the instance already exists
66 if (!isset(static::$instances[$sign]))
67 {
68 // instantiate only once
69 static::$instances[$sign] = new static($url, $plugin);
70 }
71
72 return static::$instances[$sign];
73 }
74
75 /**
76 * Class constructor.
77 *
78 * @param mixed $url Either the feed URL or an array.
79 * @param string $plugin The plugin that called this class.
80 */
81 protected function __construct($url, $plugin)
82 {
83 $this->channels = (array) $url;
84 $this->plugin = (string) $plugin;
85 }
86
87 /**
88 * Choose whether to opt in to the RSS feed services.
89 *
90 * @param boolean $status True to opt in, false to the decline.
91 *
92 * @return self This object to support chaining.
93 */
94 public function optIn($status = true)
95 {
96 $user = JFactory::getUser();
97
98 if ($user->guest)
99 {
100 // throw exception in case of guest user
101 throw new Exception('Guest users cannot opt-in to the RSS service', 403);
102 }
103
104 if ($status)
105 {
106 // register opt-in date
107 $status = JDate::getInstance()->toSql();
108 }
109 else
110 {
111 // service declined
112 $status = 0;
113 }
114
115 // register user choice
116 update_user_meta($user->id, $this->plugin . '_rss_optin', $status);
117
118 return $this;
119 }
120
121 /**
122 * Checks whether the user opted in the RSS feed services.
123 *
124 * @param string $date True to return the opt-in date.
125 *
126 * @return mixed $status True if opted in, false if declined.
127 * A JDate instance in case $date is true.
128 *
129 * @throws Exception In case the user didn't decide yet.
130 */
131 public function optedIn($date = false)
132 {
133 $user = JFactory::getUser();
134
135 if ($user->guest)
136 {
137 // ignore choice of guest users
138 return false;
139 }
140
141 // retrieve user choice
142 $choice = get_user_meta($user->id, $this->plugin . '_rss_optin', true);
143
144 // make sure a choice was made
145 if ($choice === false || $choice === '')
146 {
147 // missing choice, throw exception
148 throw new JRssOptInException();
149 }
150
151 if ($date && $choice)
152 {
153 // return opt-in date
154 return new JDate($choice);
155 }
156
157 return (bool) $choice;
158 }
159
160 /**
161 * Returns a list of RSS permalinks.
162 *
163 * @return array
164 */
165 public function getChannels()
166 {
167 return $this->channels;
168 }
169
170 /**
171 * Download a list of feeds from the registered channels.
172 *
173 * @param array $options A configuration array.
174 * - start the starting index to take feeds;
175 * - limit the number of feeds to take;
176 * - new true to retrieve only feeds never seen;
177 * - order the sorting mode (asc or desc).
178 *
179 * @return JRssFeed[] An array of feeds.
180 */
181 public function download(array $options = array())
182 {
183 // get opt-in date
184 $optinDate = $this->optedIn(true);
185
186 // make sure the user opted in the RSS service
187 if (!$optinDate)
188 {
189 // authorization denied, do not go ahead
190 throw new Exception('Missing RSS authorization', 403);
191 }
192
193 $feeds = [];
194
195 /**
196 * Since WP 6.9 and SimplePie 1.9, multiple channels are no longer accepted.
197 * We should rather instantiate a single object per feed and then merge them
198 * together through SimplePie::merge_items().
199 *
200 * @since 10.1.67
201 */
202 foreach ($this->channels as $channel)
203 {
204 // read feed channels
205 $feed = fetch_feed($channel);
206
207 if ($feed instanceof WP_Error)
208 {
209 // get SimplePie error
210 $error = $feed->get_error_message();
211
212 // extract first element as long as error is an array
213 while (is_array($error))
214 {
215 $error = array_shift($error);
216 }
217
218 // something went wrong, propagate error
219 throw new Exception($error, (int) $feed->get_error_code());
220 }
221
222 $feeds[] = $feed;
223 }
224
225 $list = array();
226
227 $start = isset($options['start']) ? abs($options['start']) : 0;
228 $limit = isset($options['limit']) ? abs($options['limit']) : 0;
229
230 // check if we should retrive only the visible feeds
231 $strict = isset($options['new']) ? (bool) $options['new'] : false;
232
233 // check if we should retrieve the feeds in ascending order
234 $ordering = isset($options['order']) ? strtolower($options['order']) : 'desc';
235
236 if ($strict || $ordering == 'asc')
237 {
238 // take all the items without limits because
239 // we need to check whether the feeds are visible
240 // $items = $feed->get_items();
241 $items = \SimplePie\SimplePie::merge_items($feeds);
242
243 if ($ordering == 'asc')
244 {
245 // load items in reverse order from oldest to newest
246 $items = array_reverse($items);
247 }
248 }
249 else
250 {
251 // take the feeds according to the specified limits
252 // $items = $feed->get_items($start, $limit);
253 $items = \SimplePie\SimplePie::merge_items($feeds, $start, $limit);
254 }
255
256 // iterate multi-feed property to scan all the supported feeds
257 foreach ($items as $data)
258 {
259 try
260 {
261 // encapsulate feed data
262 $feed = new JRssFeed($data, $this->plugin);
263
264 // check if we should take only visible feeds
265 if (!$strict || ($feed->isVisible() && $optinDate < $feed->date))
266 {
267 // take feed
268 $list[] = $feed;
269 }
270 }
271 catch (Throwable $error)
272 {
273 // an error has occurred while trying to initialize the feed
274 continue;
275 }
276 }
277
278 if ($strict)
279 {
280 // prepare base arguments for array_splice
281 $args = array(&$list, $start);
282
283 if ($limit)
284 {
285 // consider limit only if specified, because
286 // array_splice take care of the number of
287 // arguments instead of on their values
288 $args[] = $limit;
289 }
290
291 // take only the feeds inside the specified range
292 $list = call_user_func_array('array_splice', $args);
293 }
294
295 if ($limit == 1)
296 {
297 // take directly the first element
298 return array_shift($list);
299 }
300
301 return $list;
302 }
303 }
304