Completed
Push — master ( 42de88...914a72 )
by Nazar
04:02
created

Index::check_permission()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6667
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
/**
3
 * @package   CleverStyle CMS
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs;
9
use
10
	h;
11
12
/**
13
 * Provides next events:
14
 *  System/Index/block_render
15
 *  [
16
 *      'index'           => $index,        //Block index
17
 *      'blocks_array'    => &$blocks_array //Reference to array in form ['top' => '', 'left' => '', 'right' => '', 'bottom' => '']
18
 *  ]
19
 *
20
 *  System/Index/construct
21
 *
22
 *  System/Index/preload
23
 *
24
 *  System/Index/postload
25
 *
26
 * @method static Index instance($check = false)
27
 *
28
 * @property string[] $controller_path Path that will be used by controller to render page
29
 */
30
class Index {
31
	use
32
		Singleton,
33
		Index\Router;
34
35
	/**
36
	 * Appends to the end of title
37
	 *
38
	 * @var string
39
	 */
40
	protected $append_to_title = '';
41
	/**
42
	 * Name of current module
43
	 *
44
	 * @var string
45
	 */
46
	protected $module;
47
	protected $request_method;
48
	protected $working_directory = '';
49
	protected $called_once       = false;
50
	/**
51
	 * Reference to Route::instance()->path
52
	 *
53
	 * @var string[]
54
	 */
55
	protected $path = [];
56
	/**
57
	 * Reference to Route::instance()->ids
58
	 *
59
	 * @var int[]
60
	 */
61
	protected $ids = [];
62
	/**
63
	 * Path that will be used by controller to render page
64
	 *
65
	 * @var string[]
66
	 */
67
	protected $controller_path = ['index'];
68
	/**
69
	 * Detecting module folder including of admin/api request type, including prepare file, including of plugins
70
	 *
71
	 * @throws ExitException
72
	 */
73
	function construct () {
74
		$Config     = Config::instance();
75
		$Route      = Route::instance();
76
		$this->path = &$Route->path;
77
		$this->ids  = &$Route->ids;
78
		if ($this->closed_site($Config)) {
79
			return;
80
		}
81
		$this->module            = current_module();
82
		$this->working_directory = MODULES."/$this->module";
83
		if (admin_path()) {
84
			$this->working_directory .= '/admin';
85
		} elseif (api_path()) {
86
			$this->working_directory .= '/api';
87
		}
88
		if (!is_dir($this->working_directory)) {
89
			throw new ExitException(404);
90
		}
91
		if (!$this->check_permission('index')) {
92
			throw new ExitException(403);
93
		}
94
		Event::instance()->fire('System/Index/construct');
95
		/**
96
		 * Plugins processing
97
		 */
98
		foreach ($Config->components['plugins'] as $plugin) {
99
			_include(PLUGINS."/$plugin/index.php", false, false);
100
		}
101
		_include("$this->working_directory/prepare.php", false, false);
102
		/**
103
		 * @var _SERVER $_SERVER
104
		 */
105
		$this->request_method = strtolower($_SERVER->request_method);
106
		if (!preg_match('/^[a-z_]+$/', $this->request_method)) {
107
			throw new ExitException(400);
108
		}
109
	}
110
	/**
111
	 * Check if site is closed (taking user into account)
112
	 *
113
	 * @param Config $Config
114
	 *
115
	 * @return bool Whether user is not admin and this is not request for sign in (we allow to sign in on disabled site)
116
	 */
117
	protected function closed_site ($Config) {
118
		if (
119
			$Config->core['site_mode'] ||
120
			User::instance()->admin()
121
		) {
122
			return false;
123
		}
124
		return
125
			!api_path() ||
126
			$this->module != 'System' ||
127
			Route::instance()->route !== ['user', 'sign_in'];
128
	}
129
	/**
130
	 * Check whether user allowed to access to specified label
131
	 *
132
	 * @param string $label
133
	 *
134
	 * @return bool
135
	 */
136
	protected function check_permission ($label) {
137
		$permission_group = $this->module;
138
		if (admin_path()) {
139
			$permission_group = "admin/$permission_group";
140
		} elseif (api_path()) {
141
			$permission_group = "api/$permission_group";
142
		}
143
		return User::instance()->get_permission($permission_group, $label);
144
	}
145
	/**
146
	 * Page generation, blocks processing, adding of form with save/apply/cancel/reset and/or custom users buttons
147
	 *
148
	 * @throws ExitException
149
	 */
150
	protected function render_page () {
151
		$this->render_title();
152
		$this->render_content();
153
		if (!api_path()) {
154
			$this->render_blocks();
155
		}
156
	}
157
	/**
158
	 * Render page title
159
	 */
160
	protected function render_title () {
161
		$Page = Page::instance();
162
		/**
163
		 * Add generic Home or Module name title
164
		 */
165
		if (!api_path()) {
166
			$L = Language::instance();
167
			if (admin_path()) {
168
				$Page->title($L->administration);
169
			}
170
			$Page->title(
171
				$L->{home_page() ? 'home' : $this->module}
172
			);
173
		}
174
	}
175
	/**
176
	 * Render page content (without blocks, just module content)
177
	 *
178
	 * @throws ExitException
179
	 */
180
	protected function render_content () {
181
		$Page = Page::instance();
182
		/**
183
		 * If module consists of index.html only
184
		 */
185
		if (file_exists("$this->working_directory/index.html")) {
186
			ob_start();
187
			_include("$this->working_directory/index.html", false, false);
188
			$Page->content(ob_get_clean());
189
			return;
190
		}
191
		$this->execute_router();
192
	}
193
	/**
194
	 * Simple wrapper for form buttons
195
	 *
196
	 * @param string $name
197
	 * @param bool   $disabled
198
	 *
199
	 * @return string
200
	 */
201
	protected function form_button ($name, $disabled = false) {
202
		$L = Language::instance();
203
		return h::{'button[is=cs-button]'}(
204
			$L->$name,
205
			[
206
				'name'    => $name,
207
				'type'    => 'submit',
208
				'tooltip' => $L->{$name.'_info'},
209
				$disabled ? 'disabled' : false
210
			]
211
		);
212
	}
213
	/**
214
	 * Blocks rendering
215
	 */
216
	protected function render_blocks () {
217
		$blocks = Config::instance()->components['blocks'];
218
		/**
219
		 * It is frequent that there is no blocks - so, no need to to anything here
220
		 */
221
		if (!$blocks) {
222
			return;
223
		}
224
		$Page         = Page::instance();
225
		$blocks_array = [
226
			'top'    => '',
227
			'left'   => '',
228
			'right'  => '',
229
			'bottom' => ''
230
		];
231
		foreach ($blocks as $block) {
232
			/**
233
			 * If there is no need to show block or it was rendered by even handler - skip further processing
234
			 */
235
			if (
236
				!$this->should_block_be_rendered($block) ||
237
				!Event::instance()->fire(
238
					'System/Index/block_render',
239
					[
240
						'index'        => $block['index'],
241
						'blocks_array' => &$blocks_array
242
					]
243
				)
244
			) {
245
				/**
246
				 * Block was rendered by event handler
247
				 */
248
				continue;
249
			}
250
			$block['title'] = $this->ml_process($block['title']);
251
			switch ($block['type']) {
252
				default:
253
					$block['content'] = ob_wrapper(
254
						function () use ($block) {
255
							include BLOCKS."/block.$block[type].php";
256
						}
257
					);
258
					break;
259
				case 'html':
260
				case 'raw_html':
261
					$block['content'] = $this->ml_process($block['content']);
262
					break;
263
			}
264
			/**
265
			 * Template file will have access to `$block` variable, so it can use that
266
			 */
267
			$content = str_replace(
268
				[
269
					'<!--id-->',
270
					'<!--title-->',
271
					'<!--content-->'
272
				],
273
				[
274
					$block['index'],
275
					$block['title'],
276
					$block['content']
277
				],
278
				ob_wrapper(
279
					function () use ($block) {
280
						$template = file_exists(TEMPLATES."/blocks/block.$block[template]") ? $block['template'] : 'default.html';
281
						include TEMPLATES."/blocks/block.$template";
282
					}
283
				)
284
			);
285
			if ($block['position'] == 'floating') {
286
				$Page->replace(
287
					"<!--block#$block[index]-->",
288
					$content
289
				);
290
			} else {
291
				$blocks_array[$block['position']] .= $content;
292
			}
293
		}
294
		$Page->Top .= $blocks_array['top'];
295
		$Page->Left .= $blocks_array['left'];
296
		$Page->Right .= $blocks_array['right'];
297
		$Page->Bottom .= $blocks_array['bottom'];
298
	}
299
	/**
300
	 * Check whether to render block or not based on its properties (active state, when start to show, when it expires and permissions)
301
	 *
302
	 * @param array $block
303
	 *
304
	 * @return bool
305
	 */
306
	protected function should_block_be_rendered ($block) {
307
		return
308
			$block['active'] &&
309
			$block['start'] <= time() &&
310
			(
311
				!$block['expire'] ||
312
				$block['expire'] >= time()
313
			) &&
314
			User::instance()->get_permission('Block', $block['index']);
315
	}
316
	/**
317
	 * @param string $text
318
	 *
319
	 * @return string
1 ignored issue
show
Documentation introduced by
Should the return type not be array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
320
	 */
321
	protected function ml_process ($text) {
322
		return Text::instance()->process(Config::instance()->module('System')->db('texts'), $text, true);
323
	}
324
	/**
325
	 * Getter for `controller_path` property (no other properties supported currently)
326
	 *
327
	 * @param string $property
328
	 *
329
	 * @return false|string[]
330
	 */
331
	function __get ($property) {
332
		switch ($property) {
333
			case 'controller_path';
334
				return $this->controller_path;
335
		}
336
		return false;
337
	}
338
	/**
339
	 * Executes plugins processing, blocks and module page generation
340
	 *
341
	 * @throws ExitException
342
	 */
343
	function __finish () {
344
		/**
345
		 * Protection from double calling
346
		 */
347
		if ($this->called_once) {
348
			return;
349
		}
350
		$this->called_once = true;
351
		$Config            = Config::instance();
352
		$Page              = Page::instance();
353
		/**
354
		 * If site is closed
355
		 */
356
		if (!$Config->core['site_mode']) {
357
			if ($this->closed_site($Config)) {
358
				status_code(503);
359
				return;
360
			}
361
			/**
362
			 * Warning about closed site
363
			 */
364
			$Page->warning(get_core_ml_text('closed_title'));
365
		}
366
		Event::instance()->fire('System/Index/preload');
367
		$this->render_page();
368
		Event::instance()->fire('System/Index/postload');
369
	}
370
}
371