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 }