Completed
Push — master ( 6e2144...8f0765 )
by Nazar
04:26
created

Controller::get_favicon_path()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 9
rs 9.6666
c 1
b 1
f 0
1
<?php
2
/**
3
 * @package   Blogs
4
 * @category  modules
5
 * @author    Nazar Mokrynskyi <[email protected]>
6
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
7
 * @license   MIT License, see license.txt
8
 */
9
namespace cs\modules\Blogs;
10
use
11
	cs\Config,
12
	cs\Event,
13
	cs\ExitException,
14
	cs\Language\Prefix,
15
	cs\Page\Meta,
16
	cs\Page,
17
	cs\User,
18
	h;
19
20
class Controller {
21
	/**
22
	 * @param \cs\Request $Request
23
	 */
24
	static function latest_posts ($Request) {
25
		if (!Event::instance()->fire('Blogs/latest_posts')) {
26
			return;
27
		}
28
		$Config = Config::instance();
29
		$L      = new Prefix('blogs_');
30
		$Meta   = Meta::instance();
31
		$Page   = Page::instance();
32
		$Posts  = Posts::instance();
33
		/**
34
		 * Page title
35
		 */
36
		$Page->title($L->latest_posts);
37
		/**
38
		 * Now add link to Atom feed for latest posts
39
		 */
40
		$Page->atom('Blogs/atom.xml', $L->latest_posts);
41
		/**
42
		 * Set page of blog type (Open Graph protocol)
43
		 */
44
		$Meta->blog();
45
		/**
46
		 * Determine current page
47
		 */
48
		$page = static::get_page_and_set_title($Request, $Page, $L);
49
		/**
50
		 * Get posts for current page in JSON-LD structure format
51
		 */
52
		$posts_per_page = $Config->module('Blogs')->posts_per_page;
53
		$posts          = $Posts->get_latest_posts($page, $posts_per_page);
54
		/**
55
		 * Base url (without page number)
56
		 */
57
		$base_url = $Config->base_url().'/'.path($L->Blogs).'/'.path($L->latest_posts);
58
		/**
59
		 * Render posts page
60
		 */
61
		Helpers::show_posts_list(
62
			$posts,
63
			$Posts->get_total_count(),
64
			$page,
65
			$base_url
66
		);
67
	}
68
	/**
69
	 * @param \cs\Request $Request
70
	 * @param Page        $Page
71
	 * @param Prefix      $L
72
	 *
73
	 * @return int
74
	 */
75
	protected static function get_page_and_set_title ($Request, $Page, $L) {
76
		$page = max(
77
			isset($Request->route_ids[0]) ? array_slice($Request->route_ids, -1)[0] : 1,
78
			1
79
		);
80
		/**
81
		 * If this is not first page - show that in page title
82
		 */
83
		if ($page > 1) {
84
			$Page->title($L->page_number($page));
85
		}
86
		return $page;
87
	}
88
	/**
89
	 * @param \cs\Request $Request
90
	 *
91
	 * @throws ExitException
92
	 */
93
	static function section ($Request) {
94
		if (!Event::instance()->fire('Blogs/section')) {
95
			return;
96
		}
97
		$Config   = Config::instance();
98
		$L        = new Prefix('blogs_');
99
		$Meta     = Meta::instance();
100
		$Page     = Page::instance();
101
		$Posts    = Posts::instance();
102
		$Sections = Sections::instance();
103
		/**
104
		 * At first - determine part of url and get sections list based on that path
105
		 */
106
		$sections = $Sections->get_by_path(
107
			array_slice($Request->route_path, 1)
108
		);
109
		if (!$sections) {
110
			throw new ExitException(400);
111
		}
112
		$sections = $Sections->get($sections);
113
		/**
114
		 * Now lets set page title using sections names from page path
115
		 * We will not remove `$section` variable after, since it will be direct parent of each shown post
116
		 */
117
		foreach ($sections as $section) {
0 ignored issues
show
Bug introduced by
The expression $sections of type array|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
118
			$Page->title($section['title']);
119
		}
120
		/**
121
		 * Now add link to Atom feed for posts from current section only
122
		 */
123
		/** @noinspection PhpUndefinedVariableInspection */
124
		$Page->atom(
125
			"Blogs/atom.xml/?section=$section[id]",
126
			implode($Config->core['title_delimiter'], [$L->latest_posts, $L->section, $section['title']])
127
		);
128
		/**
129
		 * Set page of blog type (Open Graph protocol)
130
		 */
131
		$Meta->blog();
132
		/**
133
		 * Determine current page
134
		 */
135
		$page = static::get_page_and_set_title($Request, $Page, $L);
136
		/**
137
		 * Get posts for current page in JSON-LD structure format
138
		 */
139
		$posts_per_page = $Config->module('Blogs')->posts_per_page;
140
		$posts          = $Posts->get_for_section($section['id'], $page, $posts_per_page);
141
		/**
142
		 * Base url (without page number)
143
		 */
144
		$base_url = $Config->base_url().'/'.path($L->Blogs).'/'.path($L->section)."/$section[full_path]";
145
		/**
146
		 * Render posts page
147
		 */
148
		Helpers::show_posts_list(
149
			$posts,
150
			$section['posts'],
151
			$page,
152
			$base_url
153
		);
154
	}
155
	/**
156
	 * @param \cs\Request  $Request
157
	 * @param \cs\Response $Response
158
	 *
159
	 * @throws ExitException
160
	 */
161
	static function post ($Request, $Response) {
162
		if (!Event::instance()->fire('Blogs/post')) {
163
			return;
164
		}
165
		$Page    = Page::instance();
166
		$Posts   = Posts::instance();
167
		$rc      = $Request->route;
168
		$post_id = (int)mb_substr($rc[1], mb_strrpos($rc[1], ':') + 1);
169
		if (!$post_id) {
170
			throw new ExitException(404);
171
		}
172
		$post = $Posts->get_as_json_ld($post_id);
173
		if (
174
			!$post ||
175
			(
176
				$post['draft'] && $post['user'] != User::instance()->id
177
			)
178
		) {
179
			throw new ExitException(404);
180
		}
181
		if ($post['path'] != mb_substr($rc[1], 0, mb_strrpos($rc[1], ':'))) {
182
			$Response->redirect($post['url'], 303);
183
			return;
184
		}
185
		$Page->title($post['title']);
0 ignored issues
show
Documentation introduced by
$post['title'] is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
186
		$Page->Description = description($post['short_content']);
187
		$Page->canonical_url($post['url']);
0 ignored issues
show
Documentation introduced by
$post['url'] is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
188
		$Meta = Meta::instance();
189
		$Meta
190
			->article()
191
			->article('published_time', date('Y-m-d', $post['date'] ?: TIME))
192
			->article('section', $post['articleSection'] ? $post['articleSection'][0] : false)
193
			->article('tag', $post['tags']);
194
		array_map([$Meta, 'image'], $post['image']);
195
		$Page->content(
196
			h::{'article[is=cs-blogs-post] script[type=application/ld+json]'}(
197
				json_encode($post, JSON_UNESCAPED_UNICODE)
198
			)
199
		);
200
	}
201
	protected static function is_blogs_admin () {
202
		$User = User::instance();
203
		return
204
			$User->admin() &&
205
			$User->get_permission('admin/Blogs', 'index') &&
206
			$User->get_permission('admin/Blogs', 'edit_post');
207
	}
208
	/**
209
	 * @param \cs\Request $Request
210
	 *
211
	 * @throws ExitException
212
	 */
213
	static function tag ($Request) {
214
		if (!Event::instance()->fire('Blogs/tag')) {
215
			return;
216
		}
217
		$Config = Config::instance();
218
		$L      = new Prefix('blogs_');
219
		$Meta   = Meta::instance();
220
		$Page   = Page::instance();
221
		$Posts  = Posts::instance();
222
		$Tags   = Tags::instance();
223
		/**
224
		 * If no tag specified
225
		 */
226
		if (!isset($Request->route[1])) {
227
			throw new ExitException(404);
228
		}
229
		/**
230
		 * Find tag
231
		 */
232
		$tag = $Tags->get_by_text($Request->route[1]);
233
		if (!$tag) {
234
			throw new ExitException(404);
235
		}
236
		$tag = $Tags->get($tag);
237
		/**
238
		 * Add tag to page title
239
		 */
240
		$Page->title($tag['text']);
241
		/**
242
		 * Now add link to Atom feed for posts with current tag only
243
		 */
244
		$Page->atom(
245
			"Blogs/atom.xml/?tag=$tag[id]",
246
			implode($Config->core['title_delimiter'], [$L->latest_posts, $L->tag, $tag['text']])
247
		);
248
		/**
249
		 * Set page of blog type (Open Graph protocol)
250
		 */
251
		$Meta->blog();
252
		/**
253
		 * Determine current page
254
		 */
255
		$page = max($Request->route(2) ?: 1, 1);
256
		/**
257
		 * If this is not first page - show that in page title
258
		 */
259
		if ($page > 1) {
260
			$Page->title($L->blogs_nav_page($page));
261
		}
262
		/**
263
		 * Get posts for current page in JSON-LD structure format
264
		 */
265
		$posts_per_page = $Config->module('Blogs')->posts_per_page;
266
		$posts          = $Posts->get_for_tag($tag['id'], $L->clang, $page, $posts_per_page);
267
		$posts_count    = $Posts->get_for_tag_count($tag['id'], $L->clang);
268
		/**
269
		 * Base url (without page number)
270
		 */
271
		$base_url = $Config->base_url().'/'.path($L->Blogs).'/'.path($L->tag).'/'.$Request->route[1];
272
		/**
273
		 * Render posts page
274
		 */
275
		Helpers::show_posts_list(
276
			$posts,
277
			$posts_count,
278
			$page,
279
			$base_url
280
		);
281
	}
282
	static function new_post () {
283
		if (!Event::instance()->fire('Blogs/new_post')) {
284
			return;
285
		}
286
287
		$Config      = Config::instance();
288
		$module_data = $Config->module('Blogs');
289
		$L           = new Prefix('blogs_');
290
		$Page        = Page::instance();
291
		$User        = User::instance();
292
		$Page->title($L->new_post);
293
		if (!$User->admin() && $module_data->new_posts_only_from_admins) {
294
			throw new ExitException(403);
295
		}
296
		if (!$User->user()) {
297
			$Page->warning($L->for_registered_users_only);
298
			return;
299
		}
300
		$Page->content(
301
			h::cs_blogs_add_edit_post()
302
		);
303
	}
304
	/**
305
	 * @param \cs\Request $Request
306
	 *
307
	 * @throws ExitException
308
	 */
309
	static function edit_post ($Request) {
310
		if (!Event::instance()->fire('Blogs/edit_post')) {
311
			return;
312
		}
313
314
		$Posts       = Posts::instance();
315
		$Config      = Config::instance();
316
		$module_data = $Config->module('Blogs');
317
		$L           = new Prefix('blogs_');
318
		$Page        = Page::instance();
319
		$User        = User::instance();
320
		if ($module_data->new_posts_only_from_admins && !$User->admin()) {
321
			throw new ExitException(403);
322
		}
323
		$id = $Request->route(1);
324
		if (!$id) {
325
			throw new ExitException(400);
326
		}
327
		$post = $Posts->get($id);
328
		if (!$post) {
329
			throw new ExitException(404);
330
		}
331
		if ($post['user'] != $User->id && !static::is_blogs_admin()) {
332
			throw new ExitException(403);
333
		}
334
		$Page->title(
335
			$L->editing_of_post($post['title'])
336
		);
337
		$Page->content(
338
			h::cs_blogs_add_edit_post(
339
				[
340
					'id' => $post['id']
341
				]
342
			)
343
		);
344
	}
345
	/**
346
	 * @param \cs\Request $Request
347
	 */
348
	static function drafts ($Request) {
349
		if (!Event::instance()->fire('Blogs/drafts')) {
350
			return;
351
		}
352
		$Config = Config::instance();
353
		$L      = new Prefix('blogs_');
354
		$Page   = Page::instance();
355
		$Posts  = Posts::instance();
356
		$User   = User::instance();
357
		$Page->title($L->drafts);
358
		/**
359
		 * Determine current page
360
		 */
361
		$page = static::get_page_and_set_title($Request, $Page, $L);
362
		/**
363
		 * Get posts for current page in JSON-LD structure format
364
		 */
365
		$posts_per_page = $Config->module('Blogs')->posts_per_page;
366
		$posts          = $Posts->get_drafts($User->id, $page, $posts_per_page);
367
		$posts_count    = $Posts->get_drafts_count($User->id);
368
		/**
369
		 * Base url (without page number)
370
		 */
371
		$base_url = $Config->base_url().'/'.path($L->Blogs).'/'.path($L->drafts);
372
		/**
373
		 * Render posts page
374
		 */
375
		Helpers::show_posts_list(
376
			$posts,
377
			$posts_count,
378
			$page,
379
			$base_url
380
		);
381
	}
382
	/**
383
	 * @param \cs\Request  $Request
384
	 * @param \cs\Response $Response
385
	 *
386
	 * @throws ExitException
387
	 */
388
	static function atom_xml ($Request, $Response) {
389
		$Config   = Config::instance();
390
		$L        = new Prefix('blogs_');
391
		$Page     = Page::instance();
392
		$User     = User::instance();
393
		$title    = [
394
			get_core_ml_text('name'),
395
			$L->Blogs
396
		];
397
		$Posts    = Posts::instance();
398
		$Sections = Sections::instance();
399
		$Tags     = Tags::instance();
400
		$number   = $Config->module('Blogs')->posts_per_page;
401
		$section  = $Request->query('section');
402
		$tag      = $Request->query('tag');
403
		if ($section) {
404
			$section = $Sections->get($section);
405
			if (!$section) {
406
				throw new ExitException(404);
407
			}
408
			$title[] = $L->section;
409
			$title[] = $section['title'];
410
			$posts   = $Posts->get_for_section($section['id'], 1, $number);
411
		} elseif ($tag) {
412
			$tag = $Tags->get($tag);
413
			if (!$tag) {
414
				throw new ExitException(404);
415
			}
416
			$title[] = $L->tag;
417
			$title[] = $tag['text'];
418
			$posts   = $Posts->get_for_tag($tag['id'], $L->clang, 1, $number);
419
		} else {
420
			$posts = $Posts->get_latest_posts(1, $number);
421
		}
422
		$title[]  = $L->latest_posts;
423
		$title    = implode($Config->core['title_delimiter'], $title);
424
		$base_url = $Config->base_url();
425
		$Response->header('content-type', 'application/atom+xml');
426
		$Page->interface = false;
427
428
		$url = $Config->core_url().$Request->uri;
429
		$Page->content(
430
			"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
431
			h::feed(
432
				h::title($title).
433
				h::id($url).
434
				str_replace(
435
					'>',
436
					'/>',
437
					h::link(
438
						[
439
							'href' => $url,
440
							'rel'  => 'self'
441
						]
442
					)
443
				).
444
				h::updated(date('c')).
445
				'<icon>'.static::get_favicon_path($Config->core['theme'])."</icon>\n".
446
				h::entry(
447
					array_map(
448
						function ($post) use ($Posts, $Sections, $User, $base_url) {
449
							$post = $Posts->get($post);
450
							return
451
								h::title($post['title']).
452
								h::id("$base_url/Blogs/:$post[id]").
453
								h::updated(date('c', $post['date'])).
454
								h::published(date('c', $post['date'])).
455
								str_replace(
456
									'>',
457
									'/>',
458
									h::link(
459
										[
460
											'href' => "$base_url/Blogs/:$post[id]"
461
										]
462
									)
463
								).
464
								h::{'author name'}($User->username($post['user'])).
465
								h::category(
466
									$post['sections'] == ['0'] ? false : array_map(
467
										function ($category) {
468
											return [
469
												'term'  => $category['title'],
470
												'label' => $category['title']
471
											];
472
										},
473
										$Sections->get($post['sections'])
474
									)
475
								).
476
								h::summary(
477
									htmlentities($post['short_content']),
478
									[
479
										'type' => 'html'
480
									]
481
								).
482
								h::content(
483
									htmlentities($post['content']),
484
									[
485
										'type' => 'html'
486
									]
487
								);
488
						},
489
						$posts ?: []
490
					)
491
				),
492
				[
493
					'xmlns'    => 'http://www.w3.org/2005/Atom',
494
					'xml:lang' => $L->clang,
495
					'xml:base' => "$base_url/"
496
				]
497
			)
498
		);
499
	}
500
	/**
501
	 * @param string $theme
502
	 *
503
	 * @return string
504
	 */
505
	protected static function get_favicon_path ($theme) {
506
		$theme_favicon = "$theme/img/favicon";
507
		if (file_exists(THEMES."/$theme_favicon.png")) {
508
			return "$theme_favicon.png";
509
		} elseif (file_exists(THEMES."/$theme_favicon.ico")) {
510
			return "$theme_favicon.ico";
511
		}
512
		return 'favicon.ico';
513
	}
514
}
515