Completed
Push — master ( ae87f2...118397 )
by Nazar
04:02
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 3
Bugs 1 Features 1
Metric Value
cc 3
eloc 12
c 3
b 1
f 1
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
			if ($block['position'] == 'floating') {
217
				$Page->replace(
218
					"<!--block#$block[index]-->",
219
					$block['content']
220
				);
221
			} else {
222
				$blocks_array[$block['position']] .= $block['content'];
223
			}
224
		}
225
		$Page->Top .= $blocks_array['top'];
226
		$Page->Left .= $blocks_array['left'];
227
		$Page->Right .= $blocks_array['right'];
228
		$Page->Bottom .= $blocks_array['bottom'];
229
	}
230
	/**
231
	 * Check whether to render block or not based on its properties (active state, when start to show, when it expires and permissions)
232
	 *
233
	 * @param array $block
234
	 *
235
	 * @return bool
236
	 */
237
	protected function should_block_be_rendered ($block) {
238
		return
239
			$block['active'] &&
240
			$block['start'] <= time() &&
241
			(
242
				!$block['expire'] ||
243
				$block['expire'] >= time()
244
			) &&
245
			User::instance()->get_permission('Block', $block['index']);
246
	}
247
	/**
248
	 * @param string $text
249
	 *
250
	 * @return string
251
	 */
252
	protected function ml_process ($text) {
253
		return Text::instance()->process(Config::instance()->module('System')->db('texts'), $text, true);
254
	}
255
	/**
256
	 * Getter for `controller_path` property (no other properties supported currently)
257
	 *
258
	 * @param string $property
259
	 *
260
	 * @return false|string[]
261
	 */
262 4
	function __get ($property) {
263 4
		if ($property == 'controller_path') {
264 4
			return $this->controller_path;
265
		}
266
		return false;
267
	}
268
}
269