mirror of
https://github.com/silverstripe/silverstripe-docsviewer
synced 2024-10-22 11:05:56 +02:00
43b6d42719
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().
268 lines
5.7 KiB
PHP
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;
|
|
}
|
|
}
|