mirror of
https://github.com/silverstripe/silverstripe-docsviewer
synced 2024-10-22 09:05:56 +00:00
Support parsing and removing a YAML metadata block in markdown.
This commit is contained in:
parent
d474be2841
commit
484e57e404
@ -218,7 +218,7 @@ class DocumentationParser
|
|||||||
!
|
!
|
||||||
\[
|
\[
|
||||||
(.*?) # image title (non greedy)
|
(.*?) # image title (non greedy)
|
||||||
\]
|
\]
|
||||||
\(
|
\(
|
||||||
(.*?) # image url (non greedy)
|
(.*?) # image url (non greedy)
|
||||||
\)
|
\)
|
||||||
@ -432,18 +432,18 @@ class DocumentationParser
|
|||||||
public static function rewrite_relative_links($md, $page)
|
public static function rewrite_relative_links($md, $page)
|
||||||
{
|
{
|
||||||
$baselink = $page->getEntity()->Link();
|
$baselink = $page->getEntity()->Link();
|
||||||
|
|
||||||
$re = '/
|
$re = '/
|
||||||
([^\!]?) # exclude image format
|
([^\!]?) # exclude image format
|
||||||
\[
|
\[
|
||||||
(.*?) # link title (non greedy)
|
(.*?) # link title (non greedy)
|
||||||
\]
|
\]
|
||||||
\(
|
\(
|
||||||
(.*?) # link url (non greedy)
|
(.*?) # link url (non greedy)
|
||||||
\)
|
\)
|
||||||
/x';
|
/x';
|
||||||
preg_match_all($re, $md, $matches);
|
preg_match_all($re, $md, $matches);
|
||||||
|
|
||||||
// relative path (relative to module base folder), without the filename.
|
// relative path (relative to module base folder), without the filename.
|
||||||
// For "sapphire/en/current/topics/templates", this would be "templates"
|
// For "sapphire/en/current/topics/templates", this would be "templates"
|
||||||
$relativePath = DocumentationHelper::normalizePath(dirname($page->getRelativePath()));
|
$relativePath = DocumentationHelper::normalizePath(dirname($page->getRelativePath()));
|
||||||
@ -522,36 +522,4 @@ class DocumentationParser
|
|||||||
return $md;
|
return $md;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Strips out the metadata for a page
|
|
||||||
*
|
|
||||||
* @param DocumentationPage
|
|
||||||
*/
|
|
||||||
public static function retrieve_meta_data(DocumentationPage $page)
|
|
||||||
{
|
|
||||||
$md = $page->getMarkdown();
|
|
||||||
if ($md) {
|
|
||||||
// get the text up to the first empty line
|
|
||||||
$extPattern = "/^(.+)\n\r*\n/Uis";
|
|
||||||
$matches = preg_match($extPattern, $md, $block);
|
|
||||||
|
|
||||||
if ($matches && $block[1]) {
|
|
||||||
|
|
||||||
// find the key/value pairs
|
|
||||||
$lines = preg_split('/\v+/', $block[1]);
|
|
||||||
$key = '';
|
|
||||||
$value = '';
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
if (strpos($line, ':') !== false) {
|
|
||||||
list($key, $value) = explode(':', $line, 2);
|
|
||||||
$key = trim($key);
|
|
||||||
$value = trim($value);
|
|
||||||
} else {
|
|
||||||
$value .= ' ' . trim($line);
|
|
||||||
}
|
|
||||||
$page->setMetaData($key, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -172,13 +172,6 @@ class DocumentationPage extends ViewableData
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setMetaData($key, $value)
|
|
||||||
{
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
$this->$key = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -280,37 +273,66 @@ class DocumentationPage extends ViewableData
|
|||||||
*/
|
*/
|
||||||
public function populateMetaDataFromText(&$md, $removeMetaData = false)
|
public function populateMetaDataFromText(&$md, $removeMetaData = false)
|
||||||
{
|
{
|
||||||
if ($md) {
|
if (!$md) {
|
||||||
// get the text up to the first empty line
|
return;
|
||||||
$extPattern = "/^(.+)\n\r*\n/Uis";
|
}
|
||||||
$matches = preg_match($extPattern, $md, $block);
|
|
||||||
|
|
||||||
if ($matches && $block[1]) {
|
// See if there is YAML metadata block at the top of the document. e.g.
|
||||||
$metaDataFound = false;
|
// ---
|
||||||
|
// property: value
|
||||||
// find the key/value pairs
|
// another: value
|
||||||
$lines = preg_split('/\v+/', $block[1]);
|
// ---
|
||||||
$key = '';
|
//
|
||||||
$value = '';
|
// If we found one, then we'll use a YAML parser to extract the
|
||||||
foreach ($lines as $line) {
|
// data out and then remove the whole block from the markdown string.
|
||||||
if (strpos($line, ':') !== false) {
|
$parser = new \Mni\FrontYAML\Parser();
|
||||||
list($key, $value) = explode(':', $line, 2);
|
$document = $parser->parse($md, false);
|
||||||
$key = trim($key);
|
$yaml = $document->getYAML();
|
||||||
$value = trim($value);
|
if ($yaml) {
|
||||||
} else {
|
foreach ($yaml as $key => $value) {
|
||||||
$value .= ' ' . trim($line);
|
if (!property_exists(get_class($this), $key)) {
|
||||||
}
|
continue;
|
||||||
if (property_exists(get_class(), $key)) {
|
|
||||||
$this->$key = $value;
|
|
||||||
$metaDataFound = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$this->$key = $value;
|
||||||
|
}
|
||||||
|
if ($removeMetaData) {
|
||||||
|
$md = $document->getContent();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// optionally remove the metadata block (only on the page that
|
// this is the alternative way of parsing the properties out that don't contain
|
||||||
// is displayed)
|
// a YAML block declared with ---
|
||||||
if ($metaDataFound && $removeMetaData) {
|
//
|
||||||
$md = preg_replace($extPattern, '', $md);
|
// get the text up to the first empty line
|
||||||
|
$extPattern = "/^(.+)\n\r*\n/Uis";
|
||||||
|
$matches = preg_match($extPattern, $md, $block);
|
||||||
|
|
||||||
|
if ($matches && $block[1]) {
|
||||||
|
$metaDataFound = false;
|
||||||
|
|
||||||
|
// find the key/value pairs
|
||||||
|
$lines = preg_split('/\v+/', $block[1]);
|
||||||
|
$key = '';
|
||||||
|
$value = '';
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (strpos($line, ':') !== false) {
|
||||||
|
list($key, $value) = explode(':', $line, 2);
|
||||||
|
$key = trim($key);
|
||||||
|
$value = trim($value);
|
||||||
|
} else {
|
||||||
|
$value .= ' ' . trim($line);
|
||||||
}
|
}
|
||||||
|
if (property_exists(get_class(), $key)) {
|
||||||
|
$this->$key = $value;
|
||||||
|
$metaDataFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// optionally remove the metadata block (only on the page that
|
||||||
|
// is displayed)
|
||||||
|
if ($metaDataFound && $removeMetaData) {
|
||||||
|
$md = preg_replace($extPattern, '', $md);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,3 +350,4 @@ class DocumentationPage extends ViewableData
|
|||||||
return sprintf(get_class($this) .': %s)', $this->getPath());
|
return sprintf(get_class($this) .': %s)', $this->getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
{
|
{
|
||||||
"name": "silverstripe/docsviewer",
|
"name": "silverstripe/docsviewer",
|
||||||
"description": "Documentation viewer module for SilverStripe",
|
"description": "Documentation viewer module for SilverStripe",
|
||||||
"type": "silverstripe-module",
|
"type": "silverstripe-module",
|
||||||
"keywords": ["silverstripe", "documentation"],
|
"keywords": ["silverstripe", "documentation"],
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"authors": [{
|
"authors": [{
|
||||||
"name": "Will Rossiter",
|
"name": "Will Rossiter",
|
||||||
"homepage": "http://wilr.github.io",
|
"homepage": "http://wilr.github.io",
|
||||||
"email": "will@fullscreen.io"
|
"email": "will@fullscreen.io"
|
||||||
}],
|
}],
|
||||||
"support": {
|
"support": {
|
||||||
"email": "will@fullscreen.io",
|
"email": "will@fullscreen.io",
|
||||||
"irc": "irc://irc.freenode.org/silverstripe"
|
"irc": "irc://irc.freenode.org/silverstripe"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/framework": "~3.1",
|
"silverstripe/framework": "~3.1",
|
||||||
"erusev/parsedown-extra": "0.2.2",
|
"erusev/parsedown-extra": "0.2.2",
|
||||||
"erusev/parsedown": "~1.1.0"
|
"erusev/parsedown": "~1.1.0",
|
||||||
},
|
"mnapoli/front-yaml": "^1.5"
|
||||||
"suggest": {
|
},
|
||||||
"silverstripe/staticpublisher": "Allows publishing documentation as HTML"
|
"suggest": {
|
||||||
}
|
"silverstripe/staticpublisher": "Allows publishing documentation as HTML"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ class DocumentationParserTest extends SapphireTest
|
|||||||
public function tearDown()
|
public function tearDown()
|
||||||
{
|
{
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
|
|
||||||
Config::unnest();
|
Config::unnest();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ class DocumentationParserTest extends SapphireTest
|
|||||||
|
|
||||||
Config::nest();
|
Config::nest();
|
||||||
|
|
||||||
// explicitly use dev/docs. Custom paths should be tested separately
|
// explicitly use dev/docs. Custom paths should be tested separately
|
||||||
Config::inst()->update(
|
Config::inst()->update(
|
||||||
'DocumentationViewer', 'link_base', 'dev/docs/'
|
'DocumentationViewer', 'link_base', 'dev/docs/'
|
||||||
);
|
);
|
||||||
@ -48,7 +48,7 @@ class DocumentationParserTest extends SapphireTest
|
|||||||
'subpage.md',
|
'subpage.md',
|
||||||
DOCSVIEWER_PATH. '/tests/docs/en/subfolder/subpage.md'
|
DOCSVIEWER_PATH. '/tests/docs/en/subfolder/subpage.md'
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->subSubPage = new DocumentationPage(
|
$this->subSubPage = new DocumentationPage(
|
||||||
$this->entity,
|
$this->entity,
|
||||||
'subsubpage.md',
|
'subsubpage.md',
|
||||||
@ -128,13 +128,12 @@ with multiple
|
|||||||
lines
|
lines
|
||||||
and tab indent
|
and tab indent
|
||||||
and escaped < brackets
|
and escaped < brackets
|
||||||
|
|
||||||
```
|
```
|
||||||
Normal text after code block
|
Normal text after code block
|
||||||
HTML;
|
HTML;
|
||||||
|
|
||||||
$this->assertContains($expected, $result, 'Custom code blocks with ::: prefix');
|
$this->assertContains($expected, $result, 'Custom code blocks with ::: prefix');
|
||||||
|
|
||||||
$expected = <<<HTML
|
$expected = <<<HTML
|
||||||
```
|
```
|
||||||
code block
|
code block
|
||||||
@ -149,7 +148,7 @@ Fenced code block
|
|||||||
```
|
```
|
||||||
HTML;
|
HTML;
|
||||||
$this->assertContains($expected, $result, 'Backtick code blocks');
|
$this->assertContains($expected, $result, 'Backtick code blocks');
|
||||||
|
|
||||||
$expected = <<<HTML
|
$expected = <<<HTML
|
||||||
```php
|
```php
|
||||||
Fenced box with
|
Fenced box with
|
||||||
@ -171,7 +170,7 @@ HTML;
|
|||||||
$this->indexPage->getMarkdown(),
|
$this->indexPage->getMarkdown(),
|
||||||
$this->indexPage
|
$this->indexPage
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'[link: subfolder index](dev/docs/en/documentationparsertest/2.4/subfolder/)',
|
'[link: subfolder index](dev/docs/en/documentationparsertest/2.4/subfolder/)',
|
||||||
$result
|
$result
|
||||||
@ -183,7 +182,7 @@ HTML;
|
|||||||
$this->page->getMarkdown(),
|
$this->page->getMarkdown(),
|
||||||
$this->page
|
$this->page
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'[link: subfolder index](dev/docs/en/documentationparsertest/2.4/subfolder/)',
|
'[link: subfolder index](dev/docs/en/documentationparsertest/2.4/subfolder/)',
|
||||||
$result
|
$result
|
||||||
@ -217,7 +216,7 @@ HTML;
|
|||||||
'[link: relative](dev/docs/en/documentationparsertest/2.4/subfolder/subpage.md/)',
|
'[link: relative](dev/docs/en/documentationparsertest/2.4/subfolder/subpage.md/)',
|
||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'[link: absolute index](dev/docs/en/documentationparsertest/2.4/)',
|
'[link: absolute index](dev/docs/en/documentationparsertest/2.4/)',
|
||||||
$result
|
$result
|
||||||
@ -233,22 +232,22 @@ HTML;
|
|||||||
'[link: relative index](dev/docs/en/documentationparsertest/2.4/)',
|
'[link: relative index](dev/docs/en/documentationparsertest/2.4/)',
|
||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'[link: relative parent page](dev/docs/en/documentationparsertest/2.4/test/)',
|
'[link: relative parent page](dev/docs/en/documentationparsertest/2.4/test/)',
|
||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'[link: absolute parent page](dev/docs/en/documentationparsertest/2.4/test/)',
|
'[link: absolute parent page](dev/docs/en/documentationparsertest/2.4/test/)',
|
||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
|
|
||||||
$result = DocumentationParser::rewrite_relative_links(
|
$result = DocumentationParser::rewrite_relative_links(
|
||||||
$this->subSubPage->getMarkdown(),
|
$this->subSubPage->getMarkdown(),
|
||||||
$this->subSubPage
|
$this->subSubPage
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
'[link: absolute index](dev/docs/en/documentationparsertest/2.4/)',
|
'[link: absolute index](dev/docs/en/documentationparsertest/2.4/)',
|
||||||
$result
|
$result
|
||||||
@ -286,7 +285,7 @@ HTML;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function testImageRewrites()
|
public function testImageRewrites()
|
||||||
{
|
{
|
||||||
$result = DocumentationParser::rewrite_image_links(
|
$result = DocumentationParser::rewrite_image_links(
|
||||||
@ -311,7 +310,7 @@ HTML;
|
|||||||
),
|
),
|
||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
|
|
||||||
$expected = Controller::join_links(
|
$expected = Controller::join_links(
|
||||||
Director::absoluteBaseURL(), DOCSVIEWER_DIR, '/tests/docs/en/_images/image.png'
|
Director::absoluteBaseURL(), DOCSVIEWER_DIR, '/tests/docs/en/_images/image.png'
|
||||||
);
|
);
|
||||||
@ -321,7 +320,7 @@ HTML;
|
|||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testApiLinks()
|
public function testApiLinks()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -357,14 +356,14 @@ HTML;
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHeadlineAnchors()
|
public function testHeadlineAnchors()
|
||||||
{
|
{
|
||||||
$result = DocumentationParser::rewrite_heading_anchors(
|
$result = DocumentationParser::rewrite_heading_anchors(
|
||||||
$this->page->getMarkdown(),
|
$this->page->getMarkdown(),
|
||||||
$this->page
|
$this->page
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
# Heading one {#Heading-one}
|
# Heading one {#Heading-one}
|
||||||
|
|
||||||
@ -379,7 +378,7 @@ HTML;
|
|||||||
## Heading duplicate {#Heading-duplicate-2}
|
## Heading duplicate {#Heading-duplicate-2}
|
||||||
|
|
||||||
## Heading duplicate {#Heading-duplicate-3}
|
## Heading duplicate {#Heading-duplicate-3}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$this->assertContains('# Heading one {#heading-one}', $result);
|
$this->assertContains('# Heading one {#heading-one}', $result);
|
||||||
@ -395,14 +394,41 @@ HTML;
|
|||||||
|
|
||||||
public function testRetrieveMetaData()
|
public function testRetrieveMetaData()
|
||||||
{
|
{
|
||||||
DocumentationParser::retrieve_meta_data($this->metaDataPage);
|
$this->metaDataPage->getMarkdown(true);
|
||||||
|
$this->assertEquals('Foo Bar\'s Test page.', $this->metaDataPage->getTitle());
|
||||||
$this->assertEquals('Dr. Foo Bar.', $this->metaDataPage->author);
|
$this->assertEquals('A long intro that splits over many lines', $this->metaDataPage->getIntroduction());
|
||||||
$this->assertEquals("Foo Bar's Test page.", $this->metaDataPage->getTitle());
|
$this->assertEquals('Foo Bar Test page description', $this->metaDataPage->getSummary());
|
||||||
$this->assertEquals("Foo Bar's Test page.", $this->metaDataPage->getTitle());
|
|
||||||
$this->assertEquals("A long intro that splits over many lines", $this->metaDataPage->getIntroduction());
|
$parsed = DocumentationParser::parse($this->metaDataPage);
|
||||||
|
$expected = <<<HTML
|
||||||
|
<h1 id="content">Content</h1>
|
||||||
|
HTML;
|
||||||
|
$this->assertEquals($parsed, $expected, 'Metadata block removed, parsed correctly');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRetrieveMetaDataYamlBlock()
|
||||||
|
{
|
||||||
|
$page = new DocumentationPage(
|
||||||
|
$this->entityAlt,
|
||||||
|
'MetaDataYamlBlockTest.md',
|
||||||
|
DOCSVIEWER_PATH . '/tests/docs-parser/en/MetaDataYamlBlockTest.md'
|
||||||
|
);
|
||||||
|
$page->getMarkdown(true);
|
||||||
|
|
||||||
|
$this->assertEquals('Foo Bar\'s Test page.', $page->getTitle());
|
||||||
|
$this->assertEquals('This is the page\'s description', $page->getSummary());
|
||||||
|
|
||||||
|
$parsed = DocumentationParser::parse($page);
|
||||||
|
$expected = <<<HTML
|
||||||
|
<h2 id="content-2">Content</h2>
|
||||||
|
<p>Content goes here.</p>
|
||||||
|
<hr />
|
||||||
|
<h2>randomblock: ignored</h2>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
$this->assertEquals($parsed, $expected, 'YAML metadata block removed, parsed correctly');
|
||||||
|
}
|
||||||
|
|
||||||
public function testRewritingRelativeLinksToFiles()
|
public function testRewritingRelativeLinksToFiles()
|
||||||
{
|
{
|
||||||
$parsed = DocumentationParser::parse($this->filePage);
|
$parsed = DocumentationParser::parse($this->filePage);
|
||||||
@ -411,7 +437,7 @@ HTML;
|
|||||||
DOCSVIEWER_DIR .'/tests/docs-parser/en/_images/external_link.png',
|
DOCSVIEWER_DIR .'/tests/docs-parser/en/_images/external_link.png',
|
||||||
$parsed
|
$parsed
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
DOCSVIEWER_DIR .'/tests/docs-parser/en/_images/test.tar.gz',
|
DOCSVIEWER_DIR .'/tests/docs-parser/en/_images/test.tar.gz',
|
||||||
$parsed
|
$parsed
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
Title: Foo Bar's Test page.
|
title: Foo Bar's Test page.
|
||||||
Author: Dr. Foo Bar.
|
summary: Foo Bar Test page description
|
||||||
Another: Test.
|
introduction: A long intro that
|
||||||
Introduction: A long intro that
|
|
||||||
splits over
|
splits over
|
||||||
many lines
|
many lines
|
||||||
|
|
||||||
|
13
tests/docs-parser/en/MetaDataYamlBlockTest.md
Normal file
13
tests/docs-parser/en/MetaDataYamlBlockTest.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: "Foo Bar's Test page."
|
||||||
|
summary: "This is the page's description"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
Content goes here.
|
||||||
|
|
||||||
|
---
|
||||||
|
randomblock: ignored
|
||||||
|
---
|
@ -29,7 +29,6 @@ test
|
|||||||
lines
|
lines
|
||||||
and tab indent
|
and tab indent
|
||||||
and escaped < brackets
|
and escaped < brackets
|
||||||
|
|
||||||
|
|
||||||
Normal text after code block
|
Normal text after code block
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user