silverstripe-docsviewer/code/DocumentationManifest.php
Will Rossiter 43b6d42719 Overhaul of module to use DocumentationManifest
This major update changes the behaviour of the docviewer module to use a cached manifest rather than on demand. This allows us to simplify the URL matching and store 'nice' URL configuration rather than altering handleAction().
2014-09-07 11:26:12 +12:00

268 lines
5.7 KiB
PHP

<?php
/**
* A class which builds a manifest of all documentation present in a project.
*
* The manifest is required to map the provided documentation URL rules to a
* file path on the server. The stored cache looks similar to the following:
*
* <code>
* array(
* 'en/someniceurl/' => array(
* 'filepath' => '/path/to/docs/en/SomeniceFile.md',
* 'title' => 'Some nice URL',
* 'summary' => 'Summary Text',
* 'basename' => 'SomeniceFile.md',
* 'type' => 'DocumentationPage'
* )
* )
* </code>
*
* URL format is in the following structures:
*
* {lang}/{path}
* {lang}/{module}/{path}
* {lang}/{module}/{version}/{/path}
*
* @package framework
* @subpackage manifest
*/
class DocumentationManifest {
const TEMPLATES_DIR = 'documentation';
protected $base;
protected $cache;
protected $cacheKey;
protected $inited;
protected $forceRegen;
protected $pages = array();
private $entity;
/**
* Constructs a new template manifest. The manifest is not actually built
* or loaded from cache until needed.
*
* @param bool $includeTests Include tests in the manifest.
* @param bool $forceRegen Force the manifest to be regenerated.
*/
public function __construct($forceRegen = false) {
$this->cacheKey = 'manifest';
$this->forceRegen = $forceRegen;
$this->cache = SS_Cache::factory('DocumentationManifest', 'Core', array(
'automatic_serialization' => true,
'lifetime' => null
));
}
/**
*
*/
protected function init() {
if (!$this->forceRegen && $data = $this->cache->load($this->cacheKey)) {
$this->pages = $data;
$this->inited = true;
} else {
$this->regenerate();
}
}
/**
* Returns a map of all documentation pages.
*
* @return array
*/
public function getPages() {
if (!$this->inited) {
$this->init();
}
return $this->pages;
}
/**
* Returns a particular page for the requested URL.
*
* @return DocumentationPage
*/
public function getPage($url) {
$pages = $this->getPages();
if(!isset($pages[$url])) {
return null;
}
$record = $pages[$url];
DocumentationService::load_automatic_registration();
foreach(DocumentationService::get_registered_entities() as $entity) {
foreach($entity->getVersions() as $version) {
foreach($version->getSupportedLanguages() as $language) {
if(strpos($record['filepath'], $language->getPath()) !== false) {
$page = Injector::inst()->create(
$record['type'],
$language,
$record['basename'],
$record['filepath']
);
return $page;
}
}
}
}
}
/**
* Regenerates the manifest by scanning the base path.
*
* @param bool $cache
*/
public function regenerate($cache = true) {
$finder = new DocumentationManifestFileFinder();
$finder->setOptions(array(
'dir_callback' => array($this, 'handleFolder'),
'file_callback' => array($this, 'handleFile')
));
DocumentationService::load_automatic_registration();
foreach(DocumentationService::get_registered_entities() as $entity) {
foreach($entity->getVersions() as $version) {
foreach($version->getSupportedLanguages() as $k => $v) {
$this->entity = $v;
$this->handleFolder('', $this->entity->getPath(), 0);
$finder->find($this->entity->getPath());
}
}
}
if ($cache) {
$this->cache->save($this->pages, $this->cacheKey);
}
$this->inited = true;
}
/**
*
*/
public function handleFolder($basename, $path, $depth) {
$folder = Injector::inst()->create(
'DocumentationFolder', $this->entity, $basename, $path
);
$this->pages[$folder->Link()] = array(
'title' => $folder->getTitle(),
'basename' => $basename,
'filepath' => $path,
'type' => 'DocumentationFolder'
);
}
/**
* Individual files can optionally provide a nice title and a better URL
* through the use of markdown meta data. This creates a new
* {@link DocumentationPage} instance for the file.
*
* If the markdown does not specify the title in the meta data it falls back
* to using the file name.
*
* @param string $basename
* @param string $path
* @param int $depth
*/
public function handleFile($basename, $path, $depth) {
$page = Injector::inst()->create(
'DocumentationPage',
$this->entity, $basename, $path
);
$this->pages[$page->Link()] = array(
'title' => $page->getTitle(),
'filepath' => $path,
'basename' => $basename,
'type' => 'DocumentationPage',
'summary' => $page->getSummary()
);
}
/**
* Generate an {@link ArrayList} of the pages to the given page.
*
* @return ArrayList
*/
public function generateBreadcrumbs($record) {
$output = new ArrayList();
// @todo
return $output;
}
/**
* Determine the next page from the given page.
*
* Relies on the fact when the manifest was built, it was generated in
* order.
*
* @param string
*
* @return ArrayData
*/
public function getNextPage($filepath) {
$grabNext = false;
foreach($this->getPages() as $url => $page) {
if($grabNext) {
return new ArrayData(array(
'Link' => $url,
'Title' => $page['title']
));
}
if($filepath == $page['filepath']) {
$grabNext = true;
}
}
return null;
}
/**
* Determine the previous page from the given page.
*
* Relies on the fact when the manifest was built, it was generated in
* order.
*
* @param string
*
* @return ArrayData
*/
public function getPreviousPage($filepath) {
$previousUrl = $previousPage = null;
foreach($this->getPages() as $url => $page) {
if($filepath == $page['filepath']) {
if($previousUrl) {
return new ArrayData(array(
'Link' => $previousUrl,
'Title' => $previousPage['title']
));
}
}
$previousUrl = $url;
$previousPage = $page;
}
return null;
}
}