Add support for [CHILDREN] shortcode

This allows you to build up dynamic listing pages much better.
This commit is contained in:
Will Rossiter 2014-09-19 23:29:22 +12:00
parent b489d5120a
commit 52733d6ebf
19 changed files with 307 additions and 107 deletions

View File

@ -60,9 +60,9 @@ class DocumentationManifest {
private $entity;
/**
* @var array
* @var ArrayList
*/
private $registeredEntities = array();
private $registeredEntities;
/**
* Constructs a new template manifest. The manifest is not actually built
@ -74,6 +74,7 @@ class DocumentationManifest {
public function __construct($forceRegen = false) {
$this->cacheKey = 'manifest';
$this->forceRegen = $forceRegen;
$this->registeredEntities = new ArrayList();
$this->cache = SS_Cache::factory('DocumentationManifest', 'Core', array(
'automatic_serialization' => true,
@ -142,7 +143,7 @@ class DocumentationManifest {
$entity->setIsDefaultEntity($details['DefaultEntity']);
}
$this->registeredEntities[] = $entity;
$this->registeredEntities->push($entity);
}
}
}
@ -158,7 +159,7 @@ class DocumentationManifest {
}
/**
* @return array
* @return ArrayList
*/
public function getEntities() {
return $this->registeredEntities;
@ -300,7 +301,8 @@ class DocumentationManifest {
'title' => $folder->getTitle(),
'basename' => $basename,
'filepath' => $path,
'type' => 'DocumentationFolder'
'type' => 'DocumentationFolder',
'summary' => ''
);
}
@ -472,7 +474,7 @@ class DocumentationManifest {
// if the page is the index page then hide it from the menu
if(strpos(strtolower($pagePath), '/index.md/')) {
continue;
$pagePath = substr($pagePath, 0, strpos($pagePath, "index.md/"));
}
// only pull it up if it's one more level depth
@ -489,6 +491,7 @@ class DocumentationManifest {
'Link' => Controller::join_links($base, $url, '/'),
'Title' => $page['title'],
'LinkingMode' => $mode,
'Summary' => $page['summary'],
'Children' => $children
)));
}
@ -502,7 +505,7 @@ class DocumentationManifest {
*
* @return ArrayList
*/
public function getAllVersions(DocumentationEntity $entity) {
public function getAllVersionsOfEntity(DocumentationEntity $entity) {
$all = new ArrayList();
foreach($this->getEntities() as $check) {
@ -564,4 +567,23 @@ class DocumentationManifest {
return $output;
}
/**
* Returns a sorted array of all the unique versions registered
*/
public function getAllVersions() {
$versions = array();
foreach($this->getEntities() as $entity) {
$versions[$entity->getVersion()] = $entity->getVersion();
}
$uniqueVersions = array_unique(
ArrayLib::flatten(array_values($versions))
);
asort($uniqueVersions);
return array_combine($uniqueVersions, $uniqueVersions);
}
}

View File

@ -383,7 +383,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->getRelativeLink());
$relativePath = dirname($page->getRelativePath());
if($relativePath == '.') {
$relativePath = '';

View File

@ -359,8 +359,33 @@ class DocumentationViewer extends Controller {
*/
public function getContent() {
$page = $this->getPage();
$html = $page->getHTML();
$html = $this->replaceChildrenCalls($html);
return DBField::create_field("HTMLText", $page->getHTML());
return DBField::create_field("HTMLText", $html);
}
public function replaceChildrenCalls($html) {
$codes = new ShortcodeParser();
$codes->register('CHILDREN', array($this, 'includeChildren'));
return $codes->parse($html);
}
public function includeChildren($args) {
if(isset($args['Folder'])) {
$children = $this->getManifest()->getChildrenFor(
Controller::join_links(dirname($this->record->getPath()), $args['Folder'])
);
} else {
$children = $this->getManifest()->getChildrenFor(
dirname($this->record->getPath())
);
}
return $this->customise(new ArrayData(array(
'Children' => $children
)))->renderWith('Includes/DocumentationPages');
}
/**

View File

@ -22,7 +22,7 @@ class DocumentationViewerVersionWarning extends Extension {
return false;
}
$versions = $this->owner->getManifest()->getAllVersions($entity);
$versions = $this->owner->getManifest()->getAllVersionsOfEntity($entity);
if($entity->getIsStable()) {
return false;

View File

@ -6,30 +6,14 @@
class DocumentationAdvancedSearchForm extends Form {
public function __construct($controller) {
$versions = $controller->getManifest()->getAllVersions();
$entities = $controller->getManifest()->getEntities();
$versions = array();
foreach($entities as $entity) {
foreach($entity->getVersions() as $version) {
$versions[$version->getVersion()] = $version->getVersion();
}
}
// get a list of all the unique versions
$uniqueVersions = array_unique(
ArrayLib::flatten(array_values($versions))
);
asort($uniqueVersions);
$uniqueVersions = array_combine($uniqueVersions,$uniqueVersions);
$q = ($q = $controller->getSearchQuery()) ? $q->NoHTML() : "";
// klude to take an array of objects down to a simple map
$entities = new ArrayList($entities);
$entities = $entities->map('Folder', 'Title');
// if we haven't gone any search limit then we're searching everything
$searchedEntities = $controller->getSearchedEntities();
@ -40,16 +24,16 @@ class DocumentationAdvancedSearchForm extends Form {
$searchedVersions = $controller->getSearchedVersions();
if(count($searchedVersions) < 1) {
$searchedVersions = $uniqueVersions;
$searchedVersions = $versions;
}
$fields = new FieldList(
new TextField('Search', _t('DocumentationViewer.KEYWORDS', 'Keywords'), $q),
new TextField('q', _t('DocumentationViewer.KEYWORDS', 'Keywords'), $q),
new CheckboxSetField('Entities', _t('DocumentationViewer.MODULES', 'Modules'), $entities, $searchedEntities),
new CheckboxSetField(
'Versions',
_t('DocumentationViewer.VERSIONS', 'Versions'),
$uniqueVersions, $searchedVersions
$versions, $searchedVersions
)
);
@ -69,6 +53,6 @@ class DocumentationAdvancedSearchForm extends Form {
$this->disableSecurityToken();
$this->setFormMethod('GET');
$this->setFormAction($controller->Link('search'));
$this->setFormAction($controller->Link('results'));
}
}

View File

@ -16,13 +16,8 @@ class DocumentationSearchForm extends Form {
$this->disableSecurityToken();
$this->setFormMethod('GET');
$this->setFormAction($controller->Link('results'));
if($controller->getPage()) {
$this->setFormAction($controller->getPage()->getEntity()->Link());
} else {
$this->setFormAction($controller->Link());
}
$this->addExtraClass('search');
}
}

View File

@ -15,12 +15,7 @@ class DocumentationPage extends ViewableData {
/**
* @var string
*/
protected $title;
/**
* @var string
*/
protected $summary;
protected $title, $summary, $introduction;
/**
* @var DocumentationEntity
@ -39,6 +34,8 @@ class DocumentationPage extends ViewableData {
*/
protected $filename;
protected $read = false;
/**
* @param DocumentationEntity $entity
* @param string $filename
@ -63,7 +60,7 @@ class DocumentationPage extends ViewableData {
* @return string
*/
public function getBreadcrumbTitle($divider = ' - ') {
$pathParts = explode('/', trim($this->getRelativeLink(), '/'));
$pathParts = explode('/', trim($this->getRelativePath(), '/'));
// from the page from this
array_pop($pathParts);
@ -74,8 +71,16 @@ class DocumentationPage extends ViewableData {
$titleParts = array_map(array(
'DocumentationHelper', 'clean_page_name'
), $pathParts);
array_unshift($titleParts, $this->getTitle());
$titleParts = array_filter($titleParts, function($val) {
if($val) {
return $val;
}
});
if($this->getTitle()) {
array_unshift($titleParts, $this->getTitle());
}
return implode($divider, $titleParts);
}
@ -131,6 +136,8 @@ class DocumentationPage extends ViewableData {
return $md;
}
$this->read = true;
}
catch(InvalidArgumentException $e) {
@ -144,6 +151,14 @@ class DocumentationPage extends ViewableData {
$this->$key = $value;
}
public function getIntroduction() {
if(!$this->read) {
$this->getMarkdown();
}
return $this->introduction;
}
/**
* Parse a file and return the parsed HTML version.
@ -160,12 +175,15 @@ class DocumentationPage extends ViewableData {
}
/**
* This should return the link from the entity root to the page. The link
* value has the cleaned version of the folder names. See
* {@link getRelativePath()} for the actual file path.
*
* @return string
*/
public function getRelativeLink() {
$path = str_replace($this->entity->getPath(), '', $this->getPath());
$path = $this->getRelativePath();
$url = explode('/', $path);
$url = implode('/', array_map(function($a) {
return DocumentationHelper::clean_page_url($a);
}, $url));
@ -175,6 +193,17 @@ class DocumentationPage extends ViewableData {
return $url;
}
/**
* This should return the link from the entity root to the page. For the url
* polished version, see {@link getRelativeLink()}.
*
* @return string
*/
public function getRelativePath() {
return str_replace($this->entity->getPath(), '', $this->getPath());
}
/**
* @return string
*/
@ -217,14 +246,12 @@ class DocumentationPage extends ViewableData {
// find the key/value pairs
$intPattern = '/(?<key>[A-Za-z][A-Za-z0-9_-]+)[\t]*:[\t]*(?<value>[^:\n\r\/]+)/x';
$matches = preg_match_all($intPattern, $block[1], $meta);
foreach($meta['key'] as $index => $key) {
if(isset($meta['value'][$index])) {
// check if a property exists for this key
if (property_exists(get_class(), $key)) {
$this->$key = $meta['value'][$index];
$metaDataFound = true;
}
}

View File

@ -67,8 +67,6 @@ class RebuildLuceneDocsIndex extends BuildTask {
// iconv complains about all the markdown formatting
// turn off notices while we parse
$error = error_reporting();
error_reporting('E_ALL ^ E_NOTICE');
if(!Director::is_cli()) {
echo "<ul>";
@ -76,16 +74,19 @@ class RebuildLuceneDocsIndex extends BuildTask {
foreach($pages as $url => $record) {
$count++;
$page = $manifest->getPage($url);
$doc = new Zend_Search_Lucene_Document();
$error = error_reporting();
error_reporting(E_ALL ^ E_NOTICE);
$content = $page->getHTML();
error_reporting($error);
$doc->addField(Zend_Search_Lucene_Field::Text('content', $content));
$doc->addField($titleField = Zend_Search_Lucene_Field::Text('Title', $page->getTitle()));
$doc->addField($breadcrumbField = Zend_Search_Lucene_Field::Text('BreadcrumbTitle', $page->getBreadcrumbTitle()));
$doc->addField(Zend_Search_Lucene_Field::Keyword(
'Version', $page->getEntity()->getVersion()->getVersion()
'Version', $page->getEntity()->getVersion()
));
$doc->addField(Zend_Search_Lucene_Field::Keyword(
@ -93,13 +94,13 @@ class RebuildLuceneDocsIndex extends BuildTask {
));
$doc->addField(Zend_Search_Lucene_Field::Keyword(
'Entity', $entity
'Entity', $page->getEntity()
));
$doc->addField(Zend_Search_Lucene_Field::Keyword(
'Link', $page->Link()
));
// custom boosts
$titleField->boost = 3;
$breadcrumbField->boost = 1.5;
@ -107,20 +108,16 @@ class RebuildLuceneDocsIndex extends BuildTask {
$boost = Config::inst()->get('DocumentationSearch', 'boost_by_path');
foreach($boost as $pathExpr => $boost) {
if(preg_match($pathExpr, $page->getRelativeLink())) {
if(preg_match($pathExpr, $page->getRelativePath())) {
$doc->boost = $boost;
}
}
$index->addDocument($doc);
if(!$quiet) {
if(Director::is_cli()) echo " * adding ". $page->getPath() ."\n";
else echo "<li>adding ". $page->getPath() ."</li>\n";
}
}
error_reporting($error);
}
$index->commit();

View File

@ -1,3 +1,35 @@
fieldset {
border: none;
margin: 0;
padding: 0 0 5px 0;
}
#DocumentationAdvancedSearchForm_AdvancedSearchForm_q {
width: 100%;
font-size: 15px;
padding: 10px 10px;
}
#DocumentationAdvancedSearchForm_AdvancedSearchForm #q {
width: 55%;
padding-right: 2%;
float: left;
}
#DocumentationAdvancedSearchForm_AdvancedSearchForm #Entities {
width: 25%;
padding-right: 2%;
float: left;
}
#DocumentationAdvancedSearchForm_AdvancedSearchForm #Versions {
width: 20%;
float: right;
}
.optionset ul {
margin: 0;
}
.optionset li {
list-style: none;
}

View File

@ -28,6 +28,27 @@ html {
border-radius: 3px;
}
.introduction {
margin: -30px -30px 30px;
padding: 30px;
background: rgb(3, 91, 136);
}
.introduction h1 {
color: #fff;
margin-bottom: 20px;
}
.introduction p {
font-size: 16px;
line-height: 22px;
margin-bottom: 10px;
max-width: 80%;
text-shadow: 0 -1px rgba(0, 0, 0, 0.1);
color: #fff;
color: rgba(255, 255, 255, 0.8);
}
.no-box {
padding: 15px;
}
@ -169,7 +190,9 @@ html {
float: left;
}
#breadcrumbs p {
font-size: 11px; margin: 0; color: #798D85;
font-size: 11px;
margin: 0 0 5px 0;
color: #798D85;
}
@ -429,3 +452,55 @@ html {
.doc-breadcrumbs p a {
color: #999;
}
.well {
min-height: 20px;
padding: 19px;
margin-bottom: 40px;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
box-shadow: inset 0 1px 1px rgba(0,0,0,.05);
}
.well h4 {
margin-top: 0;
}
.result {
}
.result h2 {
margin-bottom: 0;
}
.documentation_children {
overflow: hidden;
padding-bottom: 20px;
}
.documentation_children ul {
margin: 0;
}
.documentation_children h3 {
font-size: 16px;
margin-bottom: 0 0 2px 0;
}
.documentation_children li {
float: left;
width: 33%;
margin: 0;
padding: 0 3% 0 0;
list-style: none;
}
.documentation_children li:nth-child(3n+1) {
clear: both;
}
.documentation_children p {
font-size: 13px;
opacity: 0.9;
}

View File

@ -98,19 +98,7 @@
toc += '</ul></div>';
// Table of content location
var title = $('#content h1:first');
if (title.length > 0) {
title.after(toc);
} else {
var breadcrums = $('#content .doc-breadcrumbs');
if (breadcrums.length > 0) {
breadcrums.after(toc);
} else {
$('#table-contents-holder').prepend(toc);
}
}
$('#table-contents-holder').prepend(toc);
// Toggle the TOC
$('#table-of-contents').attr('href', 'javascript:void()').toggle(

View File

@ -0,0 +1,12 @@
<% if Children %>
<div class="documentation_children">
<ul>
<% loop Children %>
<li>
<h3><a href="$Link">$Title</a></h3>
<% if Summary %><p>$Summary</p><% end_if %>
</li>
<% end_loop %>
</ul>
</div>
<% end_if %>

View File

@ -0,0 +1,12 @@
<% if Children %>
<div class="documentation_children">
<ul>
<% loop Children %>
<li>
<h3><a href="$Link">$Title</a></h3>
<% if Summary %><p>$Summary</p><% end_if %>
</li>
<% end_loop %>
</ul>
</div>
<% end_if %>

View File

@ -3,10 +3,12 @@
$DocumentationSearchForm
<ul class="nav">
<li><a href="$BaseHref" class="top">Home</a></li>
<% loop Menu %>
<% if DefaultEntity %>
<% loop Children %>
<li class="$LinkingMode $FirstLast">
<li class="$LinkingMode <% if Last %>last<% end_if %>">
<a href="$Link" class="top">$Title</a>
<% if LinkingMode == current %>
@ -20,7 +22,7 @@
</li>
<% end_loop %>
<% else %>
<li class="$LinkingMode $FirstLast"><a href="$Link" class="top">$Title <% if IsFolder %><span class="is-folder">&#9658;</span><% end_if %></a>
<li class="$LinkingMode <% if Last %>last<% end_if %>"><a href="$Link" class="top">$Title <% if IsFolder %><span class="is-folder">&#9658;</span><% end_if %></a>
<% if LinkingMode == current %>
<% if Children %>
<ul class="$FirstLast">

View File

@ -1,24 +1,30 @@
<div class="box">
<% if SearchQuery %>
$SearchResults
<% if Introduction %>
<div class="introduction">
<h1>$Title</h1>
<% if Introduction %>
<p>$Introduction</p>
<% end_if %>
</div>
<% else %>
<% include DocumentationVersions %>
<% 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 %>
<% end_if %>
<% include DocumentationVersions %>
<% if VersionWarning %>
<% include DocumentationVersion_warning %>
<% end_if %>
<% include DocumentationTableContents %>
<% loop Children %>
<ul>
<li><a href="$Link">$Title</a></li>
</ul>
<% end_loop %>
<% include DocumentationNextPrevious %>
</div>

View File

@ -1,4 +1,16 @@
<div id="documentation-page" class="box">
<% if Page.Introduction %>
<% with Page %>
<div class="introduction">
<h1>$Title</h1>
<% if Introduction %>
<p>$Introduction</p>
<% end_if %>
</div>
<% end_with %>
<% end_if %>
<% include DocumentationVersions %>
<% if VersionWarning %>
@ -9,6 +21,7 @@
<% include DocumentationBreadcrumbs %>
<% end_if %>
<% include DocumentationTableContents %>
$Content

View File

@ -0,0 +1,3 @@
<div class="box">
$SearchResults
</div>

View File

@ -1,16 +1,19 @@
<p>Your search for <strong>&quot;$SearchQuery.XML&quot;</strong> found $TotalResults result<% if TotalResults != 1 %>s<% end_if %>.</p>
<% if AdvancedSearchEnabled %>
<h4><% _t('ADVANCEDSEARCH', 'Advanced Search') %></h4>
$AdvancedSearchForm
<div class="well">
$AdvancedSearchForm
</div>
<% end_if %>
<% if Results %>
<p>Showing page $ThisPage of $TotalPages</p>
<p class="intro">Your search for <strong>&quot;$SearchQuery.XML&quot;</strong> found $TotalResults result<% if TotalResults != 1 %>s<% end_if %>. 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>
<div class="result">
<h2><a href="$Link">$Title</a></h2>
<p><small>$BreadcrumbTitle</small></p>
<p>$Content.LimitCharacters(200)</p>
</div>
<% end_loop %>
<% if SearchPages %>

View File

@ -114,6 +114,10 @@ class DocumentationManifestTests extends SapphireTest {
}
public function testGetAllEntityVersions() {
}
public function testGetStableVersion() {
}