├── .editorconfig ├── README.md ├── _config.php ├── _config ├── docsviewer.yml └── routes.yml ├── code-of-conduct.md ├── code ├── DocumentationHelper.php ├── DocumentationManifest.php ├── DocumentationManifestFileFinder.php ├── DocumentationParser.php ├── DocumentationPermalinks.php ├── DocumentationSearch.php ├── controllers │ ├── DocumentationOpenSearchController.php │ └── DocumentationViewer.php ├── extensions │ ├── DocumentationSearchExtension.php │ ├── DocumentationStaticPublisherExtension.php │ └── DocumentationViewerVersionWarning.php ├── forms │ ├── DocumentationAdvancedSearchForm.php │ └── DocumentationSearchForm.php ├── models │ ├── DocumentationEntity.php │ ├── DocumentationFolder.php │ └── DocumentationPage.php └── tasks │ ├── CheckDocsSourcesTask.php │ ├── DocumentationBuild.php │ └── RebuildLuceneDocsIndex.php ├── composer.json ├── css ├── forms.css ├── highlight.css ├── layout.css ├── normalize.css ├── shSilverStripeDocs.css ├── small.css ├── typography.css └── utilities.css ├── images ├── bullet.gif ├── error_button.png ├── external_link.png ├── ico_close_off.png ├── icons │ ├── application.png │ ├── page_excel.png │ ├── page_white_acrobat.png │ ├── page_white_zip.png │ └── page_word.png ├── info_button.png ├── lightbulb.png ├── logo.jpg ├── menu.png ├── note.gif ├── notification.png ├── quote.gif ├── readme.png ├── search.png └── warning.png ├── javascript └── DocumentationViewer.js ├── lang ├── _manifest_exclude ├── en.yml ├── en_US.php ├── hr.yml └── hr_HR.yml ├── license.md ├── templates ├── DocumentationPages.ss ├── DocumentationViewer.ss ├── DocumentationViewer_all.ss ├── DocumentationViewer_results.ss ├── Includes │ ├── DocumentationBreadcrumbs.ss │ ├── DocumentationComments.ss │ ├── DocumentationEditLink.ss │ ├── DocumentationEnd.ss │ ├── DocumentationFooter.ss │ ├── DocumentationGA.ss │ ├── DocumentationHead.ss │ ├── DocumentationNextPrevious.ss │ ├── DocumentationPages.ss │ ├── DocumentationSidebar.ss │ ├── DocumentationTableContents.ss │ ├── DocumentationVersion_warning.ss │ └── DocumentationVersions.ss ├── Layout │ ├── DocumentationViewer_DocumentationFolder.ss │ ├── DocumentationViewer_DocumentationPage.ss │ ├── DocumentationViewer_all.ss │ ├── DocumentationViewer_error.ss │ ├── DocumentationViewer_results.ss │ └── DocumentationViewer_search.ss ├── OpenSearchDescription.ss └── OpenSearchResults.ss └── thirdparty ├── Zend └── Search │ ├── Exception.php │ ├── Lucene.php │ └── Lucene │ ├── Analysis │ ├── Analyzer.php │ ├── Analyzer │ │ ├── Common.php │ │ └── Common │ │ │ ├── Text.php │ │ │ ├── Text │ │ │ └── CaseInsensitive.php │ │ │ ├── TextNum.php │ │ │ ├── TextNum │ │ │ └── CaseInsensitive.php │ │ │ ├── Utf8.php │ │ │ ├── Utf8 │ │ │ └── CaseInsensitive.php │ │ │ ├── Utf8Num.php │ │ │ └── Utf8Num │ │ │ └── CaseInsensitive.php │ ├── Token.php │ ├── TokenFilter.php │ └── TokenFilter │ │ ├── LowerCase.php │ │ ├── LowerCaseUtf8.php │ │ ├── ShortWords.php │ │ └── StopWords.php │ ├── Document.php │ ├── Document │ ├── Docx.php │ ├── Exception.php │ ├── Html.php │ ├── OpenXml.php │ ├── Pptx.php │ └── Xlsx.php │ ├── Exception.php │ ├── FSM.php │ ├── FSMAction.php │ ├── Field.php │ ├── Index │ ├── DictionaryLoader.php │ ├── DocsFilter.php │ ├── FieldInfo.php │ ├── SegmentInfo.php │ ├── SegmentMerger.php │ ├── SegmentWriter.php │ ├── SegmentWriter │ │ ├── DocumentWriter.php │ │ └── StreamWriter.php │ ├── Term.php │ ├── TermInfo.php │ ├── TermsPriorityQueue.php │ ├── TermsStream │ │ └── Interface.php │ └── Writer.php │ ├── Interface.php │ ├── LockManager.php │ ├── MultiSearcher.php │ ├── PriorityQueue.php │ ├── Proxy.php │ ├── Search │ ├── BooleanExpressionRecognizer.php │ ├── Highlighter │ │ ├── Default.php │ │ └── Interface.php │ ├── Query.php │ ├── Query │ │ ├── Boolean.php │ │ ├── Empty.php │ │ ├── Fuzzy.php │ │ ├── Insignificant.php │ │ ├── MultiTerm.php │ │ ├── Phrase.php │ │ ├── Preprocessing.php │ │ ├── Preprocessing │ │ │ ├── Fuzzy.php │ │ │ ├── Phrase.php │ │ │ └── Term.php │ │ ├── Range.php │ │ ├── Term.php │ │ └── Wildcard.php │ ├── QueryEntry.php │ ├── QueryEntry │ │ ├── Phrase.php │ │ ├── Subquery.php │ │ └── Term.php │ ├── QueryHit.php │ ├── QueryLexer.php │ ├── QueryParser.php │ ├── QueryParserContext.php │ ├── QueryParserException.php │ ├── QueryToken.php │ ├── Similarity.php │ ├── Similarity │ │ └── Default.php │ ├── Weight.php │ └── Weight │ │ ├── Boolean.php │ │ ├── Empty.php │ │ ├── MultiTerm.php │ │ ├── Phrase.php │ │ └── Term.php │ ├── Storage │ ├── Directory.php │ ├── Directory │ │ └── Filesystem.php │ ├── File.php │ └── File │ │ ├── Filesystem.php │ │ └── Memory.php │ └── TermStreamsPriorityQueue.php ├── _manifest_exclude └── highlight ├── CHANGES.md ├── LICENSE ├── README.md ├── README.ru.md └── highlight.pack.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in this file, 2 | # please see the EditorConfig documentation: 3 | # http://editorconfig.org 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [{*.yml,package.json}] 14 | indent_size = 2 15 | 16 | # The indent size used in the package.json file cannot be changed: 17 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Documentation Viewer Module 2 | 3 | ## ABANDONED 4 | 5 | This package is abandoned. See [silverstripe/doc.silverstripe.org](https://github.com/silverstripe/doc.silverstripe.org) instead. 6 | 7 | [![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-docsviewer.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-docsviewer) 8 | 9 | ## Maintainer Contact 10 | 11 | * Will Rossiter (Nickname: willr, wrossiter) 12 | 13 | 14 | ## Requirements 15 | 16 | These are pulled in via Composer. 17 | 18 | * SilverStripe 3.1 19 | * [Parsedown](http://parsedown.org/) and Parsedown Extra. 20 | 21 | ## Summary 22 | 23 | Reads markdown files from a given list of folders from your installation and 24 | provides a web interface for viewing the documentation. Ideal for providing 25 | documentation alongside your module or project code. 26 | 27 | A variation of this module powers the main SilverStripe developer documentation 28 | and the user help websites. 29 | 30 | For more documentation on how to use the module please read /docs/Writing-Documentation.md 31 | (or via this in /dev/docs/docsviewer/Writing-Documentation in your webbrowser) 32 | 33 | ## Installation 34 | 35 | composer require "silverstripe/docsviewer" "dev-master" 36 | 37 | ## Usage 38 | 39 | After installing the files via composer, rebuild the SilverStripe database.. 40 | 41 | sake dev/build 42 | 43 | Then start by viewing the documentation at `yoursite.com/dev/docs`. 44 | 45 | If something isn't working, you can run the dev task at `yoursite.com/dev/tasks/CheckDocsSourcesTask` 46 | to automatically check for configuration or source file errors. 47 | 48 | Out of the box the module will display the documentation files that have been 49 | bundled into any of your installed modules. To configure what is shown in the 50 | documentation viewer see the detailed [documentation](docs/en/configuration.md). 51 | 52 | For more information about how to use the module see each of the documentation 53 | 54 | * [Configuration](docs/en/configuration.md) 55 | * [Markdown Syntax](docs/en/markdown.md) 56 | * [Syntax Highlighting](docs/en/syntax-highlighting.md) 57 | * [Publishing Static Files](docs/en/statichtml.md) 58 | 59 | ## License 60 | 61 | See LICENSE 62 | -------------------------------------------------------------------------------- /_config.php: -------------------------------------------------------------------------------- 1 | '/\.(md|markdown)$/i', 18 | 'file_callback' => null, 19 | 'dir_callback' => null, 20 | 'ignore_vcs' => true 21 | ); 22 | 23 | /** 24 | * 25 | */ 26 | public function acceptDir($basename, $pathname, $depth) 27 | { 28 | $ignored = Config::inst()->get('DocumentationManifestFileFinder', 'ignored_files'); 29 | 30 | if ($ignored) { 31 | if (in_array(strtolower($basename), $ignored)) { 32 | return false; 33 | } 34 | } 35 | 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /code/DocumentationPermalinks.php: -------------------------------------------------------------------------------- 1 | 23 | * DocumentationPermalinks::add(array( 24 | * 'debugging' => 'current/en/sapphire/topics/debugging' 25 | * )); 26 | * 27 | * 28 | * Do not need to include the language or the version current as it 29 | * will add it based off the language or version in the session 30 | * 31 | * @param array 32 | */ 33 | public static function add($map = array()) 34 | { 35 | if (ArrayLib::is_associative($map)) { 36 | self::$mapping = array_merge(self::$mapping, $map); 37 | } else { 38 | user_error("DocumentationPermalinks::add() requires an associative array", E_USER_ERROR); 39 | } 40 | } 41 | 42 | /** 43 | * Return the location for a given short value. 44 | * 45 | * @return string|false 46 | */ 47 | public static function map($url) 48 | { 49 | return (isset(self::$mapping[$url])) ? self::$mapping[$url] : false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /code/controllers/DocumentationOpenSearchController.php: -------------------------------------------------------------------------------- 1 | httpError(404); 19 | } 20 | 21 | public function description() 22 | { 23 | $viewer = new DocumentationViewer(); 24 | 25 | if (!$viewer->canView()) { 26 | return Security::permissionFailure($this); 27 | } 28 | 29 | if (!Config::inst()->get('DocumentationSearch', 'enabled')) { 30 | return $this->httpError('404'); 31 | } 32 | 33 | $data = DocumentationSearch::get_meta_data(); 34 | $link = Director::absoluteBaseUrl() . 35 | $data['SearchPageLink'] = Controller::join_links( 36 | $viewer->Link(), 37 | 'results/?Search={searchTerms}&start={startIndex}&length={count}&action_results=1' 38 | ); 39 | 40 | $data['SearchPageAtom'] = $data['SearchPageLink'] . '&format=atom'; 41 | 42 | return $this->customise( 43 | new ArrayData($data) 44 | )->renderWith( 45 | array( 46 | 'OpenSearchDescription' 47 | ) 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /code/extensions/DocumentationSearchExtension.php: -------------------------------------------------------------------------------- 1 | getSearchQuery(); 68 | 69 | $search = new DocumentationSearch(); 70 | $search->setQuery($query); 71 | $search->setVersions($this->getSearchedVersions()); 72 | $search->setModules($this->getSearchedEntities()); 73 | $search->setOutputController($this->owner); 74 | 75 | return $search->renderResults(); 76 | } 77 | 78 | /** 79 | * Returns an search form which allows people to express more complex rules 80 | * and options than the plain search form. 81 | * 82 | * @return Form 83 | */ 84 | public function AdvancedSearchForm() 85 | { 86 | return new DocumentationAdvancedSearchForm($this->owner); 87 | } 88 | 89 | /** 90 | * @return bool 91 | */ 92 | public function getAdvancedSearchEnabled() 93 | { 94 | return Config::inst()->get("DocumentationSearch", 'advanced_search_enabled'); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /code/extensions/DocumentationStaticPublisherExtension.php: -------------------------------------------------------------------------------- 1 | 11 | * StaticExporter: 12 | * extensions: 13 | * - DocumentationStaticPublisherExtension 14 | * 15 | * 16 | * If you don't plan on using static publisher for anything else and you have 17 | * the cms module installed, make sure you disable that from being published. 18 | * 19 | * Again, in your applications config.yml file 20 | * 21 | * 22 | * StaticExporter: 23 | * disable_sitetree_export: true 24 | * 25 | * 26 | * @package docsviewer 27 | */ 28 | class DocumentationStaticPublisherExtension extends Extension 29 | { 30 | public function alterExportUrls(&$urls) 31 | { 32 | $manifest = new DocumentationManifest(true); 33 | 34 | foreach ($manifest->getPages() as $url => $page) { 35 | $urls[$url] = $url; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /code/extensions/DocumentationViewerVersionWarning.php: -------------------------------------------------------------------------------- 1 | owner->getPage(); 16 | 17 | if (!$page) { 18 | return false; 19 | } 20 | 21 | $entity = $page->getEntity(); 22 | 23 | if (!$entity) { 24 | return false; 25 | } 26 | 27 | $versions = $this->owner->getManifest()->getAllVersionsOfEntity($entity); 28 | 29 | if ($entity->getIsStable()) { 30 | return false; 31 | } 32 | 33 | $stable = $this->owner->getManifest()->getStableVersion($entity); 34 | $compare = $entity->compare($stable); 35 | 36 | if ($entity->getVersion() == 'master' || $compare > 0) { 37 | return $this->owner->customise( 38 | new ArrayData( 39 | array( 40 | 'FutureRelease' => true, 41 | 'StableVersion' => DBField::create_field('HTMLText', $stable->getVersion()) 42 | ) 43 | ) 44 | ); 45 | } else { 46 | return $this->owner->customise( 47 | new ArrayData( 48 | array( 49 | 'OutdatedRelease' => true, 50 | 'StableVersion' => DBField::create_field('HTMLText', $stable->getVersion()) 51 | ) 52 | ) 53 | ); 54 | } 55 | 56 | return false; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /code/forms/DocumentationAdvancedSearchForm.php: -------------------------------------------------------------------------------- 1 | getManifest()->getAllVersions(); 11 | $entities = $controller->getManifest()->getEntities(); 12 | 13 | $q = ($q = $controller->getSearchQuery()) ? $q->NoHTML() : ""; 14 | 15 | // klude to take an array of objects down to a simple map 16 | $entities = $entities->map('Key', 'Title'); 17 | 18 | // if we haven't gone any search limit then we're searching everything 19 | $searchedEntities = $controller->getSearchedEntities(); 20 | 21 | if (count($searchedEntities) < 1) { 22 | $searchedEntities = $entities; 23 | } 24 | 25 | $searchedVersions = $controller->getSearchedVersions(); 26 | 27 | if (count($searchedVersions) < 1) { 28 | $searchedVersions = $versions; 29 | } 30 | 31 | $fields = FieldList::create( 32 | TextField::create('q', _t('DocumentationViewer.KEYWORDS', 'Keywords'), $q), 33 | //CheckboxSetField::create('Entities', _t('DocumentationViewer.MODULES', 'Modules'), $entities, $searchedEntities), 34 | CheckboxSetField::create( 35 | 'Versions', 36 | _t('DocumentationViewer.VERSIONS', 'Versions'), 37 | $versions, 38 | $searchedVersions 39 | ) 40 | ); 41 | 42 | $actions = FieldList::create( 43 | FormAction::create('results', _t('DocumentationViewer.SEARCH', 'Search')) 44 | ); 45 | 46 | $required = RequiredFields::create(array('Search')); 47 | 48 | parent::__construct( 49 | $controller, 50 | 'AdvancedSearchForm', 51 | $fields, 52 | $actions, 53 | $required 54 | ); 55 | 56 | $this->disableSecurityToken(); 57 | $this->setFormMethod('GET'); 58 | $this->setFormAction($controller->Link('results')); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /code/forms/DocumentationSearchForm.php: -------------------------------------------------------------------------------- 1 | setAttribute('placeholder', _t('DocumentationViewer.SEARCH', 'Search')) 10 | ); 11 | 12 | $page = $controller->getPage(); 13 | 14 | if ($page) { 15 | $versions = HiddenField::create( 16 | 'Versions', 17 | _t('DocumentationViewer.VERSIONS', 'Versions'), 18 | $page->getEntity()->getVersion() 19 | ); 20 | 21 | $fields->push($versions); 22 | } 23 | 24 | $actions = new FieldList( 25 | new FormAction('results', _t('DocumentationViewer.SEARCH', 'Search')) 26 | ); 27 | 28 | parent::__construct($controller, 'DocumentationSearchForm', $fields, $actions); 29 | 30 | $this->disableSecurityToken(); 31 | $this->setFormMethod('GET'); 32 | $this->setFormAction($controller->Link('results')); 33 | 34 | $this->addExtraClass('search'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /code/models/DocumentationFolder.php: -------------------------------------------------------------------------------- 1 | getTitleFromFolder(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /code/tasks/CheckDocsSourcesTask.php: -------------------------------------------------------------------------------- 1 | "; 17 | } 18 | } 19 | 20 | public function end() 21 | { 22 | if (Director::is_cli()) { 23 | echo "\nTotal errors: {$this->errors}\n"; 24 | } else { 25 | echo ""; 26 | echo "

Total errors: {$this->errors}

"; 27 | } 28 | } 29 | 30 | public function showError($error) 31 | { 32 | $this->errors++; 33 | if (Director::is_cli()) { 34 | echo "\n$error"; 35 | } else { 36 | echo "
  • " . Convert::raw2xml($error) . "
  • "; 37 | } 38 | } 39 | 40 | /** 41 | * Validate all source files 42 | * 43 | * @param SS_HTTPRequest $request 44 | * @throws Exception 45 | */ 46 | public function run($request) 47 | { 48 | $this->start(); 49 | $registered = Config::inst()->get('DocumentationManifest', 'register_entities'); 50 | foreach ($registered as $details) { 51 | // validate the details provided through the YAML configuration 52 | $required = array('Path', 'Title'); 53 | 54 | // Check required configs 55 | foreach ($required as $require) { 56 | if (!isset($details[$require])) { 57 | $this->showError("$require is a required key in DocumentationManifest.register_entities"); 58 | } 59 | } 60 | 61 | // Check path is loaded 62 | $path = $this->getRealPath($details['Path']); 63 | if (!$path || !is_dir($path)) { 64 | $this->showError($details['Path'] . ' is not a valid documentation directory'); 65 | } 66 | } 67 | $this->end(); 68 | } 69 | 70 | public function getRealPath($path) 71 | { 72 | if (!Director::is_absolute($path)) { 73 | $path = Controller::join_links(BASE_PATH, $path); 74 | } 75 | 76 | return $path; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /code/tasks/DocumentationBuild.php: -------------------------------------------------------------------------------- 1 | "; 9 | print_r($manifest->getPages()); 10 | echo ""; 11 | die(); 12 | ; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /code/tasks/RebuildLuceneDocsIndex.php: -------------------------------------------------------------------------------- 1 | rebuildIndexes(); 22 | } 23 | 24 | public function rebuildIndexes($quiet = false) 25 | { 26 | include_once 'Zend/Search/Lucene.php'; 27 | 28 | ini_set("memory_limit", -1); 29 | ini_set('max_execution_time', 0); 30 | 31 | Filesystem::makeFolder(DocumentationSearch::get_index_location()); 32 | 33 | // only rebuild the index if we have to. Check for either flush or the time write.lock.file 34 | // was last altered 35 | $lock = DocumentationSearch::get_index_location() .'/write.lock.file'; 36 | $lockFileFresh = (file_exists($lock) && filemtime($lock) > (time() - (60 * 60 * 24))); 37 | 38 | echo "Building index in ". DocumentationSearch::get_index_location() . PHP_EOL; 39 | 40 | if ($lockFileFresh && !isset($_REQUEST['flush'])) { 41 | if (!$quiet) { 42 | echo "Index recently rebuilt. If you want to force reindex use ?flush=1"; 43 | } 44 | 45 | return true; 46 | } 47 | 48 | try { 49 | $index = Zend_Search_Lucene::open(DocumentationSearch::get_index_location()); 50 | $index->removeReference(); 51 | } catch (Zend_Search_Lucene_Exception $e) { 52 | user_error($e); 53 | } 54 | 55 | try { 56 | $index = Zend_Search_Lucene::create(DocumentationSearch::get_index_location()); 57 | } catch (Zend_Search_Lucene_Exception $c) { 58 | user_error($c); 59 | } 60 | 61 | // includes registration 62 | $manifest = new DocumentationManifest(true); 63 | $pages = $manifest->getPages(); 64 | 65 | if ($pages) { 66 | $count = 0; 67 | 68 | // iconv complains about all the markdown formatting 69 | // turn off notices while we parse 70 | 71 | if (!Director::is_cli()) { 72 | echo "