Automad
 All Classes Functions Variables Pages
automad.php
1 <?php
2 /*
3  * ....
4  * .: '':.
5  * :::: ':..
6  * ::. ''..
7  * .:'.. ..':.:::' . :. '':.
8  * :. '' '' '. ::::.. ..:
9  * ::::. ..':.. .'''::::: .
10  * :::::::.. '..:::: :. :::: :
11  * ::'':::::::. ':::.'':.:::: :
12  * :.. ''::::::....': '':: :
13  * :::::. '::::: : .. '' .
14  * .''::::::::... ':::.'' ..'' :.''''.
15  * :..:::''::::: :::::...:'' :..:
16  * ::::::. ':::: :::::::: ..:: .
17  * ::::::::.:::: :::::::: :'':.:: .''
18  * ::: '::::::::.' ''::::: :.' '': :
19  * ::: :::::::::..' :::: ::...' .
20  * ::: .:::::::::: :::: :::: .:'
21  * '::' ''::::::: :::: : :: :
22  * ':::: :::: :'' .:
23  * :::: :::: ..''
24  * :::: ..:::: .:''
25  * '''' '''''
26  *
27  *
28  * AUTOMAD
29  *
30  * Copyright (c) 2014 by Marc Anton Dahmen
31  * http://marcdahmen.de
32  *
33  * Licensed under the MIT license.
34  * http://automad.org/license
35  */
36 
37 
38 namespace Automad\Core;
39 
40 
41 defined('AUTOMAD') or die('Direct access not permitted!');
42 
43 
53 class Automad {
54 
55 
62  public $Context;
63 
64 
71  private $Filelist = false;
72 
73 
80  private $Pagelist = false;
81 
82 
88  private $parseTxt;
89 
90 
95  private $siteData = array();
96 
97 
104  private $collection = array();
105 
106 
111  private $systemVarBuffer = array();
112 
113 
131  private function makeUrl($parentUrl, $slug) {
132 
133  // strip prefix from $slug
134  $pattern = '/[a-zA-Z0-9_-]+\./';
135  $replacement = '';
136  $slug = preg_replace($pattern, $replacement, $slug);
137 
138  // Clean up $slug
139  $slug = Parse::sanitize($slug);
140 
141  // Build URL:
142  // The ltrim (/) is needed to prevent a double / in front of every url,
143  // since $parentUrl will be empty for level 0 and 1 (//path/to/page).
144  // Trimming all '/' and then prependig a single '/', makes sure that there is always just one slash
145  // at the beginning of the URL.
146  // The leading slash is better to have in case of the home page where the key becomes [/] insted of just []
147  $url = '/' . ltrim($parentUrl . '/' . $slug, '/');
148 
149  // check if url already exists
150  if (array_key_exists($url, $this->collection)) {
151 
152  $i = 0;
153 
154  $newUrl = $url;
155 
156  while (array_key_exists($newUrl, $this->collection)) {
157  $i++;
158  $newUrl = $url . "-" . $i;
159  }
160 
161  $url = $newUrl;
162 
163  }
164 
165  return $url;
166 
167  }
168 
169 
182  private function collectPages($path = '/', $level = 0, $parentUrl = '') {
183 
184  // First check, if $path contains any data files.
185  // If more that one file matches the pattern, the first one will be used as the page's data file and the others will just be ignored.
186  if ($files = glob(AM_BASE_DIR . AM_DIR_PAGES . $path . '*.' . AM_FILE_EXT_DATA)) {
187 
188  $file = reset($files);
189 
190  $url = $this->makeUrl($parentUrl, basename($path));
191 
192  $Page = new Page();
193 
194  // Directly set URL here as first property,
195  // to be able to overwrite that url with an optional redirect-url from the data file.
196  $Page->url = $url;
197 
198  // If $this->parseTxt is true (default), then all txt files get parsed as well and
199  // the corresponding properties of $Page get defined.
200  // Skipping the parsing can be useful when just the structure is needed and not the content (GUI).
201  if ($this->parseTxt) {
202 
203  $data = Parse::markdownFile($file);
204 
205  // In case the title is not set in the data file or is empty, use the slug of the URL instead.
206  // In case the title is missig for the home page, use the site name instead.
207  if (!array_key_exists(AM_KEY_TITLE, $data) || ($data[AM_KEY_TITLE] == '')) {
208  if (trim($url, '/')) {
209  // If page is not the home page...
210  $data[AM_KEY_TITLE] = ucwords(str_replace(array('_', '-'), ' ', basename($url)));
211  } else {
212  // If page is home page...
213  $data[AM_KEY_TITLE] = $this->getSiteData(AM_KEY_SITENAME);
214  }
215  }
216 
217  // Extract tags
218  $tags = Parse::extractTags($data);
219 
220  // Check for an URL in $data and use that URL instead. If no URL is defined as override, add an URL var with the page's URL to $data to be used as variable as well.
221  if (array_key_exists(AM_KEY_URL, $data)) {
222  $Page->url = $data[AM_KEY_URL];
223  } else {
224  $data[AM_KEY_URL] = $Page->url;
225  }
226 
227  // Check for a theme in $data and use that as override for the site theme.
228  if (array_key_exists(AM_KEY_THEME, $data) && $data[AM_KEY_THEME]) {
229  $theme = $data[AM_KEY_THEME];
230  } else {
231  $theme = $this->getSiteData(AM_KEY_THEME);
232  }
233 
234  // Check if the page should be hidden from selections.
235  $hidden = false;
236  if (array_key_exists(AM_KEY_HIDDEN, $data)) {
237  if ($data[AM_KEY_HIDDEN] === 'true' || $data[AM_KEY_HIDDEN] === '1') {
238  $hidden = true;
239  }
240  }
241 
242  // Set Page properties from txt file.
243  $Page->data = $data;
244  $Page->tags = $tags;
245  $Page->theme = $theme;
246  $Page->hidden = $hidden;
247 
248  }
249 
250  // Set all main Page properties
251  $Page->path = $path;
252  $Page->level = $level;
253  $Page->parentUrl = $parentUrl;
254  $Page->template = str_replace('.' . AM_FILE_EXT_DATA, '', basename($file));
255 
256  // The relative URL ($url) of the page becomes the key (in $collection).
257  // That way it is impossible to create twice the same url and it is very easy to access the page's data.
258  // It will actually always be the "real" Automad-URL, even if a redirect-URL is specified (that one will be stored in $Page->url instead).
259  $this->collection[$url] = $Page;
260 
261  // $path gets only scanned for sub-pages, in case it contains a data file.
262  // That way it is impossible to generate pages without a parent page.
263  if ($dirs = glob(AM_BASE_DIR . AM_DIR_PAGES . $path . '*', GLOB_ONLYDIR)) {
264 
265  // Sort $dirs array again to be independent from glob's default behavior in case of any inconsistency.
266  sort($dirs);
267 
268  // Scan each directory recursively.
269  foreach ($dirs as $dir) {
270  $this->collectPages($path . basename($dir) . '/', $level + 1, $url);
271  }
272 
273  }
274 
275  }
276 
277  }
278 
279 
287  public function __construct($parseTxt = true) {
288 
289  $this->parseTxt = $parseTxt;
290 
291  if ($parseTxt) {
292  $this->siteData = Parse::siteData();
293  }
294 
295  // Collect pages.
296  $this->collectPages();
297 
298  Debug::log(array('Site Data' => $this->siteData, 'Collection' => $this->collection), 'New instance created');
299 
300  // Set the context initially to the requested page.
301  $this->Context = new Context($this->getRequestedPage());
302 
303  }
304 
305 
312  public function __sleep() {
313 
314  $itemsToCache = array('collection', 'siteData');
315  Debug::log($itemsToCache, 'Preparing Automad object for serialization! Caching the following items');
316  return $itemsToCache;
317 
318  }
319 
324  public function __wakeup() {
325 
326  Debug::log(get_object_vars($this), 'Automad object got unserialized');
327  $this->Context = new Context($this->getRequestedPage());
328 
329  }
330 
331 
339  public function getSiteData($key) {
340 
341  if (array_key_exists($key, $this->siteData)) {
342  return $this->siteData[$key];
343  }
344 
345  }
346 
347 
357  public function getSystemVar($var) {
358 
359  // Check whether $var is generated within a loop and therefore stored in $systemVarBuffer or
360  // if $var is related to the context, filelist or pagelist object.
361  if (array_key_exists($var, $this->systemVarBuffer)) {
362 
363  return $this->systemVarBuffer[$var];
364 
365  } else {
366 
367  switch ($var) {
368 
369  case AM_KEY_LEVEL:
370  return $this->Context->get()->level;
371 
372  case AM_KEY_TEMPLATE:
373  return $this->Context->get()->template;
374 
375  case AM_KEY_CURRENT_PAGE:
376  return $this->Context->get()->isCurrent();
377 
378  case AM_KEY_CURRENT_PATH:
379  return $this->Context->get()->isInCurrentPath();
380 
381  case AM_KEY_FILELIST_COUNT:
382  // The filelist count represents the number of files within the last defined filelist.
383  return count($this->getFilelist()->getFiles());
384 
385  case AM_KEY_PAGELIST_COUNT:
386  // The pagelist count represents the number of pages within the last defined pagelist.
387  return count($this->getPagelist()->getPages());
388 
389  }
390 
391  }
392 
393  }
394 
395 
403  public function setSystemVar($var, $value) {
404 
405  $this->systemVarBuffer[$var] = $value;
406 
407  }
408 
409 
417  public function getValue($key) {
418 
419  // Check whether the $key is considered a query string parameter (starting with a "?"), a system variable (starting with ":") or an item from the page/site array.
420  if (strpos($key, '?') === 0) {
421 
422  $key = substr($key, 1);
423 
424  if (array_key_exists($key, $_GET)) {
425  return htmlspecialchars($_GET[$key]);
426  }
427 
428  } else if (strpos($key, ':') === 0) {
429 
430  return $this->getSystemVar($key);
431 
432  } else {
433 
434  $data = $this->Context->get()->data;
435 
436  // First try if the variable is defined for the current page, before trying the site data.
437  if (array_key_exists($key, $data)) {
438  return $data[$key];
439  } else {
440  return $this->getSiteData($key);
441  }
442 
443  }
444 
445  }
446 
447 
454  public function getCollection() {
455 
456  return $this->collection;
457 
458  }
459 
460 
468  public function getPageByUrl($url) {
469 
470  if (array_key_exists($url, $this->collection)) {
471 
472  // If page exists
473  return $this->collection[$url];
474 
475  } elseif (Parse::queryKey('search') && $url == AM_PAGE_RESULTS_URL) {
476 
477  // If not, but it has the URL of the search results page (settings) and has a query (!).
478  // An empty query for a results page doesn't make sense.
479  return $this->createPage(AM_PAGE_RESULTS_TEMPLATE, AM_PAGE_RESULTS_TITLE . ' / "' . htmlspecialchars(Parse::queryKey('search')) . '"');
480 
481  } else {
482 
483  // Else return error page
484  return $this->createPage(AM_PAGE_NOT_FOUND_TEMPLATE, AM_PAGE_NOT_FOUND_TITLE);
485 
486  }
487 
488  }
489 
490 
497  public function getRequestedPage() {
498 
499  // Check whether the GUI is requesting the currently edited page.
500  if (AM_REQUEST == AM_PAGE_GUI && isset($_POST['url'])) {
501  return $this->getPageByUrl($_POST['url']);
502  } else {
503  return $this->getPageByUrl(AM_REQUEST);
504  }
505 
506  }
507 
508 
515  public function getFilelist() {
516 
517  if (!$this->Filelist) {
518  $this->Filelist = new Filelist($this->Context);
519  }
520 
521  return $this->Filelist;
522 
523  }
524 
525 
532  public function getPagelist() {
533 
534  if (!$this->Pagelist) {
535  $this->Pagelist = new Pagelist($this->collection, $this->Context);
536  }
537 
538  return $this->Pagelist;
539 
540  }
541 
542 
551  private function createPage($template, $title) {
552 
553  $page = new Page();
554  $page->theme = $this->getSiteData(AM_KEY_THEME);
555  $page->template = $template;
556  $page->data[AM_KEY_TITLE] = $title;
557  $page->parentUrl = '';
558  $page->level = 0;
559 
560  return $page;
561 
562  }
563 
564 
571  public function currentPageExists() {
572 
573  $Page = $this->Context->get();
574 
575  return ($Page->template != AM_PAGE_NOT_FOUND_TEMPLATE);
576 
577  }
578 
579 
580 }
581 
582 
583 ?>
static log($element, $description= '')
Definition: debug.php:113
static siteData()
Definition: parse.php:447
collectPages($path= '/', $level=0, $parentUrl= '')
Definition: automad.php:182
createPage($template, $title)
Definition: automad.php:551
__construct($parseTxt=true)
Definition: automad.php:287
static sanitize($str, $removeDots=false)
Definition: parse.php:413
setSystemVar($var, $value)
Definition: automad.php:403
makeUrl($parentUrl, $slug)
Definition: automad.php:131
static queryKey($key)
Definition: parse.php:294
static markdownFile($file)
Definition: parse.php:241
static extractTags($data)
Definition: parse.php:82