Completed
Push — master ( 105618...88380e )
by Nazar
04:15
created

App::render_content()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

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