the browser-facing portion of osu!
at master 119 lines 3.4 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace App\Libraries\Wiki; 7 8use App\Libraries\Markdown\OsuMarkdown; 9use League\CommonMark\Environment\Environment; 10use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; 11use League\CommonMark\Extension\CommonMark\Node\Block\Heading; 12use League\CommonMark\Extension\CommonMark\Node\Inline\AbstractWebResource; 13use League\CommonMark\Extension\CommonMark\Node\Inline\Link; 14use League\CommonMark\Node\Block\Document; 15use League\CommonMark\Node\Block\Paragraph; 16use League\CommonMark\Node\Node; 17use League\CommonMark\Node\NodeWalkerEvent; 18use League\CommonMark\Parser\MarkdownParser; 19use League\CommonMark\Renderer\HtmlRenderer; 20 21class MainPageRenderer extends Renderer 22{ 23 private MarkdownParser $parser; 24 25 private HtmlRenderer $renderer; 26 27 public function __construct($page, $body) 28 { 29 parent::__construct($page, $body); 30 31 $config = array_merge( 32 OsuMarkdown::DEFAULT_COMMONMARK_CONFIG, 33 ['html_input' => 'allow'], 34 ); 35 36 $env = new Environment($config); 37 $env->addExtension(new CommonMarkCoreExtension()); 38 39 $this->parser = new MarkdownParser($env); 40 $this->renderer = new HtmlRenderer($env); 41 } 42 43 /** 44 * {@inheritdoc} 45 */ 46 public function render() 47 { 48 $body = OsuMarkdown::parseYamlHeader($this->body); 49 $document = $this->parser->parse($body['document']); 50 51 $this->addClasses($document); 52 53 $page = [ 54 'header' => $body['header'], 55 'output' => $this->renderer->renderDocument($document)->getContent(), 56 ]; 57 58 return $page; 59 } 60 61 /** 62 * {@inheritdoc} 63 */ 64 public function renderIndexable() 65 { 66 // returning nothing since the main page isn't searchable anyway 67 return ''; 68 } 69 70 /** 71 * @param \League\CommonMark\Block\Element\Document $document 72 * @return void 73 */ 74 private function addClasses(Document $document) 75 { 76 $walker = $document->walker(); 77 78 while ($event = $walker->next()) { 79 $node = $event->getNode(); 80 81 $this->fixLinks($event, $node); 82 83 if ($event->isEntering() || isset($node->data['attributes']['class'])) { 84 continue; 85 } 86 87 $blockClass = 'wiki-main-page'; 88 $class = ''; 89 90 switch (get_class($node)) { 91 case Heading::class: 92 $class = "{$blockClass}__heading"; 93 break; 94 case Paragraph::class: 95 $class = "{$blockClass}__paragraph"; 96 break; 97 case Link::class: 98 $class = "{$blockClass}__link"; 99 break; 100 } 101 102 if (present($class)) { 103 $node->data->set('attributes/class', $class); 104 } 105 } 106 } 107 108 private function fixLinks(NodeWalkerEvent $event, Node $node) 109 { 110 if (!$event->isEntering() || !($node instanceof AbstractWebResource)) { 111 return; 112 } 113 114 // this assumes links are in form /wiki/Path/To/Page 115 $relativeUrl = preg_replace('#^/wiki/#', './', $node->getUrl()); 116 117 $node->setUrl($relativeUrl); 118 } 119}