Completed
Push — master ( 8f0765...276fbe )
by Nazar
05:06
created

Controller::get_page_and_set_title()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

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