2010-03-04 05:39:02 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
2010-04-11 09:25:26 +02:00
|
|
|
* Documentation Viewer.
|
2010-03-04 05:39:02 +01:00
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* Reads the bundled markdown files from docs/ folders and displays output in a formatted page at /dev/docs/.
|
|
|
|
* For more documentation on how to use this class see the documentation online in /dev/docs/ or in the
|
|
|
|
* /sapphiredocs/docs folder
|
2010-03-04 05:39:02 +01:00
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* @author Will Rossiter <will@silverstripe.com>
|
2010-03-04 05:39:02 +01:00
|
|
|
* @package sapphiredocs
|
|
|
|
*/
|
|
|
|
|
|
|
|
class DocumentationViewer extends Controller {
|
|
|
|
|
|
|
|
static $url_handlers = array(
|
|
|
|
'' => 'index',
|
2010-04-11 09:25:26 +02:00
|
|
|
'$Module/$Page/$OtherPage' => 'parse'
|
2010-03-04 05:39:02 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of files to ignore from the listing
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2010-04-11 09:25:26 +02:00
|
|
|
static $ignored_files = array('.', '..', '.DS_Store', '.svn', '.git', 'assets', 'themes');
|
2010-03-04 05:39:02 +01:00
|
|
|
|
|
|
|
/**
|
2010-04-11 09:25:26 +02:00
|
|
|
* An array of case insenstive values to use as readmes
|
2010-03-04 05:39:02 +01:00
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
static $readme_files = array('readme', 'readme.md', 'readme.txt', 'readme.markdown');
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Main documentation page
|
2010-03-04 05:39:02 +01:00
|
|
|
*/
|
|
|
|
function index() {
|
2010-04-11 09:25:26 +02:00
|
|
|
return $this->customise(array(
|
|
|
|
'DocumentedModules' => $this->DocumentedModules()
|
|
|
|
))->renderWith(array('DocumentationViewer_index', 'DocumentationViewer'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Individual documentation page
|
|
|
|
*
|
|
|
|
* @param HTTPRequest
|
|
|
|
*/
|
|
|
|
function parse($request) {
|
|
|
|
require_once('../sapphiredocs/thirdparty/markdown.php');
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
$page = $request->param('Page');
|
|
|
|
$module = $request->param('Module');
|
2010-03-04 11:18:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
$path = BASE_PATH .'/'. $module .'/docs';
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
if($content = $this->findPage($path, $page)) {
|
|
|
|
$title = $page;
|
|
|
|
$content = Markdown(file_get_contents($content));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$title = 'Page not Found';
|
|
|
|
$content = false;
|
|
|
|
}
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
return $this->customise(array(
|
|
|
|
'Title' => $title,
|
|
|
|
'Content' => $content
|
|
|
|
))->renderWith('DocumentationViewer');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an array of the modules installed. Currently to determine if a module is
|
|
|
|
* installed look at all the folders and check is a _config file.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
function getModules() {
|
2010-03-04 05:39:02 +01:00
|
|
|
$modules = scandir(BASE_PATH);
|
|
|
|
|
|
|
|
if($modules) {
|
2010-04-11 09:25:26 +02:00
|
|
|
foreach($modules as $key => $module) {
|
|
|
|
if(!is_dir(BASE_PATH .'/'. $module) || in_array($module, self::$ignored_files, true) || !file_exists(BASE_PATH . '/'. $module .'/_config.php')) {
|
|
|
|
unset($modules[$key]);
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-04-11 09:25:26 +02:00
|
|
|
|
|
|
|
return $modules;
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
2010-04-11 09:25:26 +02:00
|
|
|
|
2010-03-04 05:59:00 +01:00
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
/**
|
2010-04-11 09:25:26 +02:00
|
|
|
* Generate a set of modules for the home page
|
2010-03-04 05:39:02 +01:00
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* @return DataObjectSet
|
2010-03-04 05:39:02 +01:00
|
|
|
*/
|
2010-04-11 09:25:26 +02:00
|
|
|
function DocumentedModules() {
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
$modules = new DataObjectSet();
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
// include sapphire first
|
|
|
|
$modules->push(new ArrayData(array(
|
|
|
|
'Title' => 'sapphire',
|
|
|
|
'Content' => $this->generateNestedTree('sapphire'),
|
|
|
|
'Readme' => $this->readmeExists('sapphire')
|
|
|
|
)));
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
$extra_ignore = array('sapphire');
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
foreach($this->getModules() as $module) {
|
|
|
|
if(!in_array($module, $extra_ignore) && $this->moduleHasDocs($module)) {
|
|
|
|
$modules->push(new ArrayData(array(
|
|
|
|
'Title' => $module,
|
|
|
|
'Content' => $this->generateNestedTree($module),
|
|
|
|
'Readme' => $this->readmeExists($module)
|
|
|
|
)));
|
|
|
|
}
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
return $modules;
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
|
|
|
|
2010-03-04 11:18:02 +01:00
|
|
|
/**
|
2010-04-11 09:25:26 +02:00
|
|
|
* Generate a list of modules (folder which has a _config) which have no /docs/ folder
|
|
|
|
*
|
|
|
|
* @return DataObjectSet
|
2010-03-04 11:18:02 +01:00
|
|
|
*/
|
2010-04-11 09:25:26 +02:00
|
|
|
function UndocumentedModules() {
|
|
|
|
$modules = $this->getModules();
|
|
|
|
$undocumented = array();
|
2010-03-04 11:18:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
if($modules) {
|
|
|
|
foreach($modules as $module) {
|
|
|
|
if(!$this->moduleHasDocs($module)) $undocumented[] = $module;
|
|
|
|
}
|
2010-03-04 11:18:02 +01:00
|
|
|
}
|
2010-04-11 09:25:26 +02:00
|
|
|
|
2010-06-03 05:02:44 +02:00
|
|
|
return implode(', ', $undocumented);
|
2010-03-04 11:18:02 +01:00
|
|
|
}
|
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
/**
|
|
|
|
* Helper function to determine whether a module has documentation
|
|
|
|
*
|
|
|
|
* @param String - Module folder name
|
|
|
|
* @return bool - Has docs folder
|
|
|
|
*/
|
|
|
|
function moduleHasDocs($module) {
|
|
|
|
return is_dir(BASE_PATH .'/'. $module .'/docs/');
|
2010-03-04 11:18:02 +01:00
|
|
|
}
|
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
|
2010-03-04 11:18:02 +01:00
|
|
|
/**
|
|
|
|
* Work out if a module contains a readme
|
|
|
|
*
|
|
|
|
* @param String - Module to check
|
|
|
|
* @return bool|String - of path
|
|
|
|
*/
|
|
|
|
private function readmeExists($module) {
|
|
|
|
$children = scandir(BASE_PATH.'/'.$module);
|
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
$readmeOptions = self::$readme_files;
|
2010-03-04 11:18:02 +01:00
|
|
|
|
|
|
|
if($children) {
|
|
|
|
foreach($children as $i => $file) {
|
|
|
|
if(in_array(strtolower($file), $readmeOptions)) return $file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
/**
|
|
|
|
* Find a documentation page within a given module.
|
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* @param String - Path to Module
|
2010-03-04 05:39:02 +01:00
|
|
|
* @param String - Name of doc page
|
|
|
|
*
|
|
|
|
* @return String|false - File path
|
|
|
|
*/
|
|
|
|
private function findPage($path, $name) {
|
|
|
|
|
|
|
|
// open docs folder
|
|
|
|
$handle = opendir($path);
|
|
|
|
|
|
|
|
if($handle) {
|
|
|
|
while (false !== ($file = readdir($handle))) {
|
2010-03-26 03:50:30 +01:00
|
|
|
$newpath = $path .'/'. $file;
|
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
if(!in_array($file, self::$ignored_files)) {
|
2010-03-26 03:50:30 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
if(is_dir($newpath)) return $this->findPage($newpath, $name);
|
2010-03-26 03:50:30 +01:00
|
|
|
|
|
|
|
elseif(strtolower($this->formatStringForTitle($file)) == strtolower($name)) {
|
|
|
|
return $newpath;
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-26 03:50:30 +01:00
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a nested tree for a given folder via recursion
|
|
|
|
*
|
|
|
|
* @param String - module to generate
|
|
|
|
*/
|
|
|
|
private function generateNestedTree($module) {
|
2010-04-11 06:02:25 +02:00
|
|
|
$path = BASE_PATH . '/'. $module .'/docs/';
|
2010-04-11 09:25:26 +02:00
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
return (is_dir($path)) ? $this->recursivelyGenerateTree($path, $module) : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursive method to generate the tree
|
|
|
|
*
|
|
|
|
* @param String - folder to work through
|
|
|
|
* @param String - module we're working through
|
|
|
|
*/
|
2010-04-11 09:25:26 +02:00
|
|
|
private function recursivelyGenerateTree($path, $module, $output = '') {
|
|
|
|
$output .= "<ul class='tree'>";
|
2010-03-04 05:39:02 +01:00
|
|
|
$handle = opendir($path);
|
|
|
|
|
|
|
|
if($handle) {
|
|
|
|
while (false !== ($file = readdir($handle))) {
|
|
|
|
if(!in_array($file, self::$ignored_files)) {
|
2010-03-04 11:18:02 +01:00
|
|
|
$newPath = $path.'/'.$file;
|
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
// if the file is a dir nest the pages
|
|
|
|
if(is_dir($newPath)) {
|
|
|
|
|
|
|
|
// if this has a number
|
2010-04-11 09:25:26 +02:00
|
|
|
$output .= "<li class='folder'>". $this->formatStringForTitle($file) ."</li>";
|
|
|
|
|
|
|
|
$output = $this->recursivelyGenerateTree($newPath, $module, $output);
|
2010-03-04 05:39:02 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
2010-03-26 03:50:30 +01:00
|
|
|
$offset = (strpos($file,'-') > 0) ? strpos($file,'-') + 1 : 0;
|
|
|
|
|
|
|
|
$file = substr(str_ireplace('.md', '', $file), $offset);
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
$output .= "<li class='page'><a href='". Director::absoluteBaseURL() . 'dev/docs/' . $module .'/'. $file . "'>". $this->formatStringForTitle($file) ."</a></li>";
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-04-11 09:25:26 +02:00
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
closedir($handle);
|
2010-04-11 09:25:26 +02:00
|
|
|
$output .= "</ul>";
|
2010-03-04 05:39:02 +01:00
|
|
|
|
2010-04-11 09:25:26 +02:00
|
|
|
return $output;
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-04-11 09:25:26 +02:00
|
|
|
* Take a file name and generate a 'nice' title for it.
|
2010-03-04 05:39:02 +01:00
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* example. 01-Getting-Started -> Getting Started
|
2010-03-04 05:39:02 +01:00
|
|
|
*
|
2010-04-11 09:25:26 +02:00
|
|
|
* @param String - raw title
|
|
|
|
* @return String - nicely formatted one
|
2010-03-04 05:39:02 +01:00
|
|
|
*/
|
|
|
|
private function formatStringForTitle($title) {
|
|
|
|
// remove numbers if used.
|
|
|
|
if(substr($title, 2, 1) == '-') $title = substr($title, 3);
|
|
|
|
|
|
|
|
// change - to spaces
|
|
|
|
$title = str_ireplace('-', ' ', $title);
|
|
|
|
|
2010-03-04 11:18:02 +01:00
|
|
|
// remove extension
|
2010-04-11 09:25:26 +02:00
|
|
|
$title = str_ireplace(array('.md', '.markdown'), '', $title);
|
2010-03-04 11:18:02 +01:00
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
return $title;
|
|
|
|
}
|
2010-04-11 09:25:26 +02:00
|
|
|
|
2010-03-04 05:39:02 +01:00
|
|
|
}
|