Completed
Push — master ( f3fec3...c7133e )
by Nazar
04:51
created

App::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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