Completed
Push — master ( 67d50f...8a7d08 )
by Nazar
04:56
created

App::execute()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3.004

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 15
ccs 12
cts 13
cp 0.9231
crap 3.004
rs 9.4285
1
<?php
2
/**
3
 * @package   CleverStyle Framework
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
	cs\App\Router;
11
12
/**
13
 * Provides next events:
14
 *  System/App/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/App/render/before
21
 *
22
 *  System/App/execute_router/before
23
 *
24
 *  System/App/execute_router/after
25
 *
26
 *  System/App/render/after
27
 *
28
 * @property string[] $controller_path Path that will be used by controller to render page
29
 *
30
 * @method static $this instance($check = false)
31
 */
32
class App {
33
	use
34
		Singleton,
35
		Router;
36
	const INIT_STATE_METHOD = 'init';
37 12
	protected function init () {
38 12
		$this->init_router();
39 12
	}
40
	/**
41
	 * Executes blocks and module page generation
42
	 *
43
	 * @throws ExitException
44
	 */
45 10
	function execute () {
46 10
		$Config  = Config::instance();
47 10
		$Request = Request::instance();
48 10
		if (!preg_match('/^[0-9a-z_]+$/i', $Request->method)) {
49
			throw new ExitException(400);
50
		}
51 10
		$this->handle_closed_site(!$Config->core['site_mode'], $Request);
52 8
		if (!$this->check_permission($Request, 'index')) {
53 2
			throw new ExitException(403);
54
		}
55 6
		Event::instance()->fire('System/App/render/before');
56 6
		$this->render($Request);
57 6
		Event::instance()->fire('System/App/render/after');
58 6
		Page::instance()->render();
59 6
	}
60
	/**
61
	 * @param bool    $closed_site
62
	 * @param Request $Request
63
	 *
64
	 * @throws ExitException
65
	 */
66 10
	protected function handle_closed_site ($closed_site, $Request) {
67 10
		if (!$closed_site) {
68 6
			return;
69
		}
70
		/**
71
		 * If site is closed
72
		 */
73 4
		if (!$this->allow_closed_site_request($Request)) {
74 2
			throw new ExitException(
75
				[
76 2
					get_core_ml_text('closed_title'),
77 2
					get_core_ml_text('closed_text')
78
				],
79 2
				503
80
			);
81
		}
82
		/**
83
		 * Warning about closed site for administrator
84
		 */
85 2
		Page::instance()->warning(get_core_ml_text('closed_title'));
86 2
	}
87
	/**
88
	 * Check if visitor is allowed to make current request to closed site
89
	 *
90
	 * @param Request $Request
91
	 *
92
	 * @return bool
93
	 */
94 4
	protected function allow_closed_site_request ($Request) {
95
		return
96 4
			User::instance()->admin() ||
97
			(
98 4
				$Request->api_path &&
99 4
				$Request->current_module == 'System' &&
100 4
				$Request->route == ['profile'] &&
101 4
				$Request->method == 'SIGN_IN'
102
			);
103
	}
104
	/**
105
	 * Check whether user allowed to access to specified label
106
	 *
107
	 * @param Request $Request
108
	 * @param string  $label
109
	 *
110
	 * @return bool
111
	 */
112 8
	protected function check_permission ($Request, $label) {
113 8
		if ($Request->cli_path) {
114
			return true;
115
		}
116 8
		$permission_group = $Request->current_module;
117 8
		if ($Request->admin_path) {
118 2
			$permission_group = "admin/$permission_group";
119 6
		} elseif ($Request->api_path) {
120 4
			$permission_group = "api/$permission_group";
121
		}
122 8
		return User::instance()->get_permission($permission_group, $label);
123
	}
124
	/**
125
	 * @param Request $Request
126
	 *
127
	 * @throws ExitException
128
	 */
129 6
	protected function render ($Request) {
130 6
		if ($Request->cli_path || $Request->api_path) {
131 4
			$this->execute_router($Request);
132
		} else {
133 2
			$Page = Page::instance();
134 2
			$this->render_title($Request, $Page);
135 2
			$this->execute_router($Request);
136 2
			$this->render_blocks($Page);
137
		}
138 6
	}
139
	/**
140
	 * Render page title
141
	 *
142
	 * @param Request $Request
143
	 * @param Page    $Page
144
	 */
145 2
	protected function render_title ($Request, $Page) {
146
		/**
147
		 * Add generic Home or Module name title
148
		 */
149 2
		$L = Language::instance();
150 2
		if ($Request->admin_path) {
151
			$Page->title($L->system_admin_administration);
152
		}
153 2
		$Page->title(
154 2
			$L->{$Request->home_page ? 'system_home' : $Request->current_module}
155
		);
156 2
	}
157
	/**
158
	 * Blocks rendering
159
	 *
160
	 * @param Page $Page
161
	 */
162 2
	protected function render_blocks ($Page) {
163 2
		$blocks = Config::instance()->components['blocks'];
164
		/**
165
		 * It is frequent that there is no blocks - so, no need to to anything here
166
		 */
167 2
		if (!$blocks) {
168 2
			return;
169
		}
170
		$blocks_array = [
171
			'top'    => '',
172
			'left'   => '',
173
			'right'  => '',
174
			'bottom' => ''
175
		];
176
		foreach ($blocks as $block) {
177
			/**
178
			 * If there is no need to show block or it was rendered by even handler - skip further processing
179
			 */
180
			if (
181
				!$this->should_block_be_rendered($block) ||
182
				!Event::instance()->fire(
183
					'System/Index/block_render',
184
					[
185
						'index'        => $block['index'],
186
						'blocks_array' => &$blocks_array
187
					]
188
				) ||
189
				!Event::instance()->fire(
190
					'System/App/block_render',
191
					[
192
						'index'        => $block['index'],
193
						'blocks_array' => &$blocks_array
194
					]
195
				)
196
			) {
197
				/**
198
				 * Block was rendered by event handler
199
				 */
200
				continue;
201
			}
202
			$block['title'] = $this->ml_process($block['title']);
203
			switch ($block['type']) {
204
				default:
205
					$block['content'] = ob_wrapper(
206
						function () use ($block) {
207
							include BLOCKS."/block.$block[type].php";
208
						}
209
					);
210
					break;
211
				case 'html':
212
				case 'raw_html':
213
					$block['content'] = $this->ml_process($block['content']);
214
					break;
215
			}
216
			/**
217
			 * Template file will have access to `$block` variable, so it can use that
218
			 */
219
			$content = str_replace(
220
				[
221
					'<!--id-->',
222
					'<!--title-->',
223
					'<!--content-->'
224
				],
225
				[
226
					$block['index'],
227
					$block['title'],
228
					$block['content']
229
				],
230
				ob_wrapper(
231
					function () use ($block) {
232
						$template = file_exists(TEMPLATES."/blocks/block.$block[template]") ? $block['template'] : 'default.html';
233
						include TEMPLATES."/blocks/block.$template";
234
					}
235
				)
236
			);
237
			if ($block['position'] == 'floating') {
238
				$Page->replace(
239
					"<!--block#$block[index]-->",
240
					$content
241
				);
242
			} else {
243
				$blocks_array[$block['position']] .= $content;
244
			}
245
		}
246
		$Page->Top .= $blocks_array['top'];
247
		$Page->Left .= $blocks_array['left'];
248
		$Page->Right .= $blocks_array['right'];
249
		$Page->Bottom .= $blocks_array['bottom'];
250
	}
251
	/**
252
	 * Check whether to render block or not based on its properties (active state, when start to show, when it expires and permissions)
253
	 *
254
	 * @param array $block
255
	 *
256
	 * @return bool
257
	 */
258
	protected function should_block_be_rendered ($block) {
259
		return
260
			$block['active'] &&
261
			$block['start'] <= time() &&
262
			(
263
				!$block['expire'] ||
264
				$block['expire'] >= time()
265
			) &&
266
			User::instance()->get_permission('Block', $block['index']);
267
	}
268
	/**
269
	 * @param string $text
270
	 *
271
	 * @return string
272
	 */
273
	protected function ml_process ($text) {
274
		return Text::instance()->process(Config::instance()->module('System')->db('texts'), $text, true);
275
	}
276
	/**
277
	 * Getter for `controller_path` property (no other properties supported currently)
278
	 *
279
	 * @param string $property
280
	 *
281
	 * @return false|string[]
282
	 */
283 4
	function __get ($property) {
284 4
		if ($property == 'controller_path') {
285 4
			return $this->controller_path;
286
		}
287
		return false;
288
	}
289
}
290