Completed
Push — master ( 561c31...ac170c )
by Nazar
04:13
created

App::allow_closed_site_request()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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