ENHANCEMENT: use urls in the format module/lang/version rather than version first. Tweaks to the parser to prevent infinite loops

This commit is contained in:
Will Rossiter 2011-01-14 01:03:46 +00:00
parent 24f1b13233
commit fc8ab96c51
10 changed files with 87 additions and 66 deletions

View File

@ -16,6 +16,8 @@ Reads markdown documentation files from the /docs/ folder in . To read documenta
For more documentation on how to use the module please read /docs/Writing-Documentation.md For more documentation on how to use the module please read /docs/Writing-Documentation.md
(or via this in /dev/docs/sapphiredocs/Writing-Documentation in your webbrowser) (or via this in /dev/docs/sapphiredocs/Writing-Documentation in your webbrowser)
**Note** This module assumes you are using numeric values for your versions.
## Syntax Highlighting ## ## Syntax Highlighting ##
The custom Markdown parser can render custom prefixes for code blocks, The custom Markdown parser can render custom prefixes for code blocks,
@ -58,4 +60,4 @@ To include the syntax highlighter source, add the following to your `Documentati
Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shThemeRDark.css'); Requirements::css('sapphiredocs/thirdparty/syntaxhighlighter/styles/shThemeRDark.css');
You can overload the `DocumentationViewer` class and add a custom route through `Director::addRule()` You can overload the `DocumentationViewer` class and add a custom route through `Director::addRule()`
if you prefer not to modify the module file.>>>>>>> .r115400 if you prefer not to modify the module file.

View File

@ -238,9 +238,9 @@ class DocumentationEntity extends ViewableData {
return Controller::join_links( return Controller::join_links(
Director::absoluteBaseURL(), Director::absoluteBaseURL(),
DocumentationViewer::get_link_base(), DocumentationViewer::get_link_base(),
$version, $this->moduleFolder,
$lang, $lang,
$this->moduleFolder $version
); );
} }

View File

@ -38,6 +38,7 @@ class DocumentationParser {
// Pre-processing // Pre-processing
$md = self::rewrite_image_links($md, $page); $md = self::rewrite_image_links($md, $page);
$md = self::rewrite_relative_links($md, $page, $baselink); $md = self::rewrite_relative_links($md, $page, $baselink);
$md = self::rewrite_api_links($md, $page); $md = self::rewrite_api_links($md, $page);
$md = self::rewrite_heading_anchors($md, $page); $md = self::rewrite_heading_anchors($md, $page);
$md = self::rewrite_code_blocks($md, $page); $md = self::rewrite_code_blocks($md, $page);
@ -103,6 +104,7 @@ class DocumentationParser {
// Don't process absolute links (based on protocol detection) // Don't process absolute links (based on protocol detection)
$urlParts = parse_url($url); $urlParts = parse_url($url);
if($urlParts && isset($urlParts['scheme'])) continue; if($urlParts && isset($urlParts['scheme'])) continue;
// Rewrite URL (relative or absolute) // Rewrite URL (relative or absolute)
@ -246,6 +248,7 @@ class DocumentationParser {
* @return String Markdown * @return String Markdown
*/ */
static function rewrite_relative_links($md, $page, $baselink) { static function rewrite_relative_links($md, $page, $baselink) {
$re = '/ $re = '/
([^\!]?) # exclude image format ([^\!]?) # exclude image format
\[ \[
@ -262,6 +265,7 @@ class DocumentationParser {
if($relativePath == '.') $relativePath = ''; if($relativePath == '.') $relativePath = '';
if($matches) { if($matches) {
foreach($matches[0] as $i => $match) { foreach($matches[0] as $i => $match) {
$title = $matches[2][$i]; $title = $matches[2][$i];
$url = $matches[3][$i]; $url = $matches[3][$i];
@ -275,9 +279,9 @@ class DocumentationParser {
// Rewrite URL (relative or absolute) // Rewrite URL (relative or absolute)
if(preg_match('/^\//', $url)) { if(preg_match('/^\//', $url)) {
$relativeUrl = $baselink . $url; $relativeUrl = Controller::join_links($baselink, $url);
} else { } else {
$relativeUrl = $baselink . '/' . $relativePath . '/' . $url; $relativeUrl = Controller::join_links($baselink, $relativePath, $url);
} }
// Resolve relative paths // Resolve relative paths

View File

@ -232,13 +232,14 @@ class DocumentationService {
* @return DocumentationEntity $module the registered module * @return DocumentationEntity $module the registered module
*/ */
public static function is_registered_module($module, $version = false, $lang = false) { public static function is_registered_module($module, $version = false, $lang = false) {
$check = ($module instanceof DocumentationEntity) ? $module->getModuleFolder() : (string) $module; $check = ($module instanceof DocumentationEntity) ? $module->getModuleFolder() : (string) $module;
if(isset(self::$registered_modules[$check])) { if(isset(self::$registered_modules[$check])) {
$module = self::$registered_modules[$check]; $module = self::$registered_modules[$check];
if($lang && !$module->hasLanguage($lang)) return false;
if($version && !$module->hasVersion($version)) return false; if(($lang && !$module->hasLanguage($lang)) || ($version && !$module->hasVersion($version))) {
return false;
}
return $module; return $module;
} }

View File

@ -25,7 +25,7 @@ class DocumentationViewer extends Controller {
/** /**
* @var string * @var string
*/ */
public $version = "current"; public $version = "";
/** /**
* @var string * @var string
@ -128,7 +128,6 @@ class DocumentationViewer extends Controller {
$thirdParam = $request->shift(); $thirdParam = $request->shift();
$this->Remaining = $request->shift(10); $this->Remaining = $request->shift(10);
DocumentationService::load_automatic_registration(); DocumentationService::load_automatic_registration();
if(isset($firstParam)) { if(isset($firstParam)) {
@ -146,33 +145,34 @@ class DocumentationViewer extends Controller {
return $this->response; return $this->response;
} }
else if(is_numeric($firstParam) || $firstParam == "current") {
// its a version number first in the form 2.4/en/sapphire $this->module = $firstParam;
$this->version = $firstParam;
$this->lang = $secondParam; $this->lang = $secondParam;
$this->module = $thirdParam;
if(isset($thirdParam) && (is_numeric($thirdParam))) {
$this->version = $thirdParam;
} }
else { else {
// we have a language first in the form /en/sapphire // current version so store one area para
array_unshift($this->Remaining, $thirdParam); array_unshift($this->Remaining, $thirdParam);
$this->lang = $firstParam; $this->version = "";
$this->module = $secondParam;
} }
} }
// 'current' version mapping // 'current' version mapping
$module = DocumentationService::is_registered_module($this->module, null, $this->getLang()); $module = DocumentationService::is_registered_module($this->module, null, $this->getLang());
if($module && $this->getVersion()) { if($module) {
$current = $module->getCurrentVersion(); $current = $module->getCurrentVersion();
$version = $this->getVersion(); $version = $this->getVersion();
if($version == 'current') { if(!$version || $version == '') {
$this->version = $current; $this->version = $current;
} else if($current == $version) { } else if($current == $version) {
$this->version = 'current'; $this->version = '';
$link = $this->Link($this->Remaining); $link = $this->Link($this->Remaining);
$this->response = new SS_HTTPResponse(); $this->response = new SS_HTTPResponse();
@ -182,7 +182,6 @@ class DocumentationViewer extends Controller {
} }
} }
return parent::handleRequest($request); return parent::handleRequest($request);
} }
@ -216,7 +215,8 @@ class DocumentationViewer extends Controller {
} }
/** /**
* Returns the current version * Returns the current version. If no version is set then it is the current
* set version so need to pull that from the module
* *
* @return String * @return String
*/ */
@ -479,7 +479,7 @@ class DocumentationViewer extends Controller {
if($page = $this->getPage()) { if($page = $this->getPage()) {
// Remove last portion of path (filename), we want a link to the folder base // Remove last portion of path (filename), we want a link to the folder base
$html = DocumentationParser::parse($page, $this->Link(array_slice($this->Remaining, -1, -1))); $html = DocumentationParser::parse($page, $this->Link(array_slice($this->Remaining, 0)));
return DBField::create("HTMLText", $html); return DBField::create("HTMLText", $html);
} }
@ -563,7 +563,7 @@ class DocumentationViewer extends Controller {
} }
} }
$link = Controller::join_links($base, self::get_link_base(), $version, $lang, $module, $action); $link = Controller::join_links($base, self::get_link_base(), $module, $lang, $version, $action);
return $link; return $link;
} }

View File

@ -15,21 +15,21 @@ class DocumentationPageTest extends SapphireTest {
$page->setEntity($entity); $page->setEntity($entity);
// single layer // single layer
$this->assertStringEndsWith('en/testmodule/test', $page->Link(), 'The page link should have no extension and have a language'); $this->assertStringEndsWith('testmodule/en/test', $page->Link(), 'The page link should have no extension and have a language');
$folder = new DocumentationPage(); $folder = new DocumentationPage();
$folder->setRelativePath('sort'); $folder->setRelativePath('sort');
$folder->setEntity($entity); $folder->setEntity($entity);
// folder, should have a trailing slash // folder, should have a trailing slash
$this->assertStringEndsWith('en/testmodule/sort/', $folder->Link()); $this->assertStringEndsWith('testmodule/en/sort/', $folder->Link());
// second // second
$nested = new DocumentationPage(); $nested = new DocumentationPage();
$nested->setRelativePath('subfolder/subpage.md'); $nested->setRelativePath('subfolder/subpage.md');
$nested->setEntity($entity); $nested->setEntity($entity);
$this->assertStringEndsWith('en/testmodule/subfolder/subpage', $nested->Link()); $this->assertStringEndsWith('testmodule/en/subfolder/subpage', $nested->Link());
// test with version. // test with version.
$entity = DocumentationService::register("versionlinks", BASE_PATH . "/sapphiredocs/tests/docs-2/", '1'); $entity = DocumentationService::register("versionlinks", BASE_PATH . "/sapphiredocs/tests/docs-2/", '1');
@ -37,7 +37,7 @@ class DocumentationPageTest extends SapphireTest {
$page->setRelativePath('test.md'); $page->setRelativePath('test.md');
$page->setEntity($entity); $page->setEntity($entity);
$page->setVersion('1'); $page->setVersion('1');
$this->assertStringEndsWith('1/en/versionlinks/test', $page->Link()); $this->assertStringEndsWith('versionlinks/en/1/test', $page->Link());
} }

View File

@ -139,6 +139,10 @@ HTML;
'[link: api](api:DataObject)', '[link: api](api:DataObject)',
$result $result
); );
$this->assertContains(
'[link: relative](mycontroller/cms/2.4/a-relative-file.md)',
$result
);
// Page in subfolder // Page in subfolder
$page = new DocumentationPage(); $page = new DocumentationPage();
@ -147,6 +151,11 @@ HTML;
$result = DocumentationParser::rewrite_relative_links($page->getMarkdown(), $page, 'mycontroller/cms/2.4/en/'); $result = DocumentationParser::rewrite_relative_links($page->getMarkdown(), $page, 'mycontroller/cms/2.4/en/');
$this->assertContains(
'[link: relative](mycontroller/cms/2.4/en/subfolder/subpage.md)',
$result
);
$this->assertContains( $this->assertContains(
'[link: absolute index](mycontroller/cms/2.4/en/)', '[link: absolute index](mycontroller/cms/2.4/en/)',
$result $result

View File

@ -41,7 +41,7 @@ class DocumentationViewerTest extends FunctionalTest {
function testGetModulePagesShort() { function testGetModulePagesShort() {
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '2.4/en/DocumentationViewerTests/subfolder/')); $response = $v->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/2.4/subfolder/'));
$pages = $v->getModulePages(); $pages = $v->getModulePages();
$arr = $pages->toArray(); $arr = $pages->toArray();
@ -52,7 +52,7 @@ class DocumentationViewerTest extends FunctionalTest {
function testGetModulePages() { function testGetModulePages() {
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '2.4/en/DocumentationViewerTests/subfolder/')); $response = $v->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/2.4/subfolder/'));
$pages = $v->getModulePages(); $pages = $v->getModulePages();
$this->assertEquals( $this->assertEquals(
array('sort/', 'subfolder/', 'test.md'), array('sort/', 'subfolder/', 'test.md'),
@ -69,9 +69,9 @@ class DocumentationViewerTest extends FunctionalTest {
$links = $pages->column('Link'); $links = $pages->column('Link');
$this->assertStringEndsWith('2.4/en/DocumentationViewerTests/sort/', $links[0]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/sort/', $links[0]);
$this->assertStringEndsWith('2.4/en/DocumentationViewerTests/subfolder/', $links[1]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/subfolder/', $links[1]);
$this->assertStringEndsWith('2.4/en/DocumentationViewerTests/test', $links[2]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/test', $links[2]);
// Children // Children
$pagesArr = $pages->toArray(); $pagesArr = $pages->toArray();
@ -96,36 +96,36 @@ class DocumentationViewerTest extends FunctionalTest {
$child2Links = $children->column('Link'); $child2Links = $children->column('Link');
$subpage = $children->First(); $subpage = $children->First();
$this->assertStringEndsWith('2.4/en/DocumentationViewerTests/subfolder/subpage', $child2Links[0]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/subfolder/subpage', $child2Links[0]);
$this->assertStringEndsWith('2.4/en/DocumentationViewerTests/subfolder/subsubfolder/', $child2Links[1]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/subfolder/subsubfolder/', $child2Links[1]);
} }
function testCurrentRedirection() { function testCurrentRedirection() {
$response = $this->get('dev/docs/3.0/en/DocumentationViewerTests/test'); $response = $this->get('dev/docs/DocumentationViewerTests/en/3.0/test');
$this->assertEquals(301, $response->getStatusCode()); $this->assertEquals(301, $response->getStatusCode());
$this->assertEquals( $this->assertEquals(
Director::absoluteBaseURL() . 'dev/docs/current/en/DocumentationViewerTests/test/', Director::absoluteBaseURL() . 'dev/docs/DocumentationViewerTests/en/test/',
$response->getHeader('Location'), $response->getHeader('Location'),
'Redirection to current on page' 'Redirection to current on page'
); );
$response = $this->get('dev/docs/3.0/en/DocumentationViewerTests/'); $response = $this->get('dev/docs/DocumentationViewerTests/en/3.0');
$this->assertEquals(301, $response->getStatusCode()); $this->assertEquals(301, $response->getStatusCode());
$this->assertEquals( $this->assertEquals(
Director::absoluteBaseURL() . 'dev/docs/current/en/DocumentationViewerTests/', Director::absoluteBaseURL() . 'dev/docs/DocumentationViewerTests/en/',
$response->getHeader('Location'), $response->getHeader('Location'),
'Redirection to current on index' 'Redirection to current on index'
); );
$response = $this->get('dev/docs/2.3/en/DocumentationViewerTests/'); $response = $this->get('dev/docs/DocumentationViewerTests/en/2.3');
$this->assertEquals(200, $response->getStatusCode(), 'No redirection on older versions'); $this->assertEquals(200, $response->getStatusCode(), 'No redirection on older versions');
} }
function testUrlParsing() { function testUrlParsing() {
// Module index // Module index
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '2.3/en/DocumentationViewerTests/test')); $response = $v->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/2.3/test'));
$this->assertEquals('2.3', $v->getVersion()); $this->assertEquals('2.3', $v->getVersion());
$this->assertEquals('en', $v->getLang()); $this->assertEquals('en', $v->getLang());
$this->assertEquals('DocumentationViewerTests', $v->module); $this->assertEquals('DocumentationViewerTests', $v->module);
@ -133,7 +133,7 @@ class DocumentationViewerTest extends FunctionalTest {
// Module index without version and language. Should pick up the defaults // Module index without version and language. Should pick up the defaults
$v2 = new DocumentationViewer(); $v2 = new DocumentationViewer();
$response = $v2->handleRequest(new SS_HTTPRequest('GET', 'en/DocumentationViewerTests/test')); $response = $v2->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/test'));
$this->assertEquals('3.0', $v2->getVersion()); $this->assertEquals('3.0', $v2->getVersion());
$this->assertEquals('en', $v2->getLang()); $this->assertEquals('en', $v2->getLang());
@ -143,7 +143,7 @@ class DocumentationViewerTest extends FunctionalTest {
// Overall index // Overall index
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '')); $response = $v->handleRequest(new SS_HTTPRequest('GET', ''));
$this->assertEquals('current', $v->getVersion()); $this->assertEquals('', $v->getVersion());
$this->assertEquals('en', $v->getLang()); $this->assertEquals('en', $v->getLang());
$this->assertEquals('', $v->module); $this->assertEquals('', $v->module);
$this->assertEquals(array(), $v->Remaining); $this->assertEquals(array(), $v->Remaining);
@ -152,35 +152,35 @@ class DocumentationViewerTest extends FunctionalTest {
function testBreadcrumbs() { function testBreadcrumbs() {
// Module index // Module index
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '2.4/en/DocumentationViewerTests/')); $response = $v->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/2.4'));
$crumbs = $v->getBreadcrumbs(); $crumbs = $v->getBreadcrumbs();
$this->assertEquals(1, $crumbs->Count()); $this->assertEquals(1, $crumbs->Count());
$crumbLinks = $crumbs->column('Link'); $crumbLinks = $crumbs->column('Link');
$this->assertStringEndsWith('DocumentationViewerTests/', $crumbLinks[0]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/', $crumbLinks[0]);
// Subfolder index // Subfolder index
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '2.4/en/DocumentationViewerTests/subfolder/')); $response = $v->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/2.4/subfolder/'));
$crumbs = $v->getBreadcrumbs(); $crumbs = $v->getBreadcrumbs();
$this->assertEquals(2, $crumbs->Count()); $this->assertEquals(2, $crumbs->Count());
$crumbLinks = $crumbs->column('Link'); $crumbLinks = $crumbs->column('Link');
$this->assertStringEndsWith('DocumentationViewerTests/', $crumbLinks[0]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/', $crumbLinks[0]);
$this->assertStringEndsWith('DocumentationViewerTests/subfolder/', $crumbLinks[1]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/subfolder/', $crumbLinks[1]);
// Subfolder page // Subfolder page
$v = new DocumentationViewer(); $v = new DocumentationViewer();
$response = $v->handleRequest(new SS_HTTPRequest('GET', '2.4/en/DocumentationViewerTests/subfolder/subpage')); $response = $v->handleRequest(new SS_HTTPRequest('GET', 'DocumentationViewerTests/en/2.4/subfolder/subpage'));
$crumbs = $v->getBreadcrumbs(); $crumbs = $v->getBreadcrumbs();
$this->assertEquals(3, $crumbs->Count()); $this->assertEquals(3, $crumbs->Count());
$crumbLinks = $crumbs->column('Link'); $crumbLinks = $crumbs->column('Link');
$this->assertStringEndsWith('DocumentationViewerTests/', $crumbLinks[0]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/', $crumbLinks[0]);
$this->assertStringEndsWith('DocumentationViewerTests/subfolder/', $crumbLinks[1]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/subfolder/', $crumbLinks[1]);
$this->assertStringEndsWith('DocumentationViewerTests/subfolder/subpage/', $crumbLinks[2]); $this->assertStringEndsWith('DocumentationViewerTests/en/2.4/subfolder/subpage/', $crumbLinks[2]);
} }
function testRouting() { function testRouting() {
$response = $this->get('dev/docs/2.4/en/DocumentationViewerTests/test'); $response = $this->get('dev/docs/DocumentationViewerTests/en/2.4/test');
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
$this->assertContains('english test', $response->getBody(), 'Toplevel content page'); $this->assertContains('english test', $response->getBody(), 'Toplevel content page');
} }

View File

@ -1,6 +1,9 @@
[link: absolute index](/) [link: absolute index](/)
[link: absolute index with name](/index) [link: absolute index with name](/index)
[link: relative index](../) [link: relative index](../)
[link: relative](../subfolder/subpage.md)
[link: relative multiline
](../subfolder/subpage.md)
[link: relative parent page](../test) [link: relative parent page](../test)
[link: absolute parent page](/test) [link: absolute parent page](/test)

View File

@ -9,6 +9,8 @@ test
[link: with anchor](/test#anchor) [link: with anchor](/test#anchor)
[link: http](http://silverstripe.org) [link: http](http://silverstripe.org)
[link: api](api:DataObject) [link: api](api:DataObject)
[link: relative](../a-relative-file.md)
[api:DataObject::$has_one] [api:DataObject::$has_one]
:::php :::php