Passed
Push — templating ( 0cf8c2...d1ec7e )
by Daniel
03:46
created

feeds::get_feed_items()   B

Complexity

Conditions 6
Paths 20

Size

Total Lines 46
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 6
eloc 20
nc 20
nop 6
dl 0
loc 46
rs 8.9777
c 2
b 0
f 1
1
<?php
2
3
/**
4
 *
5
 * @package sitemaker
6
 * @copyright (c) 2019 Daniel A. (blitze)
7
 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
8
 *
9
 */
10
11
namespace blitze\sitemaker\blocks;
12
13
use blitze\sitemaker\services\blocks\driver\block;
14
15
/**
16
 * Feeds Block
17
 */
18
class feeds extends block
19
{
20
	/** @var \phpbb\language\language */
21
	protected $translator;
22
23
	/** @var \phpbb\request\request_interface */
24
	protected $request;
25
26
	/** @var \phpbb\template\twig\environment */
27
	protected $twig;
28
29
	/** @var string */
30
	protected $cache_dir;
31
32
	/** @var array */
33
	protected $feed_fields = ['title', 'description', 'category', 'categories', 'author', 'authors', 'contributor', 'contributors', 'copyright', 'image_url', 'image_title', 'image_link', 'image_width', 'image_height', 'permalink', 'link', 'links'];
34
35
	/** @var array */
36
	protected $item_fields = ['id', 'title', 'description', 'content', 'category', 'categories', 'author', 'authors', 'contributor', 'contributors', 'copyright', 'date', 'updated_date', 'gmdate', 'updated_gmdate', 'permalink', 'link', 'links', 'enclosure', 'enclosures', 'latitude', 'longitude', 'source'];
37
38
	/**
39
	 * Constructor
40
	 *
41
	 * @param \phpbb\language\language				$translator			Language object
42
	 * @param \phpbb\request\request_interface		$request			Request object
43
	 * @param \phpbb\template\twig\environment		$twig				Twig environment
44
	 * @param string 								$cache_dir			Path to cache directory
45
	 */
46
	public function __construct(\phpbb\language\language $translator, \phpbb\request\request_interface $request, \phpbb\template\twig\environment $twig, $cache_dir)
47
	{
48
		$this->translator = $translator;
49
		$this->request = $request;
50
		$this->twig = $twig;
51
		$this->cache_dir = $cache_dir;
52
	}
53
54
	/**
55
	 * {@inheritdoc}
56
	 */
57
	public function get_config(array $settings)
58
	{
59
		$template_default = '<a target="_blank" href="{{ item.link }}">{{ item.title }}</a>';
60
		return array(
61
			'legend1'		=> 'SETTINGS',
62
			'feeds'			=> array('lang' => 'FEED_URLS', 'type' => 'multi_input:0', 'default' => []),
63
			'template'		=> array('type' => 'custom', 'default' => $template_default, 'object' => $this, 'method' => 'get_cfg_feeds_template'),
64
			'max'			=> array('lang' => 'MAX_ITEMS', 'validate' => 'int:1', 'type' => 'number:1', 'default' => 5),
65
			'cache'			=> array('lang' => 'CACHE_DURATION', 'validate' => 'int:1', 'type' => 'number:1', 'default' => 6, 'append' => 'HOURS_SHORT'),
66
		);
67
	}
68
69
	/**
70
	 * {@inheritdoc}
71
	 */
72
	public function display(array $bdata, $edit_mode = false)
73
	{
74
		$settings = $bdata['settings'];
75
		$feed_urls = $this->get_feeds_array($settings['feeds']);
76
		$status = 0;
77
78
		try
79
		{
80
			$content = $this->render_feeds($feed_urls, $settings, $status);
81
			$status = 1;
82
		}
83
		catch (\Exception $e)
84
		{
85
			$content = $e->getMessage();
86
		}
87
88
		return array(
89
			'title'		=> 'FEEDS',
90
			'content'	=> $status || $edit_mode ? $content : '',
91
			'status'	=> $status,
92
		);
93
	}
94
95
	/**
96
	 * @param array $feeds_url
97
	 * @param array $settings
98
	 * @param int $status
99
	 * @return string
100
	 */
101
	protected function render_feeds(array $feed_urls, array $settings, &$status)
102
	{
103
		$content = '';
104
		if ($items = $this->get_feed_items($feed_urls, $content, $settings['max'], $settings['cache']))
105
		{
106
			// We try to render block with user-provided trig template
107
			$template = $this->twig->createTemplate($this->get_feed_template($settings['template']));
108
109
			$status = 1;
110
			$content = $template->render([
111
				'items'	=> $items,
112
			]);
113
		}
114
115
		return $content;
116
	}
117
118
	/**
119
	 * @param string $source
120
	 * @return string
121
	 */
122
	public function get_cfg_feeds_template($source)
123
	{
124
		$template = $this->twig->load('@blitze_sitemaker/cfg_fields/feeds.html');
125
		return $template->render([
126
			'template'	=> $source,
127
		]);
128
	}
129
130
	/**
131
	 * Called when editing feed block to get available rss/atom fields
132
	 * @return array
133
	 */
134
	public function get_fields()
135
	{
136
		$this->translator->add_lang('feed_fields', 'blitze/sitemaker');
137
138
		$feeds = $this->request->variable('feeds', array(0 => ''));
139
		$feeds = $this->get_feeds_array($feeds);
140
141
		$message = '';
142
		$data = array('items' => []);
143
		$fields = array('items' => $this->get_field_defaults('items'));
144
145
		$feed_items = $this->get_feed_items($feeds, $message, 0, 0, 1, true);
146
147
		foreach ($feed_items as $feed)
148
		{
149
			$feed_data = $feed_fields = [];
150
			$fields['items']['children'] += $this->get_feed_fields($feed, $feed_data);
151
152
			foreach ($this->item_fields as $field)
153
			{
154
				$value = $feed->{$field};
155
				$feed_data[$field] = $value;
156
				$feed_fields[$field] = $this->build_tags($field, $value);
157
			}
158
159
			$data['items'][] = array_filter($feed_data);
160
			$fields['items']['children'] = array_replace_recursive($fields['items']['children'], array_filter($feed_fields));
161
		}
162
163
		return [
164
			'fields'	=> array_filter($fields),
165
			'data'		=> array_filter($data),
166
			'message'	=> $message,
167
		];
168
	}
169
170
	/**
171
	 * @param array $feed_urls
172
	 * @param string $message
173
	 * @param int $max
174
	 * @param int $cache
175
	 * @param bool $quiet
176
	 * @return array
177
	 */
178
	protected function get_feed_items(array $feed_urls, &$message, $max, $cache = 0, $items_per_feed = 0, $quiet = false)
179
	{
180
		if (!sizeof($feed_urls))
181
		{
182
			if (!$quiet)
183
			{
184
				throw new \Exception($this->translator->lang('FEED_URL_MISSING'));
185
			}
186
			return [];
187
		}
188
189
		$items = [];
190
191
		try
192
		{
193
			/**
194
			 * The below class cannot be added as a non-shared service using DI
195
			 * as it does not follow best practises for class contructs.
196
			 * It contains logic and method calls in the contructor for one thing.
197
			 * Passing it as a non-shared service does not work
198
			 */
199
			$feed = new \blitze\sitemaker\services\simplepie\feed;
200
			$feed->set_feed_url($feed_urls);
201
			$feed->enable_cache((bool) $cache);
202
			$feed->set_cache_location($this->cache_dir);
203
			$feed->set_cache_duration($cache * 3600);
204
205
			if ($items_per_feed)
206
			{
207
				$feed->set_item_limit($items_per_feed);
208
			}
209
210
			$feed->init();
211
			$feed->handle_content_type();
212
213
			if (!($items = $feed->get_items(0, $max)))
214
			{
215
				$message = $this->translator->lang('FEED_PROBLEMS');
216
			}
217
		}
218
		catch (\Exception $e)
219
		{
220
			$message = $e->getMessage();
221
		}
222
223
		return array_filter((array) $items);
224
	}
225
226
	/**
227
	 * @param \blitze\sitemaker\services\simplepie\item $feed
228
	 * @return array
229
	 */
230
	protected function get_feed_fields(\blitze\sitemaker\services\simplepie\item $item, array &$data)
231
	{
232
		$feed_props = [];
233
		foreach ($this->feed_fields as $field)
234
		{
235
			$feed_props[$field] = $this->build_tags($field, $item->feed->{$field});
236
			$data['feed'][$field] = $item->feed->{$field};
237
		}
238
239
		$feed_props = array_filter($feed_props);
240
		$data['feed'] = array_filter($data['feed']);
241
242
		$fields = [];
243
		if (sizeof($feed_props))
244
		{
245
			$fields['feed'] = $this->get_field_defaults('feed');
246
			$fields['feed']['children'] = $feed_props;
247
		}
248
249
		return $fields;
250
	}
251
252
	/**
253
	 * @param string $field
254
	 * @param mixed $value
255
	 * @return array|string
256
	 */
257
	protected function build_tags($field, $value)
258
	{
259
		if (empty($value))
260
		{
261
			return '';
262
		}
263
264
		$data = $this->get_field_defaults($field);
265
266
		if ($this->is_array_of_objects($value))
267
		{
268
			$value = array_slice($value, 0, 1);
269
			$this->iterate_props($value, $data);
270
		}
271
		else if (gettype($value) === 'object')
272
		{
273
			$props = array_filter(get_object_vars($value));
274
			$this->iterate_props($props, $data);
275
		}
276
277
		return $data;
278
	}
279
280
	/**
281
	 * @param array $props
282
	 * @param array $data
283
	 * @return void
284
	 */
285
	protected function iterate_props(array $props, array &$data)
286
	{
287
		ksort($props);
288
		foreach ($props as $prop => $value)
289
		{
290
			$data['children'][$prop] = $this->build_tags($prop, $value);
291
		}
292
	}
293
294
	/**
295
	 * @param mixed $value
296
	 * @return bool
297
	 */
298
	protected function is_array_of_objects($value)
299
	{
300
		return (is_array($value) && gettype($value[0]) === 'object');
301
	}
302
303
	/**
304
	 * @param string $field
305
	 * @return array
306
	 */
307
	protected function get_field_defaults($field)
308
	{
309
		$field = (string) $field;
310
		return [
311
			'text'			=> $field,
312
			'displayText'	=> $this->translator->lang(strtoupper($field)),
313
			'children'		=> [],
314
		];
315
	}
316
317
	/**
318
	 * @param string $tpl
319
	 * @return string
320
	 */
321
	protected function get_feed_template($item_tpl)
322
	{
323
		$item_tpl = html_entity_decode(trim($item_tpl));
324
		return "<ul class=\"sm-list\">
325
			{% for item in items %}
326
			<li>
327
				$item_tpl
328
			</li>
329
			{% endfor %}
330
		</ul>";
331
	}
332
333
	/**
334
	 * @param mixed $feeds
335
	 * @return array
336
	 */
337
	protected function get_feeds_array($feeds)
338
	{
339
		return array_map('trim', array_filter((array) $feeds));
340
	}
341
}
342