Completed
Push — master ( 0bab36...9a2ec2 )
by Nazar
04:08
created

Controller::is_blogs_admin()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 3
eloc 6
nc 3
nop 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
166
		$Config   = Config::instance();
167
		$Page     = Page::instance();
168
		$Comments = null;
169
		Event::instance()->fire(
170
			'Comments/instance',
171
			[
172
				'Comments' => &$Comments
173
			]
174
		);
175
		/**
176
		 * @var \cs\modules\Comments\Comments $Comments
177
		 */
178
		$Posts   = Posts::instance();
179
		$rc      = $Request->route;
180
		$post_id = (int)mb_substr($rc[1], mb_strrpos($rc[1], ':') + 1);
181
		if (!$post_id) {
182
			throw new ExitException(404);
183
		}
184
		$post = $Posts->get_as_json_ld($post_id);
185
		if (
186
			!$post ||
187
			(
188
				$post['draft'] && $post['user'] != User::instance()->id
189
			)
190
		) {
191
			throw new ExitException(404);
192
		}
193
		if ($post['path'] != mb_substr($rc[1], 0, mb_strrpos($rc[1], ':'))) {
194
			$Response->redirect($post['url'], 303);
195
			return;
196
		}
197
		$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...
198
		$Page->Description = description($post['short_content']);
199
		$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...
200
		$Meta = Meta::instance();
201
		$Meta
202
			->article()
203
			->article('published_time', date('Y-m-d', $post['date'] ?: TIME))
204
			->article('section', $post['articleSection'] ? $post['articleSection'][0] : false)
205
			->article('tag', $post['tags']);
206
		array_map([$Meta, 'image'], $post['image']);
207
		$comments_enabled = $Config->module('Blogs')->enable_comments && $Comments;
208
		$Page->content(
209
			h::{'article[is=cs-blogs-post] script[type=application/ld+json]'}(
210
				json_encode($post, JSON_UNESCAPED_UNICODE)
211
			).
212
			($comments_enabled ? $Comments->block($post['id']) : '')
0 ignored issues
show
Documentation introduced by
$post['id'] is of type array, but the function expects a integer.

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