ENHANCEMENT optional branch property to allow version aliases of development branches inside docsviewer.

This commit is contained in:
Cam Findlay 2014-12-18 09:41:13 +13:00
parent d92fb96e0e
commit 664e3eea91
4 changed files with 151 additions and 103 deletions

View File

@ -3,7 +3,7 @@
/**
* 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
* 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>
@ -92,7 +92,7 @@ class DocumentationManifest {
/**
* Sets up the top level entities.
*
* Either manually registered through the YAML syntax or automatically
* Either manually registered through the YAML syntax or automatically
* loaded through investigating the file system for `docs` folder.
*/
public function setupEntities() {
@ -128,11 +128,13 @@ class DocumentationManifest {
$version = (isset($details['Version'])) ? $details['Version'] : '';
$branch = (isset($details['Branch'])) ? $details['Branch'] : '';
$langs = scandir($path);
if($langs) {
$possible = i18n::get_common_languages(true);
foreach($langs as $k => $lang) {
if(isset($possible[$lang])) {
$entity = Injector::inst()->create(
@ -143,6 +145,7 @@ class DocumentationManifest {
$entity->setTitle($details['Title']);
$entity->setLanguage($lang);
$entity->setVersion($version);
$entity->setBranch($branch);
if(isset($details['Stable'])) {
$entity->setIsStable($details['Stable']);
@ -192,7 +195,7 @@ class DocumentationManifest {
}
$dir = Controller::join_links(BASE_PATH, $entity);
if(is_dir($dir)) {
// check to see if it has docs
$docs = Controller::join_links($dir, 'docs');
@ -202,6 +205,7 @@ class DocumentationManifest {
'Path' => $docs,
'Title' => DocumentationHelper::clean_page_name($entity),
'Version' => 'master',
'Branch' => 'master',
'Stable' => true
);
}
@ -242,7 +246,7 @@ class DocumentationManifest {
}
/**
* Returns a particular page for the requested URL.
* Returns a particular page for the requested URL.
*
* @return DocumentationPage
*/
@ -260,7 +264,7 @@ class DocumentationManifest {
foreach($this->getEntities() as $entity) {
if(strpos($record['filepath'], $entity->getPath()) !== false) {
$page = Injector::inst()->create(
$record['type'],
$record['type'],
$entity,
$record['basename'],
$record['filepath']
@ -285,11 +289,11 @@ class DocumentationManifest {
foreach($this->getEntities() as $entity) {
$this->entity = $entity;
$this->handleFolder('', $this->entity->getPath(), 0);
$finder->find($this->entity->getPath());
}
// groupds
$grouped = array();
@ -316,7 +320,7 @@ class DocumentationManifest {
if ($a['filepath'] == $b['filepath']) {
return 0;
}
return ($a['filepath'] < $b['filepath']) ? -1 : 1;
});
@ -339,8 +343,8 @@ class DocumentationManifest {
);
$link = ltrim(str_replace(
Config::inst()->get('DocumentationViewer', 'link_base'),
'',
Config::inst()->get('DocumentationViewer', 'link_base'),
'',
$folder->Link()
), '/');
@ -355,9 +359,9 @@ class DocumentationManifest {
}
/**
* 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.
* 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.
@ -368,7 +372,7 @@ class DocumentationManifest {
*/
public function handleFile($basename, $path, $depth) {
$page = Injector::inst()->create(
'DocumentationPage',
'DocumentationPage',
$this->entity, $basename, $path
);
@ -376,11 +380,11 @@ class DocumentationManifest {
$page->getMarkdown();
$link = ltrim(str_replace(
Config::inst()->get('DocumentationViewer', 'link_base'),
'',
Config::inst()->get('DocumentationViewer', 'link_base'),
'',
$page->Link()
), '/');
$this->pages[$link] = array(
'title' => $page->getTitle(),
'filepath' => $path,
@ -403,7 +407,7 @@ class DocumentationManifest {
$output = new ArrayList();
$parts = explode('/', trim($record->getRelativeLink(), '/'));
// Add the base link.
$output->push(new ArrayData(array(
'Link' => $base->Link(),
@ -429,7 +433,7 @@ class DocumentationManifest {
/**
* Determine the next page from the given page.
*
* Relies on the fact when the manifest was built, it was generated in
* Relies on the fact when the manifest was built, it was generated in
* order.
*
* @param string $filepath
@ -470,7 +474,7 @@ class DocumentationManifest {
/**
* Determine the previous page from the given page.
*
* Relies on the fact when the manifest was built, it was generated in
* Relies on the fact when the manifest was built, it was generated in
* order.
*
* @param string $filepath
@ -508,7 +512,7 @@ class DocumentationManifest {
public function normalizeUrl($url) {
$url = trim($url, '/') .'/';
// if the page is the index page then hide it from the menu
// if the page is the index page then hide it from the menu
if(strpos(strtolower($url), '/index.md/')) {
$url = substr($url, 0, strpos($url, "index.md/"));
}
@ -529,12 +533,12 @@ class DocumentationManifest {
if(!$recordPath) {
$recordPath = $entityPath;
}
$output = new ArrayList();
$base = Config::inst()->get('DocumentationViewer', 'link_base');
$entityPath = $this->normalizeUrl($entityPath);
$recordPath = $this->normalizeUrl($recordPath);
$recordParts = explode(DIRECTORY_SEPARATOR, trim($recordPath,'/'));
$recordParts = explode(DIRECTORY_SEPARATOR, trim($recordPath,'/'));
$currentRecordPath = end($recordParts);
$depth = substr_count($entityPath, '/');
@ -548,18 +552,18 @@ class DocumentationManifest {
// only pull it up if it's one more level depth
if(substr_count($pagePath, DIRECTORY_SEPARATOR) == ($depth + 1)) {
$pagePathParts = explode(DIRECTORY_SEPARATOR, trim($pagePath,'/'));
$pagePathParts = explode(DIRECTORY_SEPARATOR, trim($pagePath,'/'));
$currentPagePath = end($pagePathParts);
if($currentPagePath == $currentRecordPath) {
if($currentPagePath == $currentRecordPath) {
$mode = 'current';
}
else if(strpos($recordPath, $pagePath) !== false) {
else if(strpos($recordPath, $pagePath) !== false) {
$mode = 'section';
}
else {
$mode = 'link';
}
$children = new ArrayList();
if($mode == 'section' || $mode == 'current') {
@ -583,7 +587,7 @@ class DocumentationManifest {
* @param DocumentationEntity
*
* @return ArrayList
*/
*/
public function getAllVersionsOfEntity(DocumentationEntity $entity) {
$all = new ArrayList();
@ -602,7 +606,7 @@ class DocumentationManifest {
* @param DocumentationEntity
*
* @return DocumentationEntity
*/
*/
public function getStableVersion(DocumentationEntity $entity) {
foreach($this->getEntities() as $check) {
if($check->getKey() == $entity->getKey()) {
@ -621,19 +625,19 @@ class DocumentationManifest {
* @param DocumentationEntity
*
* @return ArrayList
*/
*/
public function getVersions($entity) {
if(!$entity) {
return null;
}
$output = new ArrayList();
foreach($this->getEntities() as $check) {
if($check->getKey() == $entity->getKey()) {
if($check->getLanguage() == $entity->getLanguage()) {
$same = ($check->getVersion() == $entity->getVersion());
$output->push(new ArrayData(array(
'Title' => $check->getVersion(),
'Link' => $check->Link(),
@ -652,7 +656,7 @@ class DocumentationManifest {
*/
public function getAllVersions() {
$versions = array();
foreach($this->getEntities() as $entity) {
if($entity->getVersion()) {
$versions[$entity->getVersion()] = $entity->getVersion();
@ -660,9 +664,9 @@ class DocumentationManifest {
$versions['0.0'] = _t('DocumentationManifest.MASTER', 'Master');
}
}
asort($versions);
return $versions;
}
}

View File

@ -3,7 +3,7 @@
/**
* Documentation Viewer.
*
* Reads the bundled markdown files from documentation folders and displays the
* Reads the bundled markdown files from documentation folders and displays the
* output (either via markdown or plain text).
*
* For more documentation on how to use this class see the documentation in the
@ -31,7 +31,7 @@ class DocumentationViewer extends Controller {
* @var string
*/
private static $documentation_title = 'SilverStripe Documentation';
/**
* @var array
*/
@ -65,7 +65,7 @@ class DocumentationViewer extends Controller {
* @var string same as the routing pattern set through Director::addRules().
*/
private static $link_base = 'dev/docs/';
/**
* @config
*
@ -91,9 +91,9 @@ class DocumentationViewer extends Controller {
Requirements::javascript('//use.typekit.net/emt4dhq.js');
Requirements::customScript('try{Typekit.load();}catch(e){}');
Requirements::javascript(THIRDPARTY_DIR .'/jquery/jquery.js');
Requirements::javascript(THIRDPARTY_DIR .'/jquery/jquery.js');
Requirements::javascript('https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js');
Requirements::javascript(DOCSVIEWER_DIR .'/javascript/DocumentationViewer.js');
Requirements::combine_files('docs.css', array(
DOCSVIEWER_DIR .'/css/normalize.css',
@ -104,16 +104,16 @@ class DocumentationViewer extends Controller {
DOCSVIEWER_DIR .'/css/small.css'
));
}
/**
* Can the user view this documentation. Hides all functionality for private
* Can the user view this documentation. Hides all functionality for private
* wikis.
*
* @return bool
*/
public function canView() {
return (Director::isDev() || Director::is_cli() ||
!$this->config()->get('check_permission') ||
return (Director::isDev() || Director::is_cli() ||
!$this->config()->get('check_permission') ||
Permission::check($this->config()->get('check_permission'))
);
}
@ -127,8 +127,8 @@ class DocumentationViewer extends Controller {
}
/**
* Overloaded to avoid "action doesn't exist" errors - all URL parts in
* this controller are virtual and handled through handleRequest(), not
* Overloaded to avoid "action doesn't exist" errors - all URL parts in
* this controller are virtual and handled through handleRequest(), not
* controller methods.
*
* @param $request
@ -145,20 +145,20 @@ class DocumentationViewer extends Controller {
$url = $request->getURL();
//
// If the current request has an extension attached to it, strip that
// If the current request has an extension attached to it, strip that
// off and redirect the user to the page without an extension.
//
if(DocumentationHelper::get_extension($url)) {
$this->response = new SS_HTTPResponse();
$this->response->redirect(
DocumentationHelper::trim_extension_off($url) .'/',
DocumentationHelper::trim_extension_off($url) .'/',
301
);
$request->shift();
$request->shift();
return $this->response;
return $this->response;
}
//
@ -170,22 +170,22 @@ class DocumentationViewer extends Controller {
if($base && strpos($url, $base) !== false) {
$url = substr(
ltrim($url, '/'),
ltrim($url, '/'),
strlen($base)
);
} else {
}
//
// Handle any permanent redirections that the developer has defined.
//
//
if($link = DocumentationPermalinks::map($url)) {
// the first param is a shortcode for a page so redirect the user to
// the short code.
$this->response = new SS_HTTPResponse();
$this->response->redirect($link, 301);
$request->shift();
$request->shift();
@ -194,7 +194,7 @@ class DocumentationViewer extends Controller {
//
// Validate the language provided. Language is a required URL parameter.
// as we use it for generic interfaces and language selection. If
// as we use it for generic interfaces and language selection. If
// language is not set, redirects to 'en'
//
$languages = i18n::get_common_languages();
@ -245,7 +245,7 @@ class DocumentationViewer extends Controller {
"DocumentationViewer_{$type}",
"DocumentationViewer"
));
return new SS_HTTPResponse($body, 200);
} else if(!$url || $url == $lang) {
$body = $this->renderWith(array(
@ -268,7 +268,7 @@ class DocumentationViewer extends Controller {
*/
public function httpError($status, $message = null) {
$this->init();
$class = get_class($this);
$body = $this->customise(new ArrayData(array(
'Message' => $message
@ -283,7 +283,7 @@ class DocumentationViewer extends Controller {
public function getManifest() {
if(!$this->manifest) {
$flush = SapphireTest::is_running_test() || (isset($_GET['flush']));
$this->manifest = new DocumentationManifest($flush);
}
@ -304,11 +304,11 @@ class DocumentationViewer extends Controller {
/**
* Generate a list of {@link Documentation } which have been registered and which can
* be documented.
* Generate a list of {@link Documentation } which have been registered and which can
* be documented.
*
* @return DataObject
*/
*/
public function getMenu() {
$entities = $this->getManifest()->getEntities();
$output = new ArrayList();
@ -319,25 +319,25 @@ class DocumentationViewer extends Controller {
$checkLang = $entity->getLanguage();
$checkVers = $entity->getVersion();
// only show entities with the same language or any entity that
// only show entities with the same language or any entity that
// isn't registered under any particular language (auto detected)
if($checkLang && $checkLang !== $this->getLanguage()) {
continue;
}
if($current && $checkVers) {
if($entity->getVersion() !== $current->getVersion()) {
continue;
}
}
$mode = 'link';
$mode = 'link';
$children = new ArrayList();
if($entity->hasRecord($record) || $entity->getIsDefaultEntity()) {
$mode = 'current';
// add children
// add children
$children = $this->getManifest()->getChildrenFor(
$entity->getPath(), ($record) ? $record->getPath() : $entity->getPath()
);
@ -360,7 +360,7 @@ class DocumentationViewer extends Controller {
return $output;
}
/**
* Return the content for the page. If its an actual documentation page then
* display the content from the page, otherwise display the contents from
@ -430,7 +430,7 @@ class DocumentationViewer extends Controller {
return new ArrayList();
}
/**
* Generate a list of breadcrumbs for the user.
*
@ -474,7 +474,7 @@ class DocumentationViewer extends Controller {
public function getTitle() {
return ($this->record) ? $this->record->getTitle() : null;
}
/**
* @return string
*/
@ -502,7 +502,7 @@ class DocumentationViewer extends Controller {
}
/**
* Generate a list of all the pages in the documentation grouped by the
* Generate a list of all the pages in the documentation grouped by the
* first letter of the page.
*
* @return GroupedList
@ -525,7 +525,7 @@ class DocumentationViewer extends Controller {
return GroupedList::create($output->sort('Title', 'ASC'));
}
/**
* Documentation Search Form. Allows filtering of the results by many entities
* and multiple versions.
@ -536,13 +536,13 @@ class DocumentationViewer extends Controller {
if(!Config::inst()->get('DocumentationSearch','enabled')) {
return false;
}
return new DocumentationSearchForm($this);
}
/**
* Sets the mapping between a entity name and the link for the end user
* to jump into editing the documentation.
* to jump into editing the documentation.
*
* Some variables are replaced:
* - %version%
@ -554,7 +554,7 @@ class DocumentationViewer extends Controller {
*
* <code>
* DocumentationViewer::set_edit_link(
* 'framework',
* 'framework',
* 'https://github.com/silverstripe/%entity%/edit/%version%/docs/%lang%/%path%',
* $opts
* ));
@ -592,6 +592,11 @@ class DocumentationViewer extends Controller {
$url = self::$edit_links[strtolower($entity->title)];
$version = $entity->getVersion();
if($entity->getBranch()){
$version = $entity->getBranch();
}
if($version == "trunk" && (isset($url['options']['rewritetrunktomaster']))) {
if($url['options']['rewritetrunktomaster']) {
$version = "master";
@ -601,9 +606,9 @@ class DocumentationViewer extends Controller {
return str_replace(
array('%entity%', '%lang%', '%version%', '%path%'),
array(
$entity->title,
$this->getLanguage(),
$version,
$entity->title,
$this->getLanguage(),
$version,
ltrim($page->getRelativePath(), '/')
),
@ -624,25 +629,25 @@ class DocumentationViewer extends Controller {
* @return DocumentationPage
*/
public function getNextPage() {
return ($this->record)
return ($this->record)
? $this->getManifest()->getNextPage(
$this->record->getPath(), $this->getEntity()->getPath())
$this->record->getPath(), $this->getEntity()->getPath())
: null;
}
}
/**
* Returns the previous page. Either returns the previous sibling or the
* Returns the previous page. Either returns the previous sibling or the
* parent of this page
*
* @return DocumentationPage
*/
public function getPreviousPage() {
return ($this->record)
return ($this->record)
? $this->getManifest()->getPreviousPage(
$this->record->getPath(), $this->getEntity()->getPath())
$this->record->getPath(), $this->getEntity()->getPath())
: null;
}
/**
* @return string
*/

View File

@ -2,25 +2,25 @@
/**
* A {@link DocumentationEntity} represents a module or folder with stored
* documentation files. An entity not an individual page but a `section` of
* documentation files. An entity not an individual page but a `section` of
* documentation arranged by version and language.
*
* Each entity has a version assigned to it (i.e master) and folders can be
* Each entity has a version assigned to it (i.e master) and folders can be
* labeled with a specific version. For instance, doc.silverstripe.org has three
* DocumentEntities for Framework - versions 2.4, 3.0 and 3.1. In addition an
* entity can have a language attached to it. So for an instance with en, de and
* fr documentation you may have three {@link DocumentationEntities} registered.
*
*
*
*
* @package docsviewer
* @subpackage models
*/
class DocumentationEntity extends ViewableData {
/**
* The key to match entities with that is not localized. For instance, you
* may have three entities (en, de, fr) that you want to display a nice
* may have three entities (en, de, fr) that you want to display a nice
* title for, but matching needs to occur on a specific key.
*
* @var string $key
@ -28,7 +28,7 @@ class DocumentationEntity extends ViewableData {
protected $key;
/**
* The human readable title of this entity. Set when the module is
* The human readable title of this entity. Set when the module is
* registered.
*
* @var string $title
@ -36,7 +36,7 @@ class DocumentationEntity extends ViewableData {
protected $title;
/**
* If the system is setup to only document one entity then you may only
* If the system is setup to only document one entity then you may only
* want to show a single entity in the URL and the sidebar. Set this when
* you register the entity with the key `DefaultEntity` and the URL will
* not include any version or language information.
@ -57,7 +57,14 @@ class DocumentationEntity extends ViewableData {
protected $version;
/**
* If this entity is a stable release or not. If it is not stable (i.e it
* The repository branch name (allows for $version to be an alias on development branches).
*
* @var string $branch
*/
protected $branch;
/**
* If this entity is a stable release or not. If it is not stable (i.e it
* could be a past or future release) then a warning message will be shown.
*
* @var boolean $stable
@ -70,12 +77,12 @@ class DocumentationEntity extends ViewableData {
protected $language;
/**
*
*
*/
public function __construct($key) {
$this->key = DocumentationHelper::clean_page_url($key);
}
/**
* Get the title of this module.
@ -103,7 +110,7 @@ class DocumentationEntity extends ViewableData {
/**
* Returns the web accessible link to this entity.
*
* Includes the version information
* Includes the version information
*
* @return string
*/
@ -116,7 +123,7 @@ class DocumentationEntity extends ViewableData {
);
} else {
$base = Controller::join_links(
Config::inst()->get('DocumentationViewer', 'link_base'),
Config::inst()->get('DocumentationViewer', 'link_base'),
$this->getLanguage(),
$this->getKey(),
'/'
@ -130,12 +137,12 @@ class DocumentationEntity extends ViewableData {
}
return Controller::join_links(
$base,
$base,
$this->getVersion(),
'/'
);
}
/**
* @return string
*/
@ -213,6 +220,22 @@ class DocumentationEntity extends ViewableData {
return $this->version;
}
/**
* @param string
*/
public function setBranch($branch) {
$this->branch = $branch;
return $this;
}
/**
* @return float
*/
public function getBranch() {
return $this->branch;
}
/**
* @return string
*/
@ -250,8 +273,8 @@ class DocumentationEntity extends ViewableData {
/**
* Returns an integer value based on if a given version is the latest
* version. Will return -1 for if the version is older, 0 if versions are
* Returns an integer value based on if a given version is the latest
* version. Will return -1 for if the version is older, 0 if versions are
* the same and 1 if the version is greater than.
*
* @param string $version
@ -269,8 +292,9 @@ class DocumentationEntity extends ViewableData {
'Key' => $this->key,
'Path' => $this->getPath(),
'Version' => $this->getVersion(),
'Branch' => $this->getBranch(),
'IsStable' => $this->getIsStable(),
'Language' => $this->getLanguage()
);
}
}
}

View File

@ -25,6 +25,21 @@ In YAML this looks like:
Path: "framework/docs/"
Title: "Framework Documentation"
###Branch aliases for the edit link (optional)
When using entities with multiple versions, one of the branches of documentation may be a development version. For example the 'master' branch. You may have an internally assigned version number for this registered in your .yml configuration.
If this version number is not the same as the branch name on the git repository the `getEditLinks` method will return an incorrect link to go and edit the documentation. In this case you can simply set an optional `branch` property on the entity which will be used in the edit link instead.
Example:
:::yml
DocumentationManifest:
register_entities:
-
Path: "framework/docs/"
Title: "Framework Documentation"
Version: "1.0"
Branch: "master"
## Permalinks
@ -92,4 +107,4 @@ 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.
in a directory called _images at the same level as your documentation.