1<?php 2if (!defined('DOKU_INC')) die(); 3 4/** 5 * Optional syntax component: renders <frontmatter> and <skillmeta> blocks as 6 * readable page metadata with an optional ZIP download action. 7 */ 8class syntax_plugin_skillforge extends DokuWiki_Syntax_Plugin { 9 public function getType() { return 'substition'; } 10 public function getSort() { return 32; } 11 public function connectTo($mode) { 12 $this->Lexer->addSpecialPattern('<frontmatter>.*?</frontmatter>', $mode, 'plugin_skillforge'); 13 $this->Lexer->addSpecialPattern('<skillmeta>.*?</skillmeta>', $mode, 'plugin_skillforge'); 14 } 15 16 public function handle($match, $state, $pos, Doku_Handler $handler) { 17 if (preg_match('/^<(frontmatter|skillmeta)>(.*?)<\/\1>$/is', $match, $m)) { 18 return array('metadata' => trim($m[2])); 19 } 20 return array('metadata' => ''); 21 } 22 23 public function render($mode, Doku_Renderer $renderer, $data) { 24 if ($mode !== 'xhtml') return true; 25 26 $showMetadata = (bool)$this->getConf('show_rendered_metadata'); 27 $showDownload = (bool)$this->getConf('show_page_download_button'); 28 if (!$showMetadata && !$showDownload) return true; 29 30 $content = ''; 31 if ($showMetadata) { 32 $content .= $this->renderMetadata($data['metadata']); 33 } 34 if ($showDownload && auth_isadmin()) { 35 $content .= $this->renderDownloadButton($data['metadata']); 36 } 37 if ($content === '') return true; 38 39 $renderer->doc .= '<div class="skillforge-page-tools">' . $content . '</div>'; 40 return true; 41 } 42 43 private function renderMetadata($metadata) { 44 $fields = $this->parseMetadata($metadata); 45 if (!$fields) return ''; 46 47 $html = '<section class="skillforge-metadata"><h2>Skill metadata</h2><dl>'; 48 foreach ($fields as $name => $value) { 49 $html .= '<dt>' . hsc($name) . '</dt><dd>'; 50 if (is_array($value)) { 51 $html .= '<ul>'; 52 foreach ($value as $item) { 53 $html .= '<li>' . hsc($item) . '</li>'; 54 } 55 $html .= '</ul>'; 56 } else { 57 $html .= hsc($value); 58 } 59 $html .= '</dd>'; 60 } 61 return $html . '</dl></section>'; 62 } 63 64 private function renderDownloadButton($metadata) { 65 global $ID; 66 $url = wl($ID, array( 67 'do' => 'skillforge_download_current', 68 'sf_page' => $ID, 69 'sectok' => getSecurityToken() 70 ), false, '&'); 71 $label = $this->downloadButtonLabel($metadata); 72 73 return '<p class="skillforge-download"><a class="button" href="' . hsc($url) . '">' . hsc($label) . '</a></p>'; 74 } 75 76 private function downloadButtonLabel($metadata) { 77 $label = trim((string)$this->getConf('download_button_label')); 78 if ($label === '') $label = 'Download SKILL.md (.zip)'; 79 80 $fields = $this->parseMetadata($metadata); 81 $replacements = array( 82 '{name}' => isset($fields['name']) && !is_array($fields['name']) ? $fields['name'] : '', 83 '{title}' => isset($fields['title']) && !is_array($fields['title']) ? $fields['title'] : '', 84 '{description}' => isset($fields['description']) && !is_array($fields['description']) ? $fields['description'] : '', 85 '{output}' => $this->getConf('output_skill_filename') ?: 'SKILL.md', 86 ); 87 88 $label = str_replace(array_keys($replacements), array_values($replacements), $label); 89 $label = preg_replace('/\s+/', ' ', trim($label)); 90 return $label !== '' ? $label : 'Download SKILL.md (.zip)'; 91 } 92 93 private function parseMetadata($metadata) { 94 $fields = array(); 95 $current = null; 96 foreach (preg_split('/\R/', (string)$metadata) as $line) { 97 $line = rtrim($line); 98 if ($line === '' || preg_match('/^\s*#/', $line)) continue; 99 100 if ($current !== null && preg_match('/^\s*-\s*(.+)$/', $line, $m)) { 101 if (!isset($fields[$current]) || !is_array($fields[$current])) $fields[$current] = array(); 102 $fields[$current][] = trim($m[1], " \t\"'"); 103 continue; 104 } 105 106 if (preg_match('/^([A-Za-z0-9_.-]+):\s*(.*)$/', $line, $m)) { 107 $current = $m[1]; 108 $value = trim($m[2], " \t\"'"); 109 $fields[$current] = ($value === '') ? array() : $value; 110 } 111 } 112 return $fields; 113 } 114} 115