1<?php 2if (!defined('DOKU_INC')) die(); 3 4class admin_plugin_skillforge extends DokuWiki_Admin_Plugin { 5 private $message = ''; 6 private $download = ''; 7 8 public function forAdminOnly() { return true; } 9 public function getMenuSort() { return 250; } 10 11 public function handle() { 12 if ($_SERVER['REQUEST_METHOD'] === 'POST' && checkSecurityToken()) { 13 try { 14 /** @var helper_plugin_skillforge $helper */ 15 $helper = plugin_load('helper', 'skillforge'); 16 $result = $helper->exportNamespace( 17 $_POST['sf_namespace'], 18 null, 19 array( 20 'recursive' => !empty($_POST['sf_recursive']), 21 'include_media' => !empty($_POST['sf_include_media']) 22 ) 23 ); 24 $this->message = 'Export created: ' . hsc($result['name']) . ' (' . (int)$result['count'] . ' pages).'; 25 $this->download = $result['name']; 26 } catch (Exception $e) { 27 $this->message = 'SkillForge export failed: ' . hsc($e->getMessage()); 28 } 29 } 30 } 31 32 public function html() { 33 /** @var helper_plugin_skillforge $helper */ 34 $helper = plugin_load('helper', 'skillforge'); 35 $namespaces = $helper ? $helper->listNamespaces() : array(); 36 $selectedNamespace = isset($_POST['sf_namespace']) ? $_POST['sf_namespace'] : ''; 37 38 ptln('<h1>SkillForge</h1>'); 39 ptln('<p>Export a DokuWiki namespace as an AI-ready Markdown package with SKILL.md, index.md, skill.json and optional media. ZIP creation uses an internal ZIP writer and does not require PHP ZipArchive.</p>'); 40 if ($this->message) ptln('<div class="info">' . $this->message . '</div>'); 41 if ($this->download) { 42 global $ID; 43 $url = wl($ID ?: 'start', array( 44 'do' => 'skillforge_download', 45 'sf_file' => $this->download, 46 'sectok' => getSecurityToken() 47 ), false, '&'); 48 ptln('<p><a class="button" href="' . hsc($url) . '">Download ZIP</a></p>'); 49 ptln('<p><small>If the button does not start a download, copy/open this link in a new tab: <code>' . hsc($url) . '</code></small></p>'); 50 } 51 ptln('<form method="post">'); 52 formSecurityToken(); 53 ptln('<table class="inline">'); 54 ptln('<tr><th>Namespace</th><td>'); 55 if ($namespaces) { 56 ptln('<select name="sf_namespace" required>'); 57 ptln('<option value="">-- Select namespace --</option>'); 58 foreach ($namespaces as $ns) { 59 $sel = ($ns === $selectedNamespace) ? ' selected' : ''; 60 ptln('<option value="' . hsc($ns) . '"' . $sel . '>' . hsc($ns) . '</option>'); 61 } 62 ptln('</select>'); 63 } else { 64 ptln('<input type="text" name="sf_namespace" value="" placeholder="ai:prompting" required>'); 65 ptln('<br><small>No namespaces were found automatically. Enter a namespace manually.</small>'); 66 } 67 ptln('</td></tr>'); 68 ptln('<tr><th>SKILL source page</th><td><code>' . hsc($this->getConf('default_skill_source')) . '</code> <small>Configured in plugin settings.</small></td></tr>'); 69 ptln('<tr><th>Recursive</th><td><label><input type="checkbox" name="sf_recursive" value="1" checked> Include subnamespaces</label></td></tr>'); 70 ptln('<tr><th>Media</th><td><label><input type="checkbox" name="sf_include_media" value="1" checked> Include media namespace files</label></td></tr>'); 71 ptln('</table>'); 72 ptln('<p><button type="submit">Forge Package</button></p>'); 73 ptln('</form>'); 74 ptln('<h2>Metadata in configured source page</h2>'); 75 ptln('<pre><frontmatter>\nname: my-skill\ndescription: What this skill helps the AI do.\nversion: 1.0.0\ntags:\n - ai\n - dokuwiki\n</frontmatter></pre>'); 76 } 77} 78