Completed
Push — master ( 65992a...c50682 )
by Nazar
04:06
created

App::get_working_directory()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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