1 <?php
2
3 4 5 6 7 8 9 10
11
12 namespace Icybee\Modules\Pages;
13
14 use ICanBoogie\AuthenticationRequired;
15 use ICanBoogie\Exception;
16 use ICanBoogie\HTTP\NotFound;
17 use ICanBoogie\HTTP\RedirectResponse;
18 use ICanBoogie\HTTP\Request;
19 use ICanBoogie\HTTP\Response;
20 use ICanBoogie\HTTP\ServiceUnavailable;
21 use ICanBoogie\I18n;
22 use ICanBoogie\Routing\Pattern;
23
24 use Icybee\Modules\Sites\Site;
25
26 define(__NAMESPACE__ . '\PageController\CSS_DOCUMENT_PLACEHOLDER', uniqid());
27 define(__NAMESPACE__ . '\PageController\JS_DOCUMENT_PLACEHOLDER', uniqid());
28
29 class PageController
30 {
31 const DOCUMENT_JS_PLACEHOLDER = '<!-- $document.js -->';
32
33 public function __invoke(Request $request)
34 {
35 global $core;
36
37 try
38 {
39 $request->context->page = $page = $this->resolve_page($request);
40
41 if (!$page)
42 {
43 return;
44 }
45
46 if ($page instanceof Response)
47 {
48 return $page;
49 }
50
51 return $this->resolve_response($page, $request);
52 }
53 catch (\Exception $e)
54 {
55 $code = $e->getCode();
56 $pathname = \ICanBoogie\DOCUMENT_ROOT . "protected/all/templates/$code.html";
57
58 if (file_exists($pathname))
59 {
60 $request->context->page = $page = Page::from([
61
62 'siteid' => $core->site_id,
63 'title' => I18n\t($e->getCode(), [], [ 'scope' => 'exception' ]),
64 'body' => I18n\t($e->getMessage(), [], [ 'scope' => 'exception' ])
65
66 ]);
67
68 $template = file_get_contents($pathname);
69 $engine = $this->resolve_engine($template);
70
71 return new Response($engine($template, $page), $code);
72 }
73
74 throw $e;
75 }
76 }
77
78 79 80 81 82 83 84 85
86 protected function resolve_response(Page $page, Request $request)
87 {
88 global $core;
89
90 $template = $this->resolve_template($page->template);
91 $document = $core->document;
92 $engine = $this->resolve_engine($template);
93 $engine->context['page'] = $page;
94 $engine->context['document'] = $document;
95
96 new PageController\BeforeRenderEvent($this, $request, $page, $engine->context);
97
98
99
100
101
102 if ($page->body && is_callable(array($page->body, 'render')))
103 {
104 $page->body->render();
105 }
106
107 $html = $engine($template, $page, [ 'file' => $page->template ]);
108
109 new PageController\RenderEvent($this, $request, $page, $html);
110
111
112
113
114
115 $pos = strpos($html, self::DOCUMENT_JS_PLACEHOLDER);
116
117 if ($pos !== false)
118 {
119 $html = substr($html, 0, $pos)
120 . $document->js
121 . substr($html, $pos + strlen(self::DOCUMENT_JS_PLACEHOLDER));
122 }
123 else
124 {
125 $html = str_replace('</body>', PHP_EOL . PHP_EOL . $document->js . PHP_EOL . '</body>', $html);
126 }
127
128 return new Response($html, 200, [
129
130 'Content-Type' => 'text/html; charset=utf-8'
131
132 ]);
133 }
134
135 136 137 138 139 140 141
142 protected function resolve_page(Request $request)
143 {
144 global $core;
145
146
147
148 $site = $request->context->site;
149
150 if (!$site->siteid)
151 {
152 throw new NotFound('Unable to find matching website.');
153 }
154
155 $status = $site->status;
156
157 switch ($status)
158 {
159 case Site::STATUS_UNAUTHORIZED: throw new AuthenticationRequired();
160 case Site::STATUS_NOT_FOUND: throw new NotFound
161 (
162 \ICanBoogie\format("The requested URL does not exists: %uri", [
163
164 'uri' => $request->uri
165
166 ])
167 );
168
169 case Site::STATUS_UNAVAILABLE: throw new ServiceUnavailable();
170 }
171
172
173
174 $path = $request->path;
175 $page = $core->models['pages']->find_by_path($request->path);
176
177 if (!$page)
178 {
179 return;
180 }
181
182 if ($page->location)
183 {
184 return new RedirectResponse($page->location->url, 301, [
185
186 'Icybee-Redirected-By' => __FILE__ . '::' . __LINE__
187
188 ]);
189 }
190
191
192
193
194
195
196 $url_pattern = Pattern::from($page->url_pattern);
197
198 if (!$url_pattern->params && $page->url != $path)
199 {
200 $query_string = $request->query_string;
201
202 return new RedirectResponse($page->url . ($query_string ? '?' . $query_string : ''), 301, [
203
204 'Icybee-Redirected-By' => __FILE__ . '::' . __LINE__
205
206 ]);
207 }
208
209 if (!$page->is_online || $page->site->status != Site::STATUS_OK)
210 {
211
212
213
214
215
216
217 if (!$core->user->has_ownership('pages', $page))
218 {
219 throw new AuthenticationRequired
220 (
221 \ICanBoogie\format('The requested URL %url requires authentication.', [
222
223 'url' => $path
224
225 ])
226 );
227 }
228
229 $page->title .= ' ✎';
230 }
231
232 if (isset($page->url_variables))
233 {
234 $request->path_params = array_merge($request->path_params, $page->url_variables);
235 $request->params = array_merge($request->params, $page->url_variables);
236 }
237
238 return $page;
239 }
240
241 protected function resolve_template($name)
242 {
243 global $core;
244
245 $root = \ICanBoogie\DOCUMENT_ROOT;
246 $pathname = $core->site->resolve_path('templates/' . $name);
247
248 if (!$pathname)
249 {
250 throw new Exception('Unable to resolve path for template: %template', [ '%template' => $pathname ]);
251 }
252
253 return file_get_contents($root . $pathname, true);
254 }
255
256 protected function resolve_engine($template)
257 {
258 return new \Patron\Engine;
259 }
260 }
261
262 namespace Icybee\Modules\Pages\PageController;
263
264 use ICanBoogie\HTTP\Request;
265
266 use Icybee\Modules\Pages\Page;
267
268 269 270
271 class BeforeRenderEvent extends \ICanBoogie\Event
272 {
273 274 275 276 277
278 public $request;
279
280 281 282 283 284
285 public $response;
286
287 288 289 290 291
292 public $context;
293
294 295 296 297 298 299
300 public function __construct(\Icybee\Modules\Pages\PageController $target, Request $request, Page $page, &$context)
301 {
302 $this->request = $request;
303 $this->page = $page;
304 $this->context = &$context;
305
306 parent::__construct($target, 'render:before');
307 }
308 }
309
310 311 312
313 class RenderEvent extends \ICanBoogie\Event
314 {
315 316 317 318 319
320 public $request;
321
322 323 324 325 326
327 public $page;
328
329 330 331 332 333
334 public $html;
335
336 337 338 339 340 341
342 public function __construct(\Icybee\Modules\Pages\PageController $target, Request $request, Page $page, &$html)
343 {
344 $this->request = $request;
345 $this->page = $page;
346 $this->html = &$html;
347
348 parent::__construct($target, 'render');
349 }
350 }