mirror of
https://github.com/silverstripe/silverstripe-docsviewer
synced 2024-10-22 09:05:56 +00:00
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().
This commit is contained in:
parent
e80edc445e
commit
43b6d42719
README.md_config.php
code
DocumentationHelper.phpDocumentationManifest.phpDocumentationManifestFileFinder.phpDocumentationParser.phpDocumentationService.php
composer.jsoncontrollers
forms
models
DocumentationEntity.phpDocumentationEntityLanguage.phpDocumentationEntityVersion.phpDocumentationFolder.phpDocumentationPage.php
tasks
css
docs/en
javascript
templates
Includes
DocumentationBreadcrumbs.ssDocumentationNextPrevious.ssDocumentationSidebar.ssDocumentationTableContents.ss
Layout
tests
46
README.md
46
README.md
@ -13,35 +13,39 @@
|
||||
|
||||
## Summary
|
||||
|
||||
Reads text files from a given list of folders from your installation and
|
||||
provides a web interface for viewing.
|
||||
Reads markdown files from a given list of folders from your installation and
|
||||
provides a web interface for viewing the documentation. Ideal for providing
|
||||
documentation alongside your module or project code.
|
||||
|
||||
To read documentation go to yoursite.com/dev/docs/
|
||||
A variation of this module powers the main SilverStripe developer documentation
|
||||
and the user help websites.
|
||||
|
||||
For more documentation on how to use the module please read /docs/Writing-Documentation.md
|
||||
(or via this in /dev/docs/docsviewer/Writing-Documentation in your webbrowser)
|
||||
|
||||
**Note** This module assumes you are using numeric values for your versions.
|
||||
## Installation
|
||||
|
||||
### HTML Publishing
|
||||
composer require "silverstripe/docsviewer" "dev-master"
|
||||
|
||||
If you wish to generate a truly static version of your documentation after it
|
||||
has been rendered through the website, add the [Static Publisher](https://github.com/silverstripe-labs/silverstripe-staticpublisher)
|
||||
module to your documentation project and set the following configuration in your
|
||||
applications config.yml:
|
||||
## Usage
|
||||
|
||||
```
|
||||
StaticExporter:
|
||||
extensions:
|
||||
- DocumentationStaticPublisherExtension
|
||||
```
|
||||
After installing the files via composer, rebuild the SilverStripe database..
|
||||
|
||||
If you don't plan on using static publisher for anything else and you have the
|
||||
cms module installed, make sure you disable the CMS from being published.
|
||||
sake dev/build
|
||||
|
||||
Again, in your applications config.yml file
|
||||
Then start by viewing the documentation at `yoursite.com/dev/docs`.
|
||||
|
||||
```
|
||||
StaticExporter:
|
||||
disable_sitetree_export: true
|
||||
```
|
||||
Out of the box the module will display the documentation files that have been
|
||||
bundled into any of your installed modules. To configure what is shown in the
|
||||
documentation viewer see the detailed [documentation](docs/en/configuration.md).
|
||||
|
||||
For more information about how to use the module see each of the documentation
|
||||
|
||||
* [Configuration](docs/en/configuration.md)
|
||||
* [Markdown Syntax](docs/en/markdown.md)
|
||||
* [Syntax Highlighting](docs/en/syntax-highlighting.md)
|
||||
* [Publishing Static Files](docs/en/statichtml.md)
|
||||
|
||||
## License
|
||||
|
||||
See LICENSE
|
@ -15,9 +15,3 @@ if(!defined('DOCSVIEWER_DIR')) {
|
||||
|
||||
define('DOCSVIEWER_DIR', array_pop($dir));
|
||||
}
|
||||
|
||||
// define filetypes to ignore
|
||||
DocumentationService::set_ignored_files(array(
|
||||
'.', '..', '.DS_Store',
|
||||
'.svn', '.git', 'assets', 'themes', '_images', '_resources'
|
||||
));
|
||||
|
@ -40,4 +40,72 @@ class DocumentationHelper {
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* String helper for cleaning a file name to a readable version.
|
||||
*
|
||||
* @param string $name to convert
|
||||
*
|
||||
* @return string $name output
|
||||
*/
|
||||
public static function clean_page_name($name) {
|
||||
$name = self::trim_extension_off($name);
|
||||
$name = self::trim_sort_number($name);
|
||||
|
||||
$name = str_replace(array('-', '_'), ' ', $name);
|
||||
|
||||
|
||||
return ucwords(trim($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* String helper for cleaning a file name to a URL safe version.
|
||||
*
|
||||
* @param string $name to convert
|
||||
*
|
||||
* @return string $name output
|
||||
*/
|
||||
public static function clean_page_url($name) {
|
||||
$name = str_replace(array(' '), '_', $name);
|
||||
|
||||
$name = self::trim_extension_off($name);
|
||||
$name = self::trim_sort_number($name);
|
||||
|
||||
if(preg_match('/^[\/]?index[\/]?/', $name)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return strtolower($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes leading numbers from pages (used to control sort order).
|
||||
*
|
||||
* @param string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function trim_sort_number($name) {
|
||||
$name = preg_replace("/^[0-9]*[_-]+/", '', $name);
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to strip the extension off and return the name without
|
||||
* the extension.
|
||||
*
|
||||
* @param string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function trim_extension_off($name) {
|
||||
if(strrpos($name,'.') !== false) {
|
||||
return substr($name, 0, strrpos($name,'.'));
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
267
code/DocumentationManifest.php
Normal file
267
code/DocumentationManifest.php
Normal file
@ -0,0 +1,267 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
38
code/DocumentationManifestFileFinder.php
Normal file
38
code/DocumentationManifestFileFinder.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
class DocumentationManifestFileFinder extends SS_FileFinder {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $ignored_files = array(
|
||||
'.', '..', '.ds_store',
|
||||
'.svn', '.git', 'assets', 'themes', '_images'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $default_options = array(
|
||||
'name_regex' => '/\.(md|markdown)$/i',
|
||||
'file_callback' => null,
|
||||
'dir_callback' => null,
|
||||
'ignore_vcs' => true
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function acceptDir($basename, $pathname, $depth) {
|
||||
$ignored = Config::inst()->get('DocumentationManifestFileFinder', 'ignored_files');
|
||||
|
||||
if($ignored) {
|
||||
if(in_array(strtolower($basename), $ignored)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -348,7 +348,7 @@ class DocumentationParser {
|
||||
|
||||
// relative path (relative to module base folder), without the filename.
|
||||
// For "sapphire/en/current/topics/templates", this would be "templates"
|
||||
$relativePath = dirname($page->getRelativePath());
|
||||
$relativePath = dirname($page->Link());
|
||||
if($relativePath == '.') $relativePath = '';
|
||||
|
||||
// file base link
|
||||
|
@ -5,43 +5,14 @@
|
||||
*
|
||||
* Handles the management of the documentation services delivered by the entity.
|
||||
*
|
||||
* Includes registering which components to document and handles the entities being
|
||||
* documented.
|
||||
* Includes registering which components to document and handles the entities
|
||||
* being documented.
|
||||
*
|
||||
* @package docsviewer
|
||||
*/
|
||||
|
||||
class DocumentationService {
|
||||
|
||||
/**
|
||||
* A mapping of known / popular languages to nice titles.
|
||||
*
|
||||
* @var Array
|
||||
*/
|
||||
private static $language_mapping = array(
|
||||
'en' => 'English',
|
||||
'fr' => 'Français',
|
||||
'de' => 'Deutsch'
|
||||
);
|
||||
|
||||
/**
|
||||
* Files to ignore from any documentation listing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $ignored_files = array(
|
||||
'.', '..', '.DS_Store',
|
||||
'.svn', '.git', 'assets', 'themes', '_images'
|
||||
);
|
||||
|
||||
/**
|
||||
* Case insenstive values to use as extensions on markdown pages. The
|
||||
* recommended extension is .md.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $valid_markdown_extensions = array('md', 'txt', 'markdown');
|
||||
|
||||
/**
|
||||
* Registered {@link DocumentationEntity} objects to include in the
|
||||
* documentation.
|
||||
@ -77,57 +48,6 @@ class DocumentationService {
|
||||
*/
|
||||
private static $automatic_registration = true;
|
||||
|
||||
/**
|
||||
* by default pagenumbers start high at 10.000
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private static $pagenumber_start_at = 10000;
|
||||
|
||||
/**
|
||||
* allow the use of key/value pairs in comments
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private static $meta_comments_enabled = false;
|
||||
|
||||
/**
|
||||
* Return the allowed extensions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_valid_extensions() {
|
||||
return self::$valid_markdown_extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a given extension is a valid extension to be rendered.
|
||||
* Assumes that $ext has a leading dot as that is what $valid_extension uses.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_valid_extension($ext) {
|
||||
return in_array(strtolower($ext), self::get_valid_extensions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ignored files list
|
||||
*
|
||||
* @param array
|
||||
*/
|
||||
public static function set_ignored_files($files) {
|
||||
self::$ignored_files = $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of files which are ignored
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_ignored_files() {
|
||||
return self::$ignored_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set automatic registration of entities and documentation folders
|
||||
*
|
||||
@ -138,8 +58,9 @@ class DocumentationService {
|
||||
self::$automatic_registration = $bool;
|
||||
|
||||
if(!$bool) {
|
||||
// remove current registed entities when disabling automatic registration
|
||||
// needed to avoid caching issues when running all the tests
|
||||
// remove current registed entities when disabling automatic
|
||||
// registration needed to avoid caching issues when running all the
|
||||
// tests
|
||||
self::$registered_entities = array();
|
||||
}
|
||||
}
|
||||
@ -152,43 +73,6 @@ class DocumentationService {
|
||||
public static function automatic_registration_enabled() {
|
||||
return self::$automatic_registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the number to start default pagenumbering, allowing room for
|
||||
* custom pagenumbers below.
|
||||
*
|
||||
* @param int $number
|
||||
*/
|
||||
public static function start_pagenumbers_at($number = 10000) {
|
||||
if (is_int($number)) self::$pagenumber_start_at = $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the startlevel for default pagenumbering
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function get_pagenumber_start_at() {
|
||||
return self::$pagenumber_start_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow the use of key/value pairs in comments?
|
||||
*
|
||||
* @param bool $allow
|
||||
*/
|
||||
public static function enable_meta_comments($allow = true) {
|
||||
self::$meta_comments_enabled = (bool) $allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* can we use key/value pairs
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function meta_comments_enabled() {
|
||||
return self::$meta_comments_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the entities which are listed for documentation. Optionally only
|
||||
@ -216,38 +100,13 @@ class DocumentationService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a entity is registered with the documenter.
|
||||
* Register a entity to be included in the documentation. To unregister a
|
||||
* entity use {@link DocumentationService::unregister()}.
|
||||
*
|
||||
* @param String $entity entity name
|
||||
* @param String $version version
|
||||
* @param String $lang language
|
||||
*
|
||||
* @return DocumentationEntity $entity the registered entity
|
||||
*/
|
||||
public static function is_registered_entity($entity, $version = false, $lang = false) {
|
||||
$check = ($entity instanceof DocumentationEntity) ? $entity->getFolder() : (string) $entity;
|
||||
|
||||
if(isset(self::$registered_entities[$check])) {
|
||||
$entity = self::$registered_entities[$check];
|
||||
|
||||
if(($lang && !$entity->hasLanguage($lang)) || ($version && !$entity->hasVersion($version))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a entity to be included in the documentation. To unregister a entity
|
||||
* use {@link DocumentationService::unregister()}. Must include the trailing slash
|
||||
*
|
||||
* @param String $entity Name of entity to register
|
||||
* @param String $path Path to documentation root.
|
||||
* @param Float $version Version of entity.
|
||||
* @param String $title Nice title to use
|
||||
* @param string $entity Name of entity to register
|
||||
* @param string $path Path to documentation root.
|
||||
* @param float $version Version of entity.
|
||||
* @param string $title Nice title to use
|
||||
* @param bool $latest - return is this the latest release.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
@ -255,38 +114,40 @@ class DocumentationService {
|
||||
* @return DocumentationEntity
|
||||
*/
|
||||
public static function register($entity, $path, $version = '', $title = false, $latest = false) {
|
||||
if(!file_exists($path)) throw new InvalidArgumentException(sprintf('Path "%s" doesn\'t exist', $path));
|
||||
if(!file_exists($path)) {
|
||||
throw new InvalidArgumentException(sprintf('Path "%s" doesn\'t exist', $path));
|
||||
}
|
||||
|
||||
// add the entity to the registered array
|
||||
if(!isset(self::$registered_entities[$entity])) {
|
||||
// entity is completely new
|
||||
$output = new DocumentationEntity($entity, $version, $path, $title);
|
||||
|
||||
self::$registered_entities[$entity] = $output;
|
||||
$de = new DocumentationEntity($entity, $title);
|
||||
|
||||
self::$registered_entities[$entity] = $de;
|
||||
}
|
||||
else {
|
||||
// entity exists so add the version to it
|
||||
$output = self::$registered_entities[$entity];
|
||||
$output->addVersion($version, $path);
|
||||
$de = self::$registered_entities[$entity];
|
||||
}
|
||||
|
||||
if($latest)
|
||||
$output->setStableVersion($version);
|
||||
|
||||
return $output;
|
||||
|
||||
// create a new version of the entity and attach it the the entity
|
||||
$dve = new DocumentationEntityVersion($de, $path, $version, $latest);
|
||||
$de->addVersion($dve);
|
||||
|
||||
return $de;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a entity from being included in the documentation. Useful
|
||||
* for keeping {@link DocumentationService::$automatic_registration} enabled
|
||||
* but disabling entities which you do not want to show. Combined with a
|
||||
* {@link Director::isLive()} you can hide entities you don't want a client to see.
|
||||
* {@link Director::isLive()} you can hide entities you don't want a client
|
||||
* to see.
|
||||
*
|
||||
* If no version or lang specified then the whole entity is removed. Otherwise only
|
||||
* the specified version of the documentation.
|
||||
* If no version or lang specified then the whole entity is removed.
|
||||
* Otherwise only the specified version of the documentation.
|
||||
*
|
||||
* @param String $entity
|
||||
* @param String $version
|
||||
* @param string $entity
|
||||
* @param string $version
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@ -296,9 +157,7 @@ class DocumentationService {
|
||||
|
||||
if($version) {
|
||||
$entity->removeVersion($version);
|
||||
}
|
||||
else {
|
||||
// only given a entity so unset the whole entity
|
||||
} else {
|
||||
unset(self::$registered_entities[$entityName]);
|
||||
}
|
||||
|
||||
@ -315,292 +174,25 @@ class DocumentationService {
|
||||
* @see {@link DocumentationService::set_automatic_registration()}
|
||||
*/
|
||||
public static function load_automatic_registration() {
|
||||
if(self::automatic_registration_enabled()) {
|
||||
$entities = scandir(BASE_PATH);
|
||||
if(!self::automatic_registration_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if($entities) {
|
||||
foreach($entities as $key => $entity) {
|
||||
$dir = is_dir(Controller::join_links(BASE_PATH, $entity));
|
||||
$ignored = in_array($entity, self::get_ignored_files(), true);
|
||||
|
||||
if($dir && !$ignored) {
|
||||
// check to see if it has docs
|
||||
$docs = Director::baseFolder() . '/' . Controller::join_links($entity, 'docs');
|
||||
|
||||
if(is_dir($docs)) {
|
||||
self::register($entity, $docs, 'current', $entity, true);
|
||||
}
|
||||
$entities = scandir(BASE_PATH);
|
||||
|
||||
if($entities) {
|
||||
foreach($entities as $key => $entity) {
|
||||
$dir = is_dir(Controller::join_links(BASE_PATH, $entity));
|
||||
|
||||
if($dir) {
|
||||
// check to see if it has docs
|
||||
$docs = Director::baseFolder() . '/' . Controller::join_links($entity, 'docs');
|
||||
|
||||
if(is_dir($docs)) {
|
||||
self::register($entity, $docs, 'current', $entity, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a language code to a 'nice' text string. Uses the
|
||||
* {@link self::$language_mapping} array combined with translatable.
|
||||
*
|
||||
* @param String $code code
|
||||
*/
|
||||
public static function get_language_title($lang) {
|
||||
$map = self::$language_mapping;
|
||||
|
||||
if(isset($map[$lang])) {
|
||||
return _t("DOCUMENTATIONSERVICE.LANG-$lang", $map[$lang]);
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find a documentation page given a path and a file name. It ignores the
|
||||
* extensions and simply compares the title.
|
||||
*
|
||||
* Name may also be a path /install/foo/bar.
|
||||
*
|
||||
* @param DocumentationEntity
|
||||
* @param array exploded url string
|
||||
* @param string version number
|
||||
* @param string lang code
|
||||
*
|
||||
* @return String|false - File path
|
||||
*/
|
||||
static function find_page($entity, $path, $version = '', $lang = 'en') {
|
||||
if($entity = self::is_registered_entity($entity, $version, $lang)) {
|
||||
return self::find_page_recursive($entity->getPath($version, $lang), $path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function for finding the goal of a path to a documentation
|
||||
* page
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function find_page_recursive($base, $goal) {
|
||||
$handle = (is_dir($base)) ? opendir($base) : false;
|
||||
|
||||
$name = self::trim_extension_off(strtolower(array_shift($goal)));
|
||||
if(!$name || $name == '/') $name = 'index';
|
||||
|
||||
|
||||
if($handle) {
|
||||
$ignored = self::get_ignored_files();
|
||||
|
||||
// ensure we end with a slash
|
||||
$base = rtrim($base, '/') .'/';
|
||||
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
if(in_array($file, $ignored)) continue;
|
||||
|
||||
$formatted = self::trim_extension_off(strtolower($file));
|
||||
|
||||
// the folder is the one that we are looking for.
|
||||
if(strtolower($name) == strtolower($formatted)) {
|
||||
|
||||
// if this file is a directory we could be displaying that
|
||||
// or simply moving towards the goal.
|
||||
if(is_dir(Controller::join_links($base, $file))) {
|
||||
|
||||
$base = $base . trim($file, '/') .'/';
|
||||
|
||||
// if this is a directory check that there is any more states to get
|
||||
// to in the goal. If none then what we want is the 'index.md' file
|
||||
if(count($goal) > 0) {
|
||||
return self::find_page_recursive($base, $goal);
|
||||
}
|
||||
else {
|
||||
// recurse but check for an index.md file next time around
|
||||
return self::find_page_recursive($base, array('index'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// goal state. End of recursion.
|
||||
// tidy up the URLs with single trailing slashes
|
||||
$result = $base . ltrim($file, '/');
|
||||
|
||||
if(is_dir($result)) $result = (rtrim($result, '/') . '/');
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($handle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* String helper for cleaning a file name to a readable version.
|
||||
*
|
||||
* @param String $name to convert
|
||||
*
|
||||
* @return String $name output
|
||||
*/
|
||||
public static function clean_page_name($name) {
|
||||
// remove dashs and _
|
||||
$name = str_replace(array('-', '_'), ' ', $name);
|
||||
|
||||
// remove extension
|
||||
$name = self::trim_extension_off($name);
|
||||
|
||||
// if it starts with a number strip and contains a space strip it off
|
||||
if(strpos($name, ' ') !== false) {
|
||||
$space = strpos($name, ' ');
|
||||
$short = substr($name, 0, $space);
|
||||
|
||||
if(is_numeric($short)) {
|
||||
$name = substr($name, $space);
|
||||
}
|
||||
}
|
||||
|
||||
// convert first letter
|
||||
return ucfirst(trim($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to strip the extension off and return the name without
|
||||
* the extension. If you need the extension see {@link get_extension()}
|
||||
*
|
||||
* @param string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function trim_extension_off($name) {
|
||||
$ext = self::get_extension($name);
|
||||
|
||||
if($ext) {
|
||||
if(self::is_valid_extension($ext)) {
|
||||
return substr($name, 0, strrpos($name,'.'));
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extension from a string. If you want to trim the extension
|
||||
* off the end of the string see {@link trim_extension_off()}
|
||||
*
|
||||
* @param string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_extension($name) {
|
||||
return substr(strrchr($name,'.'), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the children from a given entity sorted by Title using natural ordering.
|
||||
* It is used for building the tree of the page.
|
||||
*
|
||||
* @param DocumentationEntity path
|
||||
* @param string - an optional path within a entity
|
||||
* @param bool enable several recursive calls (more than 1 level)
|
||||
* @param string - version to use
|
||||
* @param string - lang to use
|
||||
*
|
||||
* @throws Exception
|
||||
* @return ArrayList
|
||||
*/
|
||||
public static function get_pages_from_folder($entity, $relativePath = false, $recursive = true, $version = 'trunk', $lang = 'en') {
|
||||
$output = new ArrayList();
|
||||
$metaCommentsEnabled = self::meta_comments_enabled();
|
||||
$pages = array();
|
||||
|
||||
if(!$entity instanceof DocumentationEntity)
|
||||
user_error("get_pages_from_folder must be passed a entity", E_USER_ERROR);
|
||||
|
||||
$path = $entity->getPath($version, $lang);
|
||||
|
||||
|
||||
if(self::is_registered_entity($entity)) {
|
||||
self::get_pages_from_folder_recursive($path, $relativePath, $recursive, $pages);
|
||||
}
|
||||
else {
|
||||
return user_error("$entity is not registered", E_USER_WARNING);
|
||||
}
|
||||
|
||||
if(count($pages) > 0) {
|
||||
$pagenumber = self::get_pagenumber_start_at();
|
||||
natsort($pages);
|
||||
|
||||
foreach($pages as $key => $pagePath) {
|
||||
|
||||
// get file name from the path
|
||||
$file = ($pos = strrpos($pagePath, '/')) ? substr($pagePath, $pos + 1) : $pagePath;
|
||||
|
||||
$page = new DocumentationPage();
|
||||
$page->setTitle(self::clean_page_name($file));
|
||||
$relative = str_replace($path, '', $pagePath);
|
||||
|
||||
// if no extension, put a slash on it
|
||||
if(strpos($relative, '.') === false) $relative .= '/';
|
||||
|
||||
$page->setEntity($entity);
|
||||
$page->setRelativePath($relative);
|
||||
$page->setVersion($version);
|
||||
$page->setLang($lang);
|
||||
|
||||
// does this page act as a folder?
|
||||
$path = $page->getPath();
|
||||
if (is_dir($path)) { $page->setIsFolder(true); }
|
||||
|
||||
$page->setPagenumber($pagenumber++);
|
||||
|
||||
// we need the markdown to get the comments
|
||||
if ($metaCommentsEnabled) $page->getMarkdown();
|
||||
|
||||
$output->push($page);
|
||||
}
|
||||
}
|
||||
|
||||
return ($metaCommentsEnabled)? $output->sort('pagenumber') : $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively search through a given folder
|
||||
*
|
||||
* @see {@link DocumentationService::get_pages_from_folder}
|
||||
*/
|
||||
private static function get_pages_from_folder_recursive($base, $relative, $recusive, &$pages) {
|
||||
if(!is_dir($base)) throw new Exception(sprintf('%s is not a folder', $folder));
|
||||
|
||||
$folder = Controller::join_links($base, $relative);
|
||||
|
||||
if(!is_dir($folder)) return false;
|
||||
|
||||
$handle = opendir($folder);
|
||||
|
||||
if($handle) {
|
||||
$ignore = self::get_ignored_files();
|
||||
$files = array();
|
||||
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
if(!in_array($file, $ignore)) {
|
||||
|
||||
$path = Controller::join_links($folder, $file);
|
||||
$relativeFilePath = Controller::join_links($relative, $file);
|
||||
|
||||
if(is_dir($path)) {
|
||||
// dir
|
||||
$pages[] = $relativeFilePath;
|
||||
|
||||
if($recusive) self::get_pages_from_folder_recursive($base, $relativeFilePath, $recusive, $pages);
|
||||
}
|
||||
else if(self::is_valid_extension(self::get_extension($path))) {
|
||||
// file we want
|
||||
$pages[] = $relativeFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($handle);
|
||||
}
|
||||
}
|
||||
|
109
code/controllers/DocumentationSearchController.php
Normal file
109
code/controllers/DocumentationSearchController.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
class DocumentationSearchController extends DocumentationViewer {
|
||||
|
||||
/**
|
||||
* Return an array of folders and titles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchedEntities() {
|
||||
$entities = array();
|
||||
|
||||
if(!empty($_REQUEST['Entities'])) {
|
||||
if(is_array($_REQUEST['Entities'])) {
|
||||
$entities = Convert::raw2att($_REQUEST['Entities']);
|
||||
}
|
||||
else {
|
||||
$entities = explode(',', Convert::raw2att($_REQUEST['Entities']));
|
||||
$entities = array_combine($entities, $entities);
|
||||
}
|
||||
}
|
||||
else if($entity = $this->getEntity()) {
|
||||
$entities[$entity->getFolder()] = Convert::raw2att($entity->getTitle());
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of versions that we're allowed to return
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchedVersions() {
|
||||
$versions = array();
|
||||
|
||||
if(!empty($_REQUEST['Versions'])) {
|
||||
if(is_array($_REQUEST['Versions'])) {
|
||||
$versions = Convert::raw2att($_REQUEST['Versions']);
|
||||
$versions = array_combine($versions, $versions);
|
||||
}
|
||||
else {
|
||||
$version = Convert::raw2att($_REQUEST['Versions']);
|
||||
$versions[$version] = $version;
|
||||
}
|
||||
}
|
||||
else if($version = $this->getVersion()) {
|
||||
$version = Convert::raw2att($version);
|
||||
$versions[$version] = $version;
|
||||
}
|
||||
|
||||
return $versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current search query
|
||||
*
|
||||
* @return HTMLText|null
|
||||
*/
|
||||
public function getSearchQuery() {
|
||||
if(isset($_REQUEST['Search'])) {
|
||||
return DBField::create_field('HTMLText', $_REQUEST['Search']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Past straight to results, display and encode the query
|
||||
*/
|
||||
public function results($data, $form = false) {
|
||||
$query = (isset($_REQUEST['Search'])) ? $_REQUEST['Search'] : false;
|
||||
|
||||
$search = new DocumentationSearch();
|
||||
$search->setQuery($query);
|
||||
$search->setVersions($this->getSearchedVersions());
|
||||
$search->setModules($this->getSearchedEntities());
|
||||
$search->setOutputController($this);
|
||||
|
||||
return $search->renderResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an search form which allows people to express more complex rules
|
||||
* and options than the plain search form.
|
||||
*
|
||||
* @todo client side filtering of checkable option based on the module selected.
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function AdvancedSearchForm() {
|
||||
$entities = DocumentationService::get_registered_entities();
|
||||
|
||||
return new DocumentationAdvancedSearchForm($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Advanced SearchForm can be displayed. It is enabled by
|
||||
* default, to disable use:
|
||||
*
|
||||
* <code>
|
||||
* DocumentationSearch::enable_advanced_search(false);
|
||||
* </code>
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAdvancedSearchEnabled() {
|
||||
return DocumentationSearch::advanced_search_enabled();
|
||||
}
|
||||
|
||||
}
|
@ -35,16 +35,6 @@ class DocumentationViewer extends Controller {
|
||||
* @var string
|
||||
*/
|
||||
private static $documentation_title = 'SilverStripe Documentation';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $version = "";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $language = "en";
|
||||
|
||||
/**
|
||||
* The string name of the currently accessed {@link DocumentationEntity}
|
||||
@ -52,27 +42,26 @@ class DocumentationViewer extends Controller {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $entity = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $remaining = array();
|
||||
|
||||
protected $entity = '';
|
||||
|
||||
/**
|
||||
* @var DocumentationPage
|
||||
*/
|
||||
public $currentLevelOnePage;
|
||||
protected $record;
|
||||
|
||||
/**
|
||||
* @var String Same as the routing pattern set through Director::addRules().
|
||||
* @config
|
||||
*
|
||||
* @var string same as the routing pattern set through Director::addRules().
|
||||
*/
|
||||
protected static $link_base = 'dev/docs/';
|
||||
private static $link_base = 'dev/docs/';
|
||||
|
||||
/**
|
||||
* @var String|array Optional permission check
|
||||
* @config
|
||||
*
|
||||
* @var string|array Optional permission check
|
||||
*/
|
||||
static $check_permission = 'ADMIN';
|
||||
private static $check_permission = 'ADMIN';
|
||||
|
||||
/**
|
||||
* @var array map of modules to edit links.
|
||||
@ -80,6 +69,13 @@ class DocumentationViewer extends Controller {
|
||||
*/
|
||||
private static $edit_links = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = array(
|
||||
'$Action' => 'handleAction'
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -162,217 +158,84 @@ class DocumentationViewer extends Controller {
|
||||
* Handle the url parsing for the documentation. In order to make this
|
||||
* user friendly this does some tricky things..
|
||||
*
|
||||
* The urls which should work
|
||||
* / - index page
|
||||
* /en/sapphire - the index page of sapphire (shows versions)
|
||||
* /2.4/en/sapphire - the docs for 2.4 sapphire.
|
||||
* /2.4/en/sapphire/installation/
|
||||
*
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function handleRequest(SS_HTTPRequest $request, DataModel $model) {
|
||||
DocumentationService::load_automatic_registration();
|
||||
|
||||
$response = parent::handleRequest($request, $model);
|
||||
|
||||
// if we submitted a form, let that pass
|
||||
if(!$request->isGET() || isset($_GET['action_results'])) {
|
||||
return parent::handleRequest($request, $model);
|
||||
}
|
||||
|
||||
$firstParam = ($request->param('Action')) ? $request->param('Action') : $request->shift();
|
||||
$secondParam = $request->shift();
|
||||
$thirdParam = $request->shift();
|
||||
|
||||
$this->Remaining = $request->shift(10);
|
||||
|
||||
// if no params passed at all then it's the homepage
|
||||
if(!$firstParam && !$secondParam && !$thirdParam) {
|
||||
return parent::handleRequest($request, $model);
|
||||
return $response;
|
||||
}
|
||||
|
||||
if($firstParam) {
|
||||
// allow assets
|
||||
if($firstParam == "assets") {
|
||||
return parent::handleRequest($request, $model);
|
||||
}
|
||||
|
||||
// check for permalinks
|
||||
if($link = DocumentationPermalinks::map($firstParam)) {
|
||||
// the first param is a shortcode for a page so redirect the user to
|
||||
// the short code.
|
||||
$this->response = new SS_HTTPResponse();
|
||||
$this->redirect($link, 301); // 301 permanent redirect
|
||||
|
||||
return $this->response;
|
||||
|
||||
}
|
||||
// look up the manifest to see find the nearest match against the
|
||||
// list of the URL. If the URL exists then set that as the current
|
||||
// page to match against.
|
||||
if($record = $this->getManifest()->getPage($this->request->getURL())) {
|
||||
$this->record = $record;
|
||||
|
||||
// check to see if the request is a valid entity. If it isn't, then we
|
||||
// need to throw a 404.
|
||||
if(!DocumentationService::is_registered_entity($firstParam)) {
|
||||
return $this->throw404();
|
||||
}
|
||||
|
||||
$this->entity = $firstParam;
|
||||
$this->language = $secondParam;
|
||||
|
||||
if(isset($thirdParam) && (is_numeric($thirdParam) || in_array($thirdParam, array('master', 'trunk')))) {
|
||||
$this->version = $thirdParam;
|
||||
}
|
||||
else {
|
||||
// current version so store one area para
|
||||
array_unshift($this->Remaining, $thirdParam);
|
||||
|
||||
$this->version = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 'current' version mapping
|
||||
$entity = DocumentationService::is_registered_entity($this->entity, null, $this->getLang());
|
||||
$type = get_class($this->record);
|
||||
$body = $this->renderWith(array(
|
||||
"DocumentationViewer_{$type}",
|
||||
"DocumentationViewer"
|
||||
));
|
||||
|
||||
if($entity) {
|
||||
$current = $entity->getStableVersion();
|
||||
$version = $this->getVersion();
|
||||
|
||||
if(!$version) {
|
||||
$this->version = $current;
|
||||
}
|
||||
|
||||
// Check if page exists, otherwise return 404
|
||||
if(!$this->locationExists()) {
|
||||
return $this->throw404();
|
||||
}
|
||||
|
||||
|
||||
return parent::handleRequest($request, $model);
|
||||
}
|
||||
|
||||
return $this->throw404();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function for throwing a 404 error from the {@link handleRequest}
|
||||
* method.
|
||||
*
|
||||
* @return HttpResponse
|
||||
*/
|
||||
function throw404() {
|
||||
$this->init();
|
||||
|
||||
$class = get_class($this);
|
||||
|
||||
$body = $this->renderWith(array("{$class}_error", $class));
|
||||
$this->response = new SS_HTTPResponse($body, 404);
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom templates for each of the sections.
|
||||
*/
|
||||
function getViewer($action) {
|
||||
// count the number of parameters after the language, version are taken
|
||||
// into account. This automatically includes ' ' so all the counts
|
||||
// are 1 more than what you would expect
|
||||
|
||||
if($this->entity || $this->Remaining) {
|
||||
|
||||
$paramCount = count($this->Remaining);
|
||||
|
||||
if($paramCount == 0) {
|
||||
return parent::getViewer('folder');
|
||||
}
|
||||
else if($entity = $this->getEntity()) {
|
||||
// if this is a folder return the folder listing
|
||||
if($this->locationExists() == 2) {
|
||||
return parent::getViewer('folder');
|
||||
}
|
||||
}
|
||||
return new SS_HTTPResponse($body, 200);
|
||||
}
|
||||
else {
|
||||
return parent::getViewer('home');
|
||||
}
|
||||
$this->init();
|
||||
|
||||
return parent::getViewer($action);
|
||||
$class = get_class($this);
|
||||
$body = $this->renderWith(array("{$class}_error", $class));
|
||||
|
||||
return new SS_HTTPResponse($body, 404);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current version. If no version is set then it is the current
|
||||
* set version so need to pull that from the {@link Entity}.
|
||||
*
|
||||
* @return String
|
||||
* @return string
|
||||
*/
|
||||
function getVersion() {
|
||||
if($this->version) return $this->version;
|
||||
|
||||
if($entity = $this->getEntity()) {
|
||||
$this->version = $entity->getStableVersion();
|
||||
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
return false;
|
||||
public function getVersion() {
|
||||
return ($this->record) ? $this->record->getEntity()->getVersion() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current language
|
||||
* Returns the current language.
|
||||
*
|
||||
* @return String
|
||||
* @return string
|
||||
*/
|
||||
function getLang() {
|
||||
return $this->language;
|
||||
public function getLanguage() {
|
||||
return ($this->record) ? $this->record->getEntity()->getLanguage() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getManifest() {
|
||||
return new DocumentationManifest((isset($_GET['flush'])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the available languages for the {@link Entity}.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getLanguages() {
|
||||
$entity = $this->getEntity();
|
||||
|
||||
if($entity) {
|
||||
return $entity->getLanguages();
|
||||
}
|
||||
|
||||
return array('en' => 'English');
|
||||
public function getLanguages() {
|
||||
return ($this->record) ? $this->record->getEntity()->getSupportedLanguages() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the versions loaded for the current {@link DocumentationEntity}.
|
||||
* the filesystem then they are loaded under the 'Current' namespace.
|
||||
* the file system then they are loaded under the 'Current' name space.
|
||||
*
|
||||
* @param String $entity name of {@link Entity} to limit it to eg sapphire
|
||||
* @return ArrayList
|
||||
*/
|
||||
function getVersions($entity = false) {
|
||||
if(!$entity) $entity = $this->entity;
|
||||
|
||||
$entity = DocumentationService::is_registered_entity($entity);
|
||||
if(!$entity) return false;
|
||||
|
||||
$versions = $entity->getVersions();
|
||||
$output = new ArrayList();
|
||||
|
||||
if($versions) {
|
||||
$lang = $this->getLang();
|
||||
$currentVersion = $this->getVersion();
|
||||
|
||||
foreach($versions as $key => $version) {
|
||||
if(!$version) continue;
|
||||
|
||||
$linkingMode = ($currentVersion == $version) ? 'current' : 'link';
|
||||
|
||||
$output->push(new ArrayData(array(
|
||||
'Title' => $version,
|
||||
'Link' => $this->Link(implode('/',$this->Remaining), $entity->getFolder(), $version),
|
||||
'LinkingMode' => $linkingMode,
|
||||
'Version' => $version // separate from title, we may want to make title nicer.
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
public function getVersions() {
|
||||
return ($this->record) ? $this->record->getEntity()->getVersions() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -381,8 +244,8 @@ class DocumentationViewer extends Controller {
|
||||
*
|
||||
* @return DataObject
|
||||
*/
|
||||
public function getEntities($version = false, $lang = false) {
|
||||
$entities = DocumentationService::get_registered_entities($version, $lang);
|
||||
public function getEntities() {
|
||||
$entities = DocumentationService::get_registered_entities();
|
||||
$output = new ArrayList();
|
||||
|
||||
$currentEntity = $this->getEntity();
|
||||
@ -392,12 +255,13 @@ class DocumentationViewer extends Controller {
|
||||
$mode = ($entity === $currentEntity) ? 'current' : 'link';
|
||||
$folder = $entity->getFolder();
|
||||
|
||||
$link = $this->Link(array(), $folder, false, $lang);
|
||||
$link = $entity->Link();
|
||||
|
||||
$content = false;
|
||||
if($page = $entity->getIndexPage($version, $lang)) {
|
||||
$content = DBField::create_field('HTMLText', DocumentationParser::parse($page, $link));
|
||||
}
|
||||
|
||||
// if($page = $entity->getIndexPage()) {
|
||||
// $content = DBField::create_field('HTMLText', DocumentationParser::parse($page, $link));
|
||||
// }
|
||||
|
||||
$output->push(new ArrayData(array(
|
||||
'Title' => $entity->getTitle(),
|
||||
@ -415,7 +279,7 @@ class DocumentationViewer extends Controller {
|
||||
/**
|
||||
* Get the currently accessed entity from the site.
|
||||
*
|
||||
* @return false|DocumentationEntity
|
||||
* @return DocumentationEntity
|
||||
*/
|
||||
public function getEntity() {
|
||||
if($this->entity) {
|
||||
@ -426,179 +290,7 @@ class DocumentationViewer extends Controller {
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple way to check for existence of page of folder
|
||||
* without constructing too much object state. Useful for
|
||||
* generating 404 pages. Returns 0 for not a page or
|
||||
* folder, returns 1 for a page and 2 for folder
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function locationExists() {
|
||||
$entity = $this->getEntity();
|
||||
|
||||
if($entity) {
|
||||
|
||||
$has_dir = is_dir(Controller::join_links(
|
||||
$entity->getPath($this->getVersion(), $this->getLang()),
|
||||
implode('/', $this->Remaining)
|
||||
));
|
||||
|
||||
if($has_dir) return 2;
|
||||
|
||||
$has_page = DocumentationService::find_page(
|
||||
$entity,
|
||||
$this->Remaining,
|
||||
$this->getVersion(),
|
||||
$this->getLang()
|
||||
);
|
||||
|
||||
if($has_page) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
function getPage() {
|
||||
$entity = $this->getEntity();
|
||||
|
||||
if(!$entity) return false;
|
||||
|
||||
$version = $this->getVersion();
|
||||
$lang = $this->getLang();
|
||||
|
||||
$absFilepath = DocumentationService::find_page(
|
||||
$entity,
|
||||
$this->Remaining,
|
||||
$version,
|
||||
$lang
|
||||
);
|
||||
|
||||
if($absFilepath) {
|
||||
$relativeFilePath = str_replace(
|
||||
$entity->getPath($version, $lang),
|
||||
'',
|
||||
$absFilepath
|
||||
);
|
||||
|
||||
$page = new DocumentationPage();
|
||||
$page->setRelativePath($relativeFilePath);
|
||||
$page->setEntity($entity);
|
||||
$page->setLang($lang);
|
||||
$page->setVersion($version);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the related pages to the current {@link DocumentationEntity} and
|
||||
* the children to those pages
|
||||
*
|
||||
* @todo this only handles 2 levels. Could make it recursive
|
||||
*
|
||||
* @return false|ArrayList
|
||||
*/
|
||||
function getEntityPages() {
|
||||
if($entity = $this->getEntity()) {
|
||||
$pages = DocumentationService::get_pages_from_folder($entity, null, self::$recursive_submenu, $this->getVersion(), $this->getLang());
|
||||
|
||||
if($pages) {
|
||||
foreach($pages as $page) {
|
||||
if(strtolower($page->Title) == "index") {
|
||||
$pages->remove($page);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$page->LinkingMode = 'link';
|
||||
$page->Children = $this->_getEntityPagesNested($page, $entity);
|
||||
|
||||
if (!empty($page->Children)) {
|
||||
$this->currentLevelOnePage = $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the pages under a given page. Recursive call for {@link getEntityPages()}
|
||||
*
|
||||
* @todo Need to rethink how to support pages which are pulling content from their children
|
||||
* i.e if a folder doesn't have 2 then it will load the first file in the folder
|
||||
* however it doesn't yet pass the highlighting to it.
|
||||
*
|
||||
* @param ArrayData CurrentPage
|
||||
* @param DocumentationEntity
|
||||
* @param int Depth of page in the tree
|
||||
*
|
||||
* @return ArrayList|false
|
||||
*/
|
||||
private function _getEntityPagesNested(&$page, $entity, $level = 0) {
|
||||
if(isset($this->Remaining[$level])) {
|
||||
// compare segment successively, e.g. with "changelogs/alpha/2.4.0-alpha",
|
||||
// first comparison on $level=0 is against "changelogs",
|
||||
// second comparison on $level=1 is against "changelogs/alpha", etc.
|
||||
$segments = array_slice($this->Remaining, 0, $level+1);
|
||||
|
||||
if(strtolower(implode('/', $segments)) == strtolower(trim($page->getRelativeLink(), '/'))) {
|
||||
|
||||
// its either in this section or is the actual link
|
||||
$page->LinkingMode = (isset($this->Remaining[$level + 1])) ? 'section' : 'current';
|
||||
|
||||
$relativePath = Controller::join_links(
|
||||
$entity->getPath($this->getVersion(), $this->getLang()),
|
||||
$page->getRelativePath()
|
||||
);
|
||||
|
||||
if(is_dir($relativePath)) {
|
||||
$children = DocumentationService::get_pages_from_folder(
|
||||
$entity,
|
||||
$page->getRelativePath(),
|
||||
self::$recursive_submenu,
|
||||
$this->getVersion(),
|
||||
$this->getLang()
|
||||
);
|
||||
|
||||
$segments = array();
|
||||
for($x = 0; $x <= $level; $x++) {
|
||||
$segments[] = $this->Remaining[$x];
|
||||
}
|
||||
|
||||
foreach($children as $child) {
|
||||
if(strtolower($child->Title) == "index") {
|
||||
$children->remove($child);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$child->LinkingMode = 'link';
|
||||
$child->Children = $this->_getEntityPagesNested($child, $entity, $level + 1);
|
||||
}
|
||||
|
||||
return $children;
|
||||
}
|
||||
} else {
|
||||
if ($page->getRelativeLink() == $this->Remaining[$level]) {
|
||||
$page->LinkingMode = 'current';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -612,7 +304,9 @@ class DocumentationViewer extends Controller {
|
||||
$page = $this->getPage();
|
||||
|
||||
if($page) {
|
||||
return DBField::create_field("HTMLText", $page->getHTML($this->getVersion(), $this->getLang()));
|
||||
return DBField::create_field("HTMLText", $page->getHTML(
|
||||
$this->getVersion(), $this->getLanguage()
|
||||
));
|
||||
}
|
||||
|
||||
// If no page found then we may want to get the listing of the folder.
|
||||
@ -621,13 +315,7 @@ class DocumentationViewer extends Controller {
|
||||
$url = $this->Remaining;
|
||||
|
||||
if($url && $entity) {
|
||||
$pages = DocumentationService::get_pages_from_folder(
|
||||
$entity,
|
||||
implode('/', $url),
|
||||
false,
|
||||
$this->getVersion(),
|
||||
$this->getLang()
|
||||
);
|
||||
// @todo manifest
|
||||
|
||||
return $this->customise(array(
|
||||
'Content' => false,
|
||||
@ -647,96 +335,40 @@ class DocumentationViewer extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a list of breadcrumbs for the user. Based off the remaining
|
||||
* params in the url
|
||||
* Generate a list of breadcrumbs for the user.
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function getBreadcrumbs() {
|
||||
if(!$this->Remaining) {
|
||||
$this->Remaining = array();
|
||||
if($this->record) {
|
||||
return $this->getManifest()->generateBreadcrumbs($this->record);
|
||||
}
|
||||
|
||||
$pages = array_merge(array($this->entity), $this->Remaining);
|
||||
$output = new ArrayList();
|
||||
|
||||
if($pages) {
|
||||
$path = array();
|
||||
$version = $this->getVersion();
|
||||
$lang = $this->getLang();
|
||||
|
||||
foreach($pages as $i => $title) {
|
||||
if($title) {
|
||||
// Don't add entity name, already present in Link()
|
||||
if($i > 0) $path[] = $title;
|
||||
|
||||
$output->push(new ArrayData(array(
|
||||
'Title' => DocumentationService::clean_page_name($title),
|
||||
'Link' => rtrim($this->Link($path, false, $version, $lang), "/"). "/"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
public function getPage() {
|
||||
return $this->record;
|
||||
}
|
||||
/**
|
||||
* Generate a string for the title tag in the URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPageTitle() {
|
||||
if($pages = $this->getBreadcrumbs()) {
|
||||
$output = "";
|
||||
|
||||
foreach($pages as $page) {
|
||||
$output = $page->Title .' – '. $output;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
return false;
|
||||
return ($this->record) ? $this->record->getBreadcrumbTitle() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base link to this documentation location
|
||||
* Return the base link to this documentation location.
|
||||
*
|
||||
* @param string $path - subfolder path
|
||||
* @param string $entity - name of entity
|
||||
* @param float $version - optional version
|
||||
* @param string $lang - optional lang
|
||||
*
|
||||
* @return String
|
||||
* @return string
|
||||
*/
|
||||
public function Link($path = false, $entity = false, $version = false, $lang = false) {
|
||||
$version = ($version === null) ? $this->getVersion() : $version;
|
||||
|
||||
$lang = (!$lang) ? $this->getLang() : $lang;
|
||||
|
||||
$entity = (!$entity && $this->entity) ? $this->entity : $entity;
|
||||
$action = '';
|
||||
|
||||
if(is_string($path)) {
|
||||
$action = $path;
|
||||
}
|
||||
else if(is_array($path)) {
|
||||
$action = implode('/', $path);
|
||||
}
|
||||
|
||||
// check for stable version: if so, remove version from link
|
||||
// (see DocumentationEntity->getRelativeLink() )
|
||||
$objEntity = $this->getEntity();
|
||||
if ($objEntity && $objEntity->getStableVersion() == $version) $version = '';
|
||||
|
||||
public function Link() {
|
||||
$link = Controller::join_links(
|
||||
Director::absoluteBaseURL(),
|
||||
self::get_link_base(),
|
||||
$entity,
|
||||
($entity) ? $lang : "", // only include lang for entity - sapphire/en vs en/
|
||||
($entity) ? $version :"",
|
||||
$action
|
||||
Config::inst()->get('DocumentationViewer', 'link_base')
|
||||
);
|
||||
|
||||
return $link;
|
||||
@ -751,7 +383,7 @@ class DocumentationViewer extends Controller {
|
||||
*/
|
||||
public function LanguageForm() {
|
||||
$langs = $this->getLanguages();
|
||||
|
||||
|
||||
$fields = new FieldList(
|
||||
$dropdown = new DropdownField(
|
||||
'LangCode',
|
||||
@ -764,9 +396,7 @@ class DocumentationViewer extends Controller {
|
||||
$actions = new FieldList(
|
||||
new FormAction('doLanguageForm', _t('DocumentationViewer.CHANGE', 'Change'))
|
||||
);
|
||||
|
||||
$dropdown->setDisabled(true);
|
||||
|
||||
|
||||
return new Form($this, 'LanguageForm', $fields, $actions);
|
||||
}
|
||||
|
||||
@ -780,27 +410,6 @@ class DocumentationViewer extends Controller {
|
||||
return $this->redirect($this->Link());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
*/
|
||||
public static function set_link_base($base) {
|
||||
self::$link_base = $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function get_link_base() {
|
||||
return self::$link_base;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link Form::FormObjectLink()}
|
||||
*/
|
||||
public function FormObjectLink($name) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Documentation Search Form. Allows filtering of the results by many entities
|
||||
* and multiple versions.
|
||||
@ -811,140 +420,11 @@ class DocumentationViewer extends Controller {
|
||||
if(!DocumentationSearch::enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$q = ($q = $this->getSearchQuery()) ? $q->NoHTML() : "";
|
||||
|
||||
$entities = $this->getSearchedEntities();
|
||||
$versions = $this->getSearchedVersions();
|
||||
|
||||
$fields = new FieldList(
|
||||
new TextField('Search', _t('DocumentationViewer.SEARCH', 'Search'), $q)
|
||||
);
|
||||
|
||||
if ($entities) $fields->push(
|
||||
new HiddenField('Entities', '', implode(',', array_keys($entities)))
|
||||
);
|
||||
|
||||
if ($versions) $fields->push(
|
||||
new HiddenField('Versions', '', implode(',', $versions))
|
||||
);
|
||||
|
||||
$actions = new FieldList(
|
||||
new FormAction('results', 'Search')
|
||||
);
|
||||
|
||||
$form = new Form($this, 'DocumentationSearchForm', $fields, $actions);
|
||||
$form->disableSecurityToken();
|
||||
$form->setFormMethod('GET');
|
||||
$form->setFormAction(self::$link_base . 'DocumentationSearchForm');
|
||||
|
||||
return $form;
|
||||
return new DocumentationSearchForm($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of folders and titles
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchedEntities() {
|
||||
$entities = array();
|
||||
|
||||
if(!empty($_REQUEST['Entities'])) {
|
||||
if(is_array($_REQUEST['Entities'])) {
|
||||
$entities = Convert::raw2att($_REQUEST['Entities']);
|
||||
}
|
||||
else {
|
||||
$entities = explode(',', Convert::raw2att($_REQUEST['Entities']));
|
||||
$entities = array_combine($entities, $entities);
|
||||
}
|
||||
}
|
||||
else if($entity = $this->getEntity()) {
|
||||
$entities[$entity->getFolder()] = Convert::raw2att($entity->getTitle());
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of versions that we're allowed to return
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSearchedVersions() {
|
||||
$versions = array();
|
||||
|
||||
if(!empty($_REQUEST['Versions'])) {
|
||||
if(is_array($_REQUEST['Versions'])) {
|
||||
$versions = Convert::raw2att($_REQUEST['Versions']);
|
||||
$versions = array_combine($versions, $versions);
|
||||
}
|
||||
else {
|
||||
$version = Convert::raw2att($_REQUEST['Versions']);
|
||||
$versions[$version] = $version;
|
||||
}
|
||||
}
|
||||
else if($version = $this->getVersion()) {
|
||||
$version = Convert::raw2att($version);
|
||||
$versions[$version] = $version;
|
||||
}
|
||||
|
||||
return $versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current search query
|
||||
*
|
||||
* @return HTMLText|null
|
||||
*/
|
||||
public function getSearchQuery() {
|
||||
if(isset($_REQUEST['Search'])) {
|
||||
return DBField::create_field('HTMLText', $_REQUEST['Search']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Past straight to results, display and encode the query
|
||||
*/
|
||||
public function results($data, $form = false) {
|
||||
$query = (isset($_REQUEST['Search'])) ? $_REQUEST['Search'] : false;
|
||||
|
||||
$search = new DocumentationSearch();
|
||||
$search->setQuery($query);
|
||||
$search->setVersions($this->getSearchedVersions());
|
||||
$search->setModules($this->getSearchedEntities());
|
||||
$search->setOutputController($this);
|
||||
|
||||
return $search->renderResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an search form which allows people to express more complex rules
|
||||
* and options than the plain search form.
|
||||
*
|
||||
* @todo client side filtering of checkable option based on the module selected.
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function AdvancedSearchForm() {
|
||||
$entities = DocumentationService::get_registered_entities();
|
||||
|
||||
return new DocumentationAdvancedSearchForm($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Advanced SearchForm can be displayed. It is enabled by
|
||||
* default, to disable use:
|
||||
*
|
||||
* <code>
|
||||
* DocumentationSearch::enable_advanced_search(false);
|
||||
* </code>
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAdvancedSearchEnabled() {
|
||||
return DocumentationSearch::advanced_search_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the currently accessed version is out of date or
|
||||
* perhaps a future version rather than the stable edition
|
||||
@ -1026,7 +506,7 @@ class DocumentationViewer extends Controller {
|
||||
if($entity && isset(self::$edit_links[$entity->title])) {
|
||||
// build the edit link, using the version defined
|
||||
$url = self::$edit_links[$entity->title];
|
||||
$version = $page->getVersion();
|
||||
$version = $this->getVersion();
|
||||
|
||||
if($version == "trunk" && (isset($url['options']['rewritetrunktomaster']))) {
|
||||
if($url['options']['rewritetrunktomaster']) {
|
||||
@ -1037,10 +517,10 @@ class DocumentationViewer extends Controller {
|
||||
return str_replace(
|
||||
array('%entity%', '%lang%', '%version%', '%path%'),
|
||||
array(
|
||||
$entity->getFolder(),
|
||||
$page->getLang(),
|
||||
$entity->getBaseFolder(),
|
||||
$this->getLanguage(),
|
||||
$version,
|
||||
ltrim($page->getRelativePath(), '/')
|
||||
ltrim($page->getPath(), '/')
|
||||
),
|
||||
|
||||
$url['url']
|
||||
@ -1050,6 +530,27 @@ class DocumentationViewer extends Controller {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next page. Either retrieves the sibling of the current page
|
||||
* or return the next sibling of the parent page.
|
||||
*
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
public function getNextPage() {
|
||||
return ($this->record) ? $this->getManifest()->getNextPage($this->record->getPath()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous page. Either returns the previous sibling or the
|
||||
* parent of this page
|
||||
*
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
public function getPreviousPage() {
|
||||
return ($this->record) ? $this->getManifest()->getPreviousPage($this->record->getPath()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
|
32
code/forms/DocumentationSearchForm.php
Normal file
32
code/forms/DocumentationSearchForm.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
class DocumentationSearchForm extends Form {
|
||||
|
||||
public function __construct($controller) {
|
||||
$q = ($q = $controller->getSearchQuery()) ? $q->NoHTML() : "";
|
||||
|
||||
$entities = $controller->getSearchedEntities();
|
||||
$versions = $controller->getSearchedVersions();
|
||||
|
||||
$fields = new FieldList(
|
||||
new TextField('Search', _t('DocumentationViewer.SEARCH', 'Search'), $q)
|
||||
);
|
||||
|
||||
if ($entities) $fields->push(
|
||||
new HiddenField('Entities', '', implode(',', array_keys($entities)))
|
||||
);
|
||||
|
||||
if ($versions) $fields->push(
|
||||
new HiddenField('Versions', '', implode(',', $versions))
|
||||
);
|
||||
|
||||
$actions = new FieldList(
|
||||
new FormAction('results', 'Search')
|
||||
);
|
||||
|
||||
parent::__construct($controller, 'DocumentationSearchForm', $fields, $actions);
|
||||
|
||||
$this->disableSecurityToken();
|
||||
$this->setFormMethod('GET');
|
||||
$this->setFormAction($controller->Link('DocumentationSearchForm'));
|
||||
}
|
@ -1,24 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A {@link DocumentationEntity} is created when registering a path with
|
||||
* {@link DocumentationService::register()}.
|
||||
*
|
||||
* A {@link DocumentationEntity} represents a module or folder with
|
||||
* documentation not an individual page. Individual documentation pages are
|
||||
* represented by a {@link DocumentationPage}
|
||||
*
|
||||
* Each entity folder must have at least one language sub folder, which is.
|
||||
* determined through {@link addVersion()} and should not be included in the
|
||||
* $path argument.
|
||||
* documentation not an individual page. Entities are loaded via
|
||||
* {@link DocumentationService::register()} and individual pages are represented
|
||||
* by a {@link DocumentationPage} and are loaded by the manifest.
|
||||
*
|
||||
*
|
||||
* Versions are assumed to be in numeric format (e.g. '2.4'),
|
||||
*
|
||||
* They're also parsed through version_compare() in {@link getStableVersion()}
|
||||
* which assumes a certain format:
|
||||
*
|
||||
* @see http://php.net/manual/en/function.version-compare.php
|
||||
*
|
||||
* @package docsviewer
|
||||
* @subpackage models
|
||||
*/
|
||||
@ -29,92 +17,39 @@ class DocumentationEntity extends ViewableData {
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = array(
|
||||
'Name' => 'Text'
|
||||
'Title' => 'Text'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string $folder folder name
|
||||
* @var string $title
|
||||
*/
|
||||
private $folder;
|
||||
|
||||
/**
|
||||
* @var string $title nice title
|
||||
*/
|
||||
private $title;
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* @var array $version version numbers and the paths to each
|
||||
* @var string $folder
|
||||
*/
|
||||
private $versions = array();
|
||||
|
||||
protected $folder;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var ArrayList $versions
|
||||
*/
|
||||
private $stableVersion;
|
||||
|
||||
/**
|
||||
* @var Array $langs a list of available langauges
|
||||
*/
|
||||
private $langs = array();
|
||||
protected $versions;
|
||||
|
||||
/**
|
||||
* Constructor. You do not need to pass the langs to this as
|
||||
* it will work out the languages from the filesystem
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @param string $version version of this module
|
||||
* @param string $path Absolute path to this module (excluding language folders)
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct($folder, $version, $path, $title = false) {
|
||||
$this->addVersion($version, $path);
|
||||
$this->title = (!$title) ? $folder : $title;
|
||||
public function __construct($folder, $title = false) {
|
||||
$this->versions = new ArrayList();
|
||||
$this->folder = $folder;
|
||||
$this->title = (!$title) ? $folder : $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the languages which are available
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLanguages() {
|
||||
return $this->langs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this entity has a given language
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLanguage($lang) {
|
||||
return (in_array($lang, $this->langs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a langauge or languages to the entity
|
||||
*
|
||||
* @param Array|String languages
|
||||
*/
|
||||
public function addLanguage($language) {
|
||||
if(is_array($language)) {
|
||||
$this->langs = array_unique(array_merge($this->langs, $language));
|
||||
}
|
||||
else {
|
||||
$this->langs[] = $language;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the folder name of this module
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public function getFolder() {
|
||||
return $this->folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of this module
|
||||
* Get the title of this module.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@ -128,35 +63,22 @@ class DocumentationEntity extends ViewableData {
|
||||
* @return array
|
||||
*/
|
||||
public function getVersions() {
|
||||
return array_keys($this->versions);
|
||||
return $this->versions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|boo
|
||||
*/
|
||||
public function getStableVersion() {
|
||||
if(!$this->hasVersions()) return false;
|
||||
|
||||
if($this->stableVersion) {
|
||||
return $this->stableVersion;
|
||||
} else {
|
||||
$sortedVersions = $this->getVersions();
|
||||
|
||||
usort($sortedVersions, create_function('$a,$b', 'return version_compare($a,$b);'));
|
||||
|
||||
return array_pop($sortedVersions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String $version
|
||||
*/
|
||||
public function setStableVersion($version) {
|
||||
if(!$this->hasVersion($version)) {
|
||||
throw new InvalidArgumentException(sprintf('Version "%s" does not exist', $version));
|
||||
if(!$this->hasVersions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->stableVersion = $version;
|
||||
$sortedVersions = $this->getVersions();
|
||||
|
||||
usort($sortedVersions, create_function('$a,$b', 'return version_compare($a,$b);'));
|
||||
|
||||
return array_pop($sortedVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,7 +101,7 @@ class DocumentationEntity extends ViewableData {
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVersion($version) {
|
||||
return (isset($this->versions[$version]));
|
||||
return $this->versions->find('Version', $version);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,133 +110,65 @@ class DocumentationEntity extends ViewableData {
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVersions() {
|
||||
return (sizeof($this->versions) > 0);
|
||||
return $this->versions->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another version to this entity
|
||||
*
|
||||
* @param Float $version Version number
|
||||
* @param String $path path to folder
|
||||
* @param DocumentationEntityVersion
|
||||
*/
|
||||
public function addVersion($version = '', $path) {
|
||||
public function addVersion($version) {
|
||||
$this->versions->push($version);
|
||||
|
||||
$langs = scandir($path);
|
||||
$available = array();
|
||||
|
||||
if($langs) {
|
||||
foreach($langs as $key => $lang) {
|
||||
if(!is_dir($path . $lang) || strlen($lang) > 2 || in_array($lang, DocumentationService::get_ignored_files(), true)) {
|
||||
$lang = 'en';
|
||||
}
|
||||
|
||||
if(!in_array($lang, $available)) {
|
||||
$available[] = $lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->addLanguage($available);
|
||||
$this->versions[$version] = $path;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a version from this entity
|
||||
*
|
||||
* @param Float $version
|
||||
* @param float $version
|
||||
*
|
||||
*/
|
||||
public function removeVersion($version = '') {
|
||||
if(isset($this->versions[$version])) {
|
||||
unset($this->versions[$version]);
|
||||
}
|
||||
public function removeVersion($version) {
|
||||
$this->versions->remove('Version', $version);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute path to this documentation entity on the
|
||||
* filesystem
|
||||
* Return the absolute path to this documentation entity.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath($version = false, $lang = false) {
|
||||
if(!$version) {
|
||||
$version = $this->getStableVersion();
|
||||
}
|
||||
|
||||
if(!$lang) {
|
||||
$lang = 'en';
|
||||
}
|
||||
|
||||
if($this->hasVersion($version)) {
|
||||
$path = $this->versions[$version];
|
||||
}
|
||||
else {
|
||||
$versions = $this->getVersions();
|
||||
$path = $this->versions[$versions[0]];
|
||||
}
|
||||
|
||||
return Controller::join_links($path, $lang);
|
||||
public function getPath() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFolder() {
|
||||
return $this->folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the web accessible link to this Entity
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Link($version = false, $lang = false) {
|
||||
public function Link() {
|
||||
return Controller::join_links(
|
||||
Director::absoluteBaseURL(),
|
||||
$this->getRelativeLink($version, $lang)
|
||||
Config::inst()->get('DocumentationViewer', 'link_base'),
|
||||
$this->getFolder()
|
||||
);
|
||||
}
|
||||
|
||||
public function getRelativeLink($version = false, $lang = false) {
|
||||
if(!$lang) {
|
||||
$lang = 'en';
|
||||
}
|
||||
|
||||
if($version == $this->getStableVersion()) {
|
||||
$version = false;
|
||||
}
|
||||
|
||||
return Controller::join_links(
|
||||
DocumentationViewer::get_link_base(),
|
||||
$this->getFolder(),
|
||||
$lang,
|
||||
$version
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the summary / index text for this entity. Either pulled
|
||||
* from an index file or some other summary field
|
||||
*
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
public function getIndexPage($version, $lang = 'en') {
|
||||
$path = $this->getPath($version, $lang);
|
||||
$absFilepath = Controller::join_links($path, 'index.md');
|
||||
|
||||
if(file_exists($absFilepath)) {
|
||||
$relativeFilePath = str_replace($path, '', $absFilepath);
|
||||
|
||||
$page = new DocumentationPage();
|
||||
$page->setRelativePath($relativeFilePath);
|
||||
$page->setEntity($this);
|
||||
$page->setLang($lang);
|
||||
$page->setVersion($version);
|
||||
|
||||
return $page;
|
||||
} else {
|
||||
// fall back to reading the modules README.md
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return sprintf('DocumentationEntity: %s)', $this->getPath());
|
||||
}
|
||||
|
||||
}
|
83
code/models/DocumentationEntityLanguage.php
Normal file
83
code/models/DocumentationEntityLanguage.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package docsviewer
|
||||
*/
|
||||
class DocumentationEntityLanguage extends ViewableData {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* @var DocumentationEntityVersion
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* @param DocumentationEntityVersion $version
|
||||
* @param string $language
|
||||
*/
|
||||
public function __construct(DocumentationEntityVersion $version, $language) {
|
||||
$this->entity = $version;
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Link() {
|
||||
return Controller::join_links($this->entity->Link(), $this->language);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVersion() {
|
||||
return $this->entity->getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLanguage() {
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath() {
|
||||
return Controller::join_links($this->entity->getPath(), $this->language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBasePath() {
|
||||
return $this->entity->getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->entity->getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseFolder() {
|
||||
return $this->entity->getBaseFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedLanguages() {
|
||||
return $this->entity->getSupportedLanguages();
|
||||
}
|
||||
}
|
130
code/models/DocumentationEntityVersion.php
Normal file
130
code/models/DocumentationEntityVersion.php
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A more specific instance of a {@link DocumentationEntity}. Each instance of
|
||||
* a entity will have at least one of these objects attached to encapsulate
|
||||
* linking to a particular URL.
|
||||
*
|
||||
* Versions are assumed to be in numeric format (e.g. '2.4'),
|
||||
*
|
||||
* They're also parsed through version_compare() in {@link getStableVersion()}
|
||||
* which assumes a certain format:
|
||||
*
|
||||
* @see http://php.net/manual/en/function.version-compare.php
|
||||
*
|
||||
* Each {@link DocumentationEntityVersion} has a list of supported language
|
||||
* instances. All documentation in the docs folder must sit under a supported
|
||||
* language {@link DocumentationEntityLanguage}.
|
||||
*
|
||||
* @package docsviewer
|
||||
*/
|
||||
|
||||
class DocumentationEntityVersion extends ViewableData {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $supportedLanguages = array();
|
||||
|
||||
/**
|
||||
* @var DocumentationEntity
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $path, $version, $stable;
|
||||
|
||||
/**
|
||||
* @param DocumentationEntity $entity
|
||||
* @param string $path
|
||||
* @param float $version
|
||||
* @param boolean $stable
|
||||
*/
|
||||
public function __construct($entity, $path, $version, $stable) {
|
||||
$this->entity = $entity;
|
||||
$this->path = $path;
|
||||
$this->version = $version;
|
||||
$this->stable = $stable;
|
||||
|
||||
// check what languages that this instance will support.
|
||||
$langs = scandir($path);
|
||||
$available = array();
|
||||
|
||||
if($langs) {
|
||||
$possible = i18n::get_common_languages(true);
|
||||
$possible['en'] = true;
|
||||
|
||||
foreach($langs as $key => $lang) {
|
||||
if(isset($possible[$lang])) {
|
||||
$this->supportedLanguages[$lang] = Injector::inst()->create(
|
||||
'DocumentationEntityLanguage',
|
||||
$this,
|
||||
$lang
|
||||
);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Link() {
|
||||
if($this->stable) {
|
||||
return $this->entity->Link();
|
||||
}
|
||||
|
||||
return Controller::join_links($this->entity->Link(), $this->version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the languages which are available for this version of the entity.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedLanguages() {
|
||||
return $this->supportedLanguages;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this entity has a given language.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLanguageSupport($lang) {
|
||||
return (in_array($lang, $this->getSupportedLanguages()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float
|
||||
*/
|
||||
public function getVersion() {
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseFolder() {
|
||||
return $this->entity->getFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->entity->getTitle();
|
||||
}
|
||||
}
|
19
code/models/DocumentationFolder.php
Normal file
19
code/models/DocumentationFolder.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A specific documentation folder within a {@link DocumentationEntity}.
|
||||
*
|
||||
* Maps to a folder on the file system.
|
||||
*
|
||||
* @package docsviewer
|
||||
* @subpackage model
|
||||
*/
|
||||
class DocumentationFolder extends DocumentationPage {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle() {
|
||||
return DocumentationHelper::clean_page_name($this->filename);
|
||||
}
|
||||
}
|
@ -12,137 +12,42 @@
|
||||
*/
|
||||
class DocumentationPage extends ViewableData {
|
||||
|
||||
/**
|
||||
* @var DocumentationEntity
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* Stores the relative path (from the {@link DocumentationEntity} to
|
||||
* this page. The actual file name can be accessed via {@link $this->getFilename()}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $relativePath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $lang = 'en';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $isFolder = false;
|
||||
protected $summary;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var DocumentationEntityLanguage
|
||||
*/
|
||||
protected $pagenumber = 0;
|
||||
|
||||
/**
|
||||
* @param boolean
|
||||
*/
|
||||
public function setIsFolder($isFolder = false) {
|
||||
$this->isFolder = $isFolder;
|
||||
}
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @var string
|
||||
*/
|
||||
public function getIsFolder($isFolder = false) {
|
||||
return $this->isFolder;
|
||||
}
|
||||
protected $path, $filename;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $number
|
||||
* @param DocumentationEntityLanguage $entity
|
||||
* @param string $filename
|
||||
* @param string $path
|
||||
*/
|
||||
public function setPagenumber($number = 0) {
|
||||
if (is_int($number )) $this->pagenumber = $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DocumentationEntity
|
||||
*/
|
||||
public function getEntity() {
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DocumentationEntity
|
||||
*/
|
||||
public function setEntity($entity) {
|
||||
public function __construct(DocumentationEntityLanguage $entity, $filename, $path) {
|
||||
$this->filename = $filename;
|
||||
$this->path = $path;
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativePath() {
|
||||
return $this->relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
*/
|
||||
public function setRelativePath($path) {
|
||||
$this->relativePath = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getExtension() {
|
||||
return DocumentationService::get_extension($this->getRelativePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute path including version and lang folder.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @param bool $defaultFile - If this is a folder and this is set to true then getPath
|
||||
* will return the path of the first file in the folder
|
||||
* @return string
|
||||
*/
|
||||
public function getPath($defaultFile = false, $realpath = true) {
|
||||
if($this->entity) {
|
||||
$path = Controller::join_links(
|
||||
$this->entity->getPath($this->getVersion(), $this->lang),
|
||||
$this->getRelativePath()
|
||||
);
|
||||
|
||||
if(!is_dir($path) && $realpath) $path = realpath($path);
|
||||
else if($defaultFile) {
|
||||
$file = DocumentationService::find_page($this->entity, explode('/', $this->getRelativePath()));
|
||||
|
||||
if($file) $path = $file;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$path = $this->getRelativePath();
|
||||
}
|
||||
if(!file_exists($path)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Path could not be found. Module path: %s, file path: %s',
|
||||
$this->entity->getPath(),
|
||||
$this->getRelativePath()
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
return (is_dir($path)) ? rtrim($path, '/') . '/' : $path;
|
||||
return DocumentationService::get_extension($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,110 +56,39 @@ class DocumentationPage extends ViewableData {
|
||||
* @return string
|
||||
*/
|
||||
public function getBreadcrumbTitle($divider = ' - ') {
|
||||
$pathParts = explode('/', $this->getRelativePath());
|
||||
$pathParts = explode('/', $this->getRelativeLink());
|
||||
|
||||
// add the module to the breadcrumb trail.
|
||||
array_unshift($pathParts, $this->entity->getTitle());
|
||||
|
||||
$titleParts = array_map(array('DocumentationService', 'clean_page_name'), $pathParts);
|
||||
$titleParts = array_map(array('DocumentationHelper', 'clean_page_name'), $pathParts);
|
||||
|
||||
return implode($divider, $titleParts + array($this->getTitle()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public accessible link for this page.
|
||||
*
|
||||
* @param Boolean Absolute URL (incl. domain), or relative to webroot
|
||||
* @return string
|
||||
* @return DocumentationEntityLanguage
|
||||
*/
|
||||
public function getLink($absolute = true) {
|
||||
if($entity = $this->getEntity()) {
|
||||
$link = $this->getRelativeLink();
|
||||
$link = rtrim(DocumentationService::trim_extension_off($link), '/');
|
||||
|
||||
// folders should have a / on them. Looks nicer
|
||||
try {
|
||||
if(is_dir($this->getPath())) $link .= '/';
|
||||
}
|
||||
catch (Exception $e) {}
|
||||
}
|
||||
else {
|
||||
$link = $this->getPath(true);
|
||||
}
|
||||
|
||||
if($absolute) {
|
||||
$fullLink = Controller::join_links($entity->Link($this->getVersion(), $this->lang), $link);
|
||||
} else {
|
||||
$fullLink = Controller::join_links($entity->getRelativeLink($this->getVersion(), $this->lang), $link);
|
||||
}
|
||||
|
||||
return $fullLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relative to the module base, not the webroot.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativeLink() {
|
||||
$link = rtrim(DocumentationService::trim_extension_off($this->getRelativePath()), '/');
|
||||
|
||||
// folders should have a / on them. Looks nicer
|
||||
try {
|
||||
if(is_dir($this->getPath())) $link .= '/';
|
||||
} catch (Exception $e) {};
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
function getLang() {
|
||||
return $this->lang;
|
||||
}
|
||||
|
||||
function setLang($lang) {
|
||||
$this->lang = $lang;
|
||||
}
|
||||
|
||||
function getVersion() {
|
||||
return $this->version ? $this->version : $this->entity->getStableVersion();
|
||||
}
|
||||
|
||||
function setVersion($version) {
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
function setTitle($title) {
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
function getTitle() {
|
||||
return $this->title;
|
||||
public function getEntity() {
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a variable from the metadata field on this class
|
||||
*
|
||||
* @param string key
|
||||
* @param mixed value
|
||||
* @return string
|
||||
*/
|
||||
public function setMetaData($key, $value) {
|
||||
$this->$key = $value;
|
||||
public function getTitle() {
|
||||
if($this->title) {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
return DocumentationHelper::clean_page_name($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFilename() {
|
||||
$path = rtrim($this->relativePath, '/');
|
||||
|
||||
try {
|
||||
return (is_dir($this->getPath())) ? $path . '/' : $path;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
return $path;
|
||||
public function getSummary() {
|
||||
return $this->summary;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,20 +100,17 @@ class DocumentationPage extends ViewableData {
|
||||
*/
|
||||
public function getMarkdown($removeMetaData = false) {
|
||||
try {
|
||||
$path = $this->getPath(true);
|
||||
|
||||
if($path) {
|
||||
$ext = $this->getExtension();
|
||||
|
||||
if(empty($ext) || DocumentationService::is_valid_extension($ext)) {
|
||||
if ($md = file_get_contents($path)) {
|
||||
if ($this->title != 'Index') $this->getMetadataFromComments($md, $removeMetaData);
|
||||
}
|
||||
return $md;
|
||||
}
|
||||
if ($md = file_get_contents($this->path)) {
|
||||
if ($this->title != 'Index') {
|
||||
$this->populateMetaDataFromText($md, $removeMetaData);
|
||||
}
|
||||
|
||||
return $md;
|
||||
}
|
||||
}
|
||||
catch(InvalidArgumentException $e) {}
|
||||
catch(InvalidArgumentException $e) {
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -291,23 +122,65 @@ class DocumentationPage extends ViewableData {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHTML($version, $lang = 'en') {
|
||||
return DocumentationParser::parse($this, $this->entity->getRelativeLink($version, $lang));
|
||||
public function getHTML() {
|
||||
return DocumentationParser::parse(
|
||||
$this,
|
||||
$this->entity->Link()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativeLink() {
|
||||
$path = str_replace($this->entity->getPath(), '', $this->path);
|
||||
$url = explode('/', $path);
|
||||
|
||||
$url = implode('/', array_map(function($a) {
|
||||
return DocumentationHelper::clean_page_url($a);
|
||||
}, $url));
|
||||
|
||||
$url = rtrim($url, '/') . '/';
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL that will be required for the user to hit to view the
|
||||
* given document base name.
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Link() {
|
||||
return Controller::join_links(
|
||||
$this->entity->Link(),
|
||||
$this->getRelativeLink()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get metadata from the first html block in the page, then remove the
|
||||
* Return metadata from the first html block in the page, then remove the
|
||||
* block on request
|
||||
*
|
||||
* @param DocumentationPage $md
|
||||
* @param bool $remove
|
||||
*/
|
||||
public function getMetadataFromComments(&$md, $removeMetaData = false) {
|
||||
if($md && DocumentationService::meta_comments_enabled()) {
|
||||
|
||||
public function populateMetaDataFromText(&$md, $removeMetaData = false) {
|
||||
if($md) {
|
||||
// get the text up to the first whiteline
|
||||
$extPattern = "/^(.+)\n(\r)*\n/Uis";
|
||||
$matches = preg_match($extPattern, $md, $block);
|
||||
|
||||
if($matches && $block[1]) {
|
||||
$metaDataFound = false;
|
||||
|
||||
@ -320,36 +193,19 @@ class DocumentationPage extends ViewableData {
|
||||
|
||||
// check if a property exists for this key
|
||||
if (property_exists(get_class(), $key)) {
|
||||
$this->setMetaData($key, $meta['value'][$index]);
|
||||
$this->$key = $meta['value'][$index];
|
||||
|
||||
$metaDataFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// optionally remove the metadata block (only on the page that is displayed)
|
||||
|
||||
// optionally remove the metadata block (only on the page that
|
||||
// is displayed)
|
||||
if ($metaDataFound && $removeMetaData) {
|
||||
$md = preg_replace($extPattern, '', $md);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next page. Either retrieves the sibling of the current page
|
||||
* or return the next sibling of the parent page.
|
||||
*
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
public function getNextPage() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous page. Either returns the previous sibling or the
|
||||
* parent of this page
|
||||
*
|
||||
* @return DocumentationPage
|
||||
*/
|
||||
public function getPreviousPage() {
|
||||
|
||||
}
|
||||
}
|
14
code/tasks/DocumentationBuild.php
Normal file
14
code/tasks/DocumentationBuild.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
class DocumentationBuild extends BuildTask {
|
||||
|
||||
public function run($request) {
|
||||
$manifest = new DocumentationManifest(true);
|
||||
echo "<pre>";
|
||||
print_r($manifest->getPages());
|
||||
echo "</pre>";
|
||||
die();;
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -17,10 +17,6 @@
|
||||
"silverstripe/framework": "~3.1",
|
||||
"erusev/parsedown-extra": "0.1.0"
|
||||
},
|
||||
"repositories": [{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/wilr/parsedown"
|
||||
}],
|
||||
"suggest": {
|
||||
"silverstripe/staticpublisher": "Allows publishing documentation as HTML"
|
||||
}
|
||||
|
@ -57,6 +57,11 @@ html {
|
||||
padding: 15px 15px 14px;
|
||||
}
|
||||
|
||||
#sidebar .nav .current {
|
||||
background: #f6f7f8;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#layout {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
title: Syntax Highlighting
|
||||
|
||||
# Syntax Highlighting
|
||||
|
||||
The custom Markdown parser can render custom prefixes for code blocks, and
|
||||
@ -9,12 +11,12 @@ To include the syntax highlighter source, add the following to your `Documentati
|
||||
|
||||
|
||||
```php
|
||||
|
||||
Requirements::javascript(THIRDPARTY_DIR .'/jquery/jquery.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shCore.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushJScript.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushPHP.js');
|
||||
Requirements::javascript('sapphiredocs/thirdparty/syntaxhighlighter/scripts/shBrushXML.js');
|
||||
|
||||
// ... any additional syntaxes you want to support
|
||||
Requirements::combine_files(
|
||||
'syntaxhighlighter.js',
|
||||
@ -33,6 +35,3 @@ Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shCore.css')
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shCoreDefault.css');
|
||||
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shThemeRDark.css');
|
||||
```
|
||||
|
||||
You can overload the `DocumentationViewer` class and add a custom route through `Director::addRule()`
|
||||
if you prefer not to modify the module file.
|
||||
|
@ -1,62 +0,0 @@
|
||||
# Writing Documentation
|
||||
|
||||
The files have to end with the __.md__ or __.markdown__ extension. The
|
||||
documentation viewer will automatically replace hyphens (-) with spaces.
|
||||
|
||||
my-documentation-file.md
|
||||
|
||||
Translates to:
|
||||
|
||||
My documentation file
|
||||
|
||||
The module also support number prefixing for specifying the order of pages in
|
||||
the index pages and navigation trees.
|
||||
|
||||
03-foo.md
|
||||
1-bar.md
|
||||
4-baz.md
|
||||
|
||||
Will be output as the following in the listing views.
|
||||
|
||||
Bar
|
||||
Foo
|
||||
Baz
|
||||
|
||||
## Localization
|
||||
|
||||
All documentation folder should be localized. Even if you do not plan on supporting
|
||||
multiple languages you need to write your documentation in a 'en' subfolder
|
||||
|
||||
/module/docs/en/
|
||||
|
||||
|
||||
## Syntax
|
||||
|
||||
Documentation should be written in markdown with an `.md` extension attached.
|
||||
To view the syntax for page formatting check out [Daring Fireball](http://daringfireball.net/projects/markdown/syntax).
|
||||
|
||||
To see how to use the documentation from examples, I recommend opening up this
|
||||
file in your text editor and playing around. As these files are plain text, any
|
||||
text editor will be able to open and write markdown files.
|
||||
|
||||
|
||||
## Creating Hierarchy
|
||||
|
||||
The document viewer supports a hierarchical folder structure so you can categorize
|
||||
documentation and create topics.
|
||||
|
||||
## Directory Listing
|
||||
|
||||
Each folder you create should also contain a __index.md__ file which contains
|
||||
an overview of the module and related links. If no index is available, the
|
||||
default behaviour is to display an ordered list of links.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
The table of contents on each module page is generated based on where and what
|
||||
headers you use.
|
||||
|
||||
## Images and Files
|
||||
|
||||
If you want to attach images and other assets to a page you need to bundle those
|
||||
in a directory called _images at the same level as your documentation.
|
@ -2,9 +2,9 @@
|
||||
|
||||
## Registering what to document
|
||||
|
||||
By default the documentation system will parse all the directories in your project
|
||||
and include the documentation. If you want to only specify a few folders you can
|
||||
disable it and register your paths manually
|
||||
By default the documentation system will parse all the directories in your
|
||||
project and include the documentation. If you want to only specify a few folders
|
||||
you can disable it and register your paths manually
|
||||
|
||||
:::php
|
||||
// turns off automatic parsing of filesystem
|
||||
@ -65,13 +65,6 @@ Custom metadata can be added to the head of the MarkDown file like this:
|
||||
Make sure to add an empty line to separate the metadata from the content of
|
||||
the file.
|
||||
|
||||
You now need to explicitly enable the use of metadata by adding the following to
|
||||
your _config.php:
|
||||
|
||||
```php
|
||||
DocumentationService::enable_meta_comments();
|
||||
```
|
||||
|
||||
**Note:** SilverStripe needs to read the contents of each page to retrieve the
|
||||
metadata. This is expensive, so if you do not plan to use custom sorting,
|
||||
do not enable this feature:
|
||||
@ -101,4 +94,65 @@ Basically all DocumentationPage properties can be added to the metadata comment
|
||||
block. Beware that the outcome isn't always predictable. Adding a title
|
||||
property to the block will change the menu title, but the breadcrumbs
|
||||
are at this time not yet supported.
|
||||
|
||||
|
||||
|
||||
The files have to end with the __.md__ or __.markdown__ extension. The
|
||||
documentation viewer will automatically replace hyphens (-) with spaces.
|
||||
|
||||
my-documentation-file.md
|
||||
|
||||
Translates to:
|
||||
|
||||
My documentation file
|
||||
|
||||
The module also support number prefixing for specifying the order of pages in
|
||||
the index pages and navigation trees.
|
||||
|
||||
03-foo.md
|
||||
1-bar.md
|
||||
4-baz.md
|
||||
|
||||
Will be output as the following in the listing views.
|
||||
|
||||
Bar
|
||||
Foo
|
||||
Baz
|
||||
|
||||
## Localization
|
||||
|
||||
All documentation folder should be localized. Even if you do not plan on supporting
|
||||
multiple languages you need to write your documentation in a 'en' subfolder
|
||||
|
||||
/module/docs/en/
|
||||
|
||||
|
||||
## Syntax
|
||||
|
||||
Documentation should be written in markdown with an `.md` extension attached.
|
||||
To view the syntax for page formatting check out [Daring Fireball](http://daringfireball.net/projects/markdown/syntax).
|
||||
|
||||
To see how to use the documentation from examples, I recommend opening up this
|
||||
file in your text editor and playing around. As these files are plain text, any
|
||||
text editor will be able to open and write markdown files.
|
||||
|
||||
|
||||
## Creating Hierarchy
|
||||
|
||||
The document viewer supports a hierarchical folder structure so you can categorize
|
||||
documentation and create topics.
|
||||
|
||||
## Directory Listing
|
||||
|
||||
Each folder you create should also contain a __index.md__ file which contains
|
||||
an overview of the module and related links. If no index is available, the
|
||||
default behaviour is to display an ordered list of links.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
The table of contents on each module page is generated based on where and what
|
||||
headers you use.
|
||||
|
||||
## Images and Files
|
||||
|
||||
If you want to attach images and other assets to a page you need to bundle those
|
||||
in a directory called _images at the same level as your documentation.
|
@ -84,7 +84,7 @@ By default, the documentation is available in `dev/docs`. If you want it to
|
||||
live on the webroot instead of a subfolder or on another url address, add the
|
||||
following configuration to your _config.php file:
|
||||
|
||||
DocumentationViewer::set_link_base('');
|
||||
Config::inst()->update('DocumentationViewer', 'link_base', '');
|
||||
|
||||
Director::addRules(1, array(
|
||||
'$Action' => 'DocumentationViewer',
|
||||
|
@ -108,7 +108,8 @@ Currently this is not supported, as all HTML is generated on the fly.
|
||||
|
||||
### Can I contribute to the parser and rendering project?
|
||||
|
||||
Of course, the `docsviewer` code is BSD licensed - we're looking forward to your contributions!
|
||||
Of course, the `docsviewer` code is BSD licensed - we're looking forward to your
|
||||
contributions!
|
||||
|
||||
## Related ##
|
||||
|
24
docs/en/statichtml.md
Normal file
24
docs/en/statichtml.md
Normal file
@ -0,0 +1,24 @@
|
||||
title: Publishing Static Files
|
||||
|
||||
# HTML Publishing
|
||||
|
||||
If you wish to generate a truly static version of your documentation after it
|
||||
has been rendered through the website, add the [Static Publisher](https://github.com/silverstripe-labs/silverstripe-staticpublisher)
|
||||
module to your documentation project and set the following configuration in your
|
||||
applications config.yml:
|
||||
|
||||
```
|
||||
StaticExporter:
|
||||
extensions:
|
||||
- DocumentationStaticPublisherExtension
|
||||
```
|
||||
|
||||
If you don't plan on using static publisher for anything else and you have the
|
||||
cms module installed, make sure you disable the CMS from being published.
|
||||
|
||||
Again, in your applications config.yml file
|
||||
|
||||
```
|
||||
StaticExporter:
|
||||
disable_sitetree_export: true
|
||||
```
|
@ -77,7 +77,7 @@
|
||||
*
|
||||
* Transform a #table-of-contents div to a nested list
|
||||
*/
|
||||
if($("#content-column").length > 0) {
|
||||
if($("#table-contents-holder").length > 0) {
|
||||
var toc = '<div id="table-of-contents" class="open">' +
|
||||
'<h4>Table of contents<span class="updown">▼</span></h4><ul style="display: none;">';
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
var pageURL = window.location.href.replace(/#[a-zA-Z0-9\-\_]*/g, '');
|
||||
|
||||
var itemCount = 0;
|
||||
$('#content-column h1[id], #content-column h2[id], #content-column h3[id], #content-column h4[id]').each(function(i) {
|
||||
$('#content h1[id], #content h2[id], #content h3[id], #content h4[id]').each(function(i) {
|
||||
var current = $(this);
|
||||
var tagName = current.prop("tagName");
|
||||
if(typeof tagName == "String") tagName = tagName.toLowerCase();
|
||||
@ -99,15 +99,15 @@
|
||||
toc += '</ul></div>';
|
||||
|
||||
// Table of content location
|
||||
var title = $('#content-column h1:first');
|
||||
var title = $('#content h1:first');
|
||||
if (title.length > 0) {
|
||||
title.after(toc);
|
||||
} else {
|
||||
var breadcrums = $('#content-column .doc-breadcrumbs');
|
||||
var breadcrums = $('#content .doc-breadcrumbs');
|
||||
if (breadcrums.length > 0) {
|
||||
breadcrums.after(toc);
|
||||
} else {
|
||||
$('#content-column').prepend(toc);
|
||||
$('#content').prepend(toc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@
|
||||
*/
|
||||
var url = window.location.href;
|
||||
|
||||
$("#content-column h1[id], #content-column h2[id], #content-column h3[id], #content-column h4[id], #content-column h5[id], #content-column h6[id]").each(function() {
|
||||
$("#content h1[id], #content h2[id], #content h3[id], #content h4[id], #content h5[id], #content h6[id]").each(function() {
|
||||
var link = '<a class="heading-anchor-link" title="Link to this section" href="'+ url + '#' + $(this).attr('id') + '">¶</a>';
|
||||
$(this).append(' ' + link);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="doc-breadcrumbs">
|
||||
<p>
|
||||
<% loop Breadcrumbs %>
|
||||
<a href="$Link">$Title</a> <% if Last %><% else %>›<% end_if %>
|
||||
<% end_loop %>
|
||||
</p>
|
||||
<p>
|
||||
<% loop Breadcrumbs %>
|
||||
<a href="$Link">$Title</a> <% if Last %><% else %>›<% end_if %>
|
||||
<% end_loop %>
|
||||
</p>
|
||||
</div>
|
11
templates/Includes/DocumentationNextPrevious.ss
Normal file
11
templates/Includes/DocumentationNextPrevious.ss
Normal file
@ -0,0 +1,11 @@
|
||||
<% if NextPage || PreviousPage %>
|
||||
<div class="next-prev">
|
||||
<% if PreviousPage %>
|
||||
<p class="prev-link"><a href="$PreviousPage.Link">$PreviousPage.Title</a></p>
|
||||
<% end_if %>
|
||||
|
||||
<% if NextPage %>
|
||||
<p class="next-link"><a href="$NextPage.Link">$NextPage.Title</a></p>
|
||||
<% end_if %>
|
||||
</div>
|
||||
<% end_if %>
|
@ -1,16 +1,20 @@
|
||||
<div id="sidebar" class="box">
|
||||
<ul class="nav">
|
||||
<% loop Entities %>
|
||||
<li><a href="$Link" class="$LinkingMode top">$Title <% if IsFolder %><span class="is-folder">►</span><% end_if %></a>
|
||||
<% if LinkingMode == current %>
|
||||
<% if Children %>
|
||||
<ul>
|
||||
<% loop Children %>
|
||||
<li><a href="$Link" class="$LinkingMode">$Title</a></li>
|
||||
<% end_loop %>
|
||||
</ul><% end_if %>
|
||||
<% end_if %>
|
||||
</li>
|
||||
<% if DefaultEntity %>
|
||||
|
||||
<% else %>
|
||||
<li><a href="$Link" class="$LinkingMode top">$Title <% if IsFolder %><span class="is-folder">►</span><% end_if %></a>
|
||||
<% if LinkingMode == current %>
|
||||
<% if Children %>
|
||||
<ul>
|
||||
<% loop Children %>
|
||||
<li><a href="$Link" class="$LinkingMode">$Title</a></li>
|
||||
<% end_loop %>
|
||||
</ul><% end_if %>
|
||||
<% end_if %>
|
||||
</li>
|
||||
<% end_if %>
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
</div>
|
||||
|
1
templates/Includes/DocumentationTableContents.ss
Normal file
1
templates/Includes/DocumentationTableContents.ss
Normal file
@ -0,0 +1 @@
|
||||
<div id="table-contents-holder"></div>
|
@ -1,19 +0,0 @@
|
||||
<% if VersionWarning %>
|
||||
<% include DocumentationVersion_warning %>
|
||||
<% end_if %>
|
||||
|
||||
<div id="documentation-page">
|
||||
<div id="content-column">
|
||||
<% if Breadcrumbs %>
|
||||
<% include DocumentationBreadcrumbs %>
|
||||
<% end_if %>
|
||||
|
||||
$Content
|
||||
|
||||
<% if EditLink %>
|
||||
<% include DocumentationEditLink %>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% include DocumentationComments %>
|
17
templates/Layout/DocumentationViewer_DocumentationFolder.ss
Executable file
17
templates/Layout/DocumentationViewer_DocumentationFolder.ss
Executable file
@ -0,0 +1,17 @@
|
||||
<div class="box">
|
||||
<% if VersionWarning %>
|
||||
<% include DocumentationVersion_warning %>
|
||||
<% end_if %>
|
||||
|
||||
<h2>$Title</h2>
|
||||
|
||||
<% include DocumentationTableContents %>
|
||||
|
||||
<% loop Children %>
|
||||
<ul>
|
||||
<li><a href="$Link">$Title</a></li>
|
||||
</ul>
|
||||
<% end_loop %>
|
||||
|
||||
<% include DocumentationNextPrevious %>
|
||||
</div>
|
20
templates/Layout/DocumentationViewer_DocumentationPage.ss
Executable file
20
templates/Layout/DocumentationViewer_DocumentationPage.ss
Executable file
@ -0,0 +1,20 @@
|
||||
<div id="documentation-page" class="box">
|
||||
<% if VersionWarning %>
|
||||
<% include DocumentationVersion_warning %>
|
||||
<% end_if %>
|
||||
|
||||
<% if Breadcrumbs %>
|
||||
<% include DocumentationBreadcrumbs %>
|
||||
<% end_if %>
|
||||
|
||||
$Content
|
||||
|
||||
<% include DocumentationNextPrevious %>
|
||||
|
||||
<% if EditLink %>
|
||||
<% include DocumentationEditLink %>
|
||||
<% end_if %>
|
||||
|
||||
|
||||
<% include DocumentationComments %>
|
||||
</div>
|
@ -1,15 +0,0 @@
|
||||
<div id="module-home" class="box">
|
||||
<% if VersionWarning %>
|
||||
<% include DocumentationVersion_warning %>
|
||||
<% end_if %>
|
||||
|
||||
<% if Content %>
|
||||
$Content
|
||||
|
||||
<% if EditLink %>
|
||||
<% include DocumentationEditLink %>
|
||||
<% end_if %>
|
||||
<% else %>
|
||||
<h2>$Title</h2>
|
||||
<% end_if %>
|
||||
</div>
|
@ -1,13 +0,0 @@
|
||||
<div id="home">
|
||||
<h2><% _t('DOCUMENTEDMODULES', 'Documented Modules') %></h2>
|
||||
|
||||
<% if Entities %>
|
||||
<% loop Entities %>
|
||||
<div class="module">
|
||||
<h3><a href="$Link">$Title</a></h3>
|
||||
</div>
|
||||
<% end_loop %>
|
||||
<% else %>
|
||||
<p><% _t('NOMODULEDOCUMENTATION', 'No modules with documentation installed could be found.') %></p>
|
||||
<% end_if %>
|
||||
</div>
|
@ -1,48 +1,45 @@
|
||||
<div id="documentation-page">
|
||||
<div id="content-column">
|
||||
<p>Your search for <strong>"$Query.XML"</strong> found $TotalResults result<% if TotalResults != 1 %>s<% end_if %>.</p>
|
||||
<% if AdvancedSearchEnabled %>
|
||||
<div id="documentation-page" class="box">
|
||||
<p>Your search for <strong>"$Query.XML"</strong> found $TotalResults result<% if TotalResults != 1 %>s<% end_if %>.</p>
|
||||
|
||||
<% if AdvancedSearchEnabled %>
|
||||
<h4><% _t('ADVANCEDSEARCH', 'Advanced Search') %></h4>
|
||||
$AdvancedSearchForm
|
||||
$AdvancedSearchForm
|
||||
<% end_if %>
|
||||
|
||||
<% if Results %>
|
||||
<p>Showing page $ThisPage of $TotalPages</p>
|
||||
|
||||
<% loop Results %>
|
||||
<h2><a href="$Link"><% if BreadcrumbTitle %>$BreadcrumbTitle<% else %>$Title<% end_if %></a></h2>
|
||||
<p>$Content.LimitCharacters(200)</p>
|
||||
<% end_loop %>
|
||||
|
||||
<% if SearchPages %>
|
||||
<ul class="pagination">
|
||||
<% if PrevUrl = false %><% else %>
|
||||
<li class="prev"><a href="$PrevUrl">Prev</a></li>
|
||||
<% end_if %>
|
||||
|
||||
<% loop SearchPages %>
|
||||
<% if IsEllipsis %>
|
||||
<li class="ellipsis">...</li>
|
||||
<% else %>
|
||||
<% if Current %>
|
||||
<li class="active"><strong>$PageNumber</strong></li>
|
||||
<% else %>
|
||||
<li><a href="$Link">$PageNumber</a></li>
|
||||
<% end_if %>
|
||||
<% end_if %>
|
||||
<% end_loop %>
|
||||
|
||||
<% if NextUrl = false %>
|
||||
<% if Results %>
|
||||
<p>Showing page $ThisPage of $TotalPages</p>
|
||||
|
||||
<% loop Results %>
|
||||
<h2><a href="$Link"><% if BreadcrumbTitle %>$BreadcrumbTitle<% else %>$Title<% end_if %></a></h2>
|
||||
<p>$Content.LimitCharacters(200)</p>
|
||||
<% end_loop %>
|
||||
|
||||
<% if SearchPages %>
|
||||
<ul class="pagination">
|
||||
<% if PrevUrl = false %><% else %>
|
||||
<li class="prev"><a href="$PrevUrl">Prev</a></li>
|
||||
<% end_if %>
|
||||
|
||||
<% loop SearchPages %>
|
||||
<% if IsEllipsis %>
|
||||
<li class="ellipsis">...</li>
|
||||
<% else %>
|
||||
<li class="next"><a href="$NextUrl">Next</a></li>
|
||||
<% end_if %>
|
||||
</ul>
|
||||
<% end_if %>
|
||||
|
||||
<% else %>
|
||||
<p>No Results</p>
|
||||
<% if Current %>
|
||||
<li class="active"><strong>$PageNumber</strong></li>
|
||||
<% else %>
|
||||
<li><a href="$Link">$PageNumber</a></li>
|
||||
<% end_if %>
|
||||
<% end_if %>
|
||||
<% end_loop %>
|
||||
|
||||
<% if NextUrl = false %>
|
||||
<% else %>
|
||||
<li class="next"><a href="$NextUrl">Next</a></li>
|
||||
<% end_if %>
|
||||
</ul>
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<% else %>
|
||||
<p>No Results</p>
|
||||
<% end_if %>
|
||||
</div>
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package docsviewer
|
||||
* @subpackage tests
|
||||
*/
|
||||
class DocumentationEntityTest extends SapphireTest {
|
||||
|
||||
function testDocumentationEntityAccessing() {
|
||||
$entity = new DocumentationEntity('docs', '1.0', DOCSVIEWER_PATH .'/tests/docs/', 'My Test');
|
||||
|
||||
$this->assertEquals($entity->getTitle(), 'My Test');
|
||||
$this->assertEquals($entity->getVersions(), array('1.0'));
|
||||
$this->assertEquals($entity->getLanguages(), array('en', 'de'));
|
||||
$this->assertEquals($entity->getFolder(), 'docs');
|
||||
|
||||
$this->assertTrue($entity->hasVersion('1.0'));
|
||||
$this->assertFalse($entity->hasVersion('2.0'));
|
||||
|
||||
$this->assertTrue($entity->hasLanguage('en'));
|
||||
$this->assertFalse($entity->hasLanguage('fr'));
|
||||
}
|
||||
|
||||
function testgetStableVersion() {
|
||||
$entity = new DocumentationEntity('docs', '1.0', DOCSVIEWER_PATH. '/tests/docs/', 'My Test');
|
||||
$entity->addVersion('1.1', DOCSVIEWER_PATH. '/tests/docs-v2.4/');
|
||||
$entity->addVersion('0.0', DOCSVIEWER_PATH. '/tests/docs-v3.0/');
|
||||
$this->assertEquals('1.1', $entity->getStableVersion(), 'Automatic version sorting');
|
||||
|
||||
$entity = new DocumentationEntity('docs', '1.0', DOCSVIEWER_PATH. '/tests/docs/', 'My Test');
|
||||
$entity->addVersion('1.1.', DOCSVIEWER_PATH .'/tests/docs-v2.4/');
|
||||
$entity->setStableVersion('1.0');
|
||||
$this->assertEquals('1.0', $entity->getStableVersion(), 'Manual setting');
|
||||
}
|
||||
}
|
85
tests/DocumentationManifestTests.php
Normal file
85
tests/DocumentationManifestTests.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class DocumentationManifestTests extends SapphireTest {
|
||||
|
||||
private $manifest, $pages;
|
||||
|
||||
public function setUpOnce() {
|
||||
parent::setUpOnce();
|
||||
|
||||
$this->origEnabled = DocumentationService::automatic_registration_enabled();
|
||||
DocumentationService::set_automatic_registration(false);
|
||||
$this->origModules = DocumentationService::get_registered_entities();
|
||||
|
||||
$this->origLinkBase = Config::inst()->get('DocumentationViewer', 'link_base');
|
||||
Config::inst()->update('DocumentationViewer', 'link_base', 'dev/docs/');
|
||||
|
||||
foreach($this->origModules as $module) {
|
||||
DocumentationService::unregister($module->getFolder());
|
||||
}
|
||||
|
||||
// We set 3.0 as current, and test most assertions against 2.4 - to avoid 'current' rewriting issues
|
||||
DocumentationService::register("testdocs", DOCSVIEWER_PATH . "/tests/docs/", '2.3');
|
||||
DocumentationService::register("testdocs", DOCSVIEWER_PATH . "/tests/docs-v2.4/", '2.4', 'Doc Test', true);
|
||||
DocumentationService::register("testdocs", DOCSVIEWER_PATH . "/tests/docs-v3.0/", '3.0', 'Doc Test');
|
||||
|
||||
|
||||
$this->manifest = new DocumentationManifest(true);
|
||||
$this->pages = $this->manifest->getPages();
|
||||
}
|
||||
|
||||
public function tearDownOnce() {
|
||||
parent::tearDownOnce();
|
||||
|
||||
DocumentationService::unregister("testdocs");
|
||||
DocumentationService::set_automatic_registration($this->origEnabled);
|
||||
|
||||
Config::inst()->update('DocumentationViewer', 'link_base', $this->origLinkBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the manifest matches what we'd expect.
|
||||
*/
|
||||
public function testRegenerate() {
|
||||
$match = array(
|
||||
'dev/docs/testdocs/2.3/de/',
|
||||
'dev/docs/testdocs/2.3/de/german/',
|
||||
'dev/docs/testdocs/2.3/de/test/',
|
||||
'dev/docs/testdocs/2.3/en/',
|
||||
'dev/docs/testdocs/2.3/en/sort/',
|
||||
'dev/docs/testdocs/2.3/en/subfolder/',
|
||||
'dev/docs/testdocs/2.3/en/test/',
|
||||
'dev/docs/testdocs/2.3/en/sort/basic/',
|
||||
'dev/docs/testdocs/2.3/en/sort/some-page/',
|
||||
'dev/docs/testdocs/2.3/en/sort/intermediate/',
|
||||
'dev/docs/testdocs/2.3/en/sort/another-page/',
|
||||
'dev/docs/testdocs/2.3/en/sort/advanced/',
|
||||
'dev/docs/testdocs/2.3/en/subfolder/subpage/',
|
||||
'dev/docs/testdocs/2.3/en/subfolder/subsubfolder/',
|
||||
'dev/docs/testdocs/2.3/en/subfolder/subsubfolder/subsubpage/',
|
||||
'dev/docs/testdocs/en/',
|
||||
'dev/docs/testdocs/en/test/',
|
||||
'dev/docs/testdocs/3.0/en/',
|
||||
'dev/docs/testdocs/3.0/en/changelog/',
|
||||
'dev/docs/testdocs/3.0/en/tutorials/',
|
||||
'dev/docs/testdocs/3.0/en/empty/'
|
||||
);
|
||||
|
||||
$this->assertEquals($match, array_keys($this->pages));
|
||||
}
|
||||
|
||||
public function testGetNextPage() {
|
||||
|
||||
}
|
||||
|
||||
public function testGetPreviousPage() {
|
||||
|
||||
}
|
||||
|
||||
public function testGetPage() {
|
||||
|
||||
}
|
||||
}
|
@ -4,10 +4,9 @@
|
||||
* @package docsviewer
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class DocumentationPageTest extends SapphireTest {
|
||||
|
||||
function testGetLink() {
|
||||
public function testGetLink() {
|
||||
$entity = new DocumentationEntity('testmodule', null, DOCSVIEWER_PATH .'/tests/docs/');
|
||||
|
||||
$page = new DocumentationPage();
|
||||
@ -43,8 +42,7 @@ class DocumentationPageTest extends SapphireTest {
|
||||
$this->assertStringEndsWith('versionlinks/en/1/test', $page->Link);
|
||||
}
|
||||
|
||||
|
||||
function testGetRelativePath() {
|
||||
public function testGetRelativePath() {
|
||||
$page = new DocumentationPage();
|
||||
$page->setRelativePath('test.md');
|
||||
$page->setEntity(new DocumentationEntity('mymodule', null, DOCSVIEWER_PATH . '/tests/docs/'));
|
||||
@ -58,7 +56,7 @@ class DocumentationPageTest extends SapphireTest {
|
||||
$this->assertEquals('subfolder/subpage.md', $page->getRelativePath());
|
||||
}
|
||||
|
||||
function testGetPath() {
|
||||
public function testGetPath() {
|
||||
$absPath = DOCSVIEWER_PATH .'/tests/docs/';
|
||||
$page = new DocumentationPage();
|
||||
$page->setRelativePath('test.md');
|
||||
@ -73,7 +71,7 @@ class DocumentationPageTest extends SapphireTest {
|
||||
$this->assertEquals($absPath . 'en/subfolder/subpage.md', $page->getPath());
|
||||
}
|
||||
|
||||
function testGetBreadcrumbTitle() {
|
||||
public function testGetBreadcrumbTitle() {
|
||||
$entity = new DocumentationEntity('testmodule', null, DOCSVIEWER_PATH . '/tests/docs/');
|
||||
|
||||
$page = new DocumentationPage();
|
||||
|
@ -12,14 +12,16 @@ class DocumentationViewerTest extends FunctionalTest {
|
||||
|
||||
protected $autoFollowRedirection = false;
|
||||
|
||||
function setUpOnce() {
|
||||
public function setUpOnce() {
|
||||
parent::setUpOnce();
|
||||
|
||||
$this->origEnabled = DocumentationService::automatic_registration_enabled();
|
||||
DocumentationService::set_automatic_registration(false);
|
||||
$this->origModules = DocumentationService::get_registered_entities();
|
||||
$this->origLinkBase = DocumentationViewer::get_link_base();
|
||||
DocumentationViewer::set_link_base('dev/docs/');
|
||||
|
||||
$this->origLinkBase = Config::inst()->get('DocumentationViewer', 'link_base');
|
||||
Config::inst()->update('DocumentationViewer', 'link_base', 'dev/docs/');
|
||||
|
||||
foreach($this->origModules as $module) {
|
||||
DocumentationService::unregister($module->getFolder());
|
||||
}
|
||||
@ -33,12 +35,13 @@ class DocumentationViewerTest extends FunctionalTest {
|
||||
DocumentationService::register("DocumentationViewerAltModule2", DOCSVIEWER_PATH . "/tests/docs-search/", '1.0');
|
||||
}
|
||||
|
||||
function tearDownOnce() {
|
||||
public function tearDownOnce() {
|
||||
parent::tearDownOnce();
|
||||
|
||||
DocumentationService::unregister("DocumentationViewerTests");
|
||||
DocumentationService::set_automatic_registration($this->origEnabled);
|
||||
DocumentationViewer::set_link_base($this->origLinkBase);
|
||||
|
||||
Config::inst()->update('DocumentationViewer', 'link_base', $this->origLinkBase);
|
||||
}
|
||||
|
||||
/**
|
||||
|
1
tests/docs-withoutlang/fail.md
Normal file
1
tests/docs-withoutlang/fail.md
Normal file
@ -0,0 +1 @@
|
||||
# Failure
|
2
tests/docs/de/german.md
Normal file
2
tests/docs/de/german.md
Normal file
@ -0,0 +1,2 @@
|
||||
# German
|
||||
|
Loading…
x
Reference in New Issue
Block a user