Completed
Push — master ( 17bd20...4edca1 )
by Nazar
04:17
created

App::render_title()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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