diff --git a/README b/README index a417c0b..b0cdb89 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -LuxTools plugin for DokuWiki +luxtools plugin for DokuWiki Lists files matching a given glob pattern. diff --git a/_test/GeneralTest.php b/_test/GeneralTest.php index 5b23e0b..2e46184 100644 --- a/_test/GeneralTest.php +++ b/_test/GeneralTest.php @@ -39,47 +39,43 @@ class GeneralTest extends DokuWikiTest } /** - * Test to ensure that every conf['...'] entry in conf/default.php has a corresponding meta['...'] entry in - * conf/metadata.php. + * luxtools settings are managed via the plugin's admin page, not via the Configuration Manager. + * Ensure default config exists and (when present) metadata.php does not expose any settings. */ public function testPluginConf(): void { $conf_file = __DIR__ . '/../conf/default.php'; $meta_file = __DIR__ . '/../conf/metadata.php'; - if (!file_exists($conf_file) && !file_exists($meta_file)) { - self::markTestSkipped('No config files exist -> skipping test'); + if (!file_exists($conf_file)) { + self::markTestSkipped('No config default.php exists -> skipping test'); } - if (file_exists($conf_file)) { - include($conf_file); - } - if (file_exists($meta_file)) { - include($meta_file); - } + $conf = null; + $meta = null; - $this->assertEquals( - gettype($conf), - gettype($meta), - 'Both ' . DOKU_PLUGIN . 'luxtools/conf/default.php and ' . DOKU_PLUGIN . 'luxtools/conf/metadata.php have to exist and contain the same keys.' + include($conf_file); + $this->assertIsArray( + $conf, + 'The ' . DOKU_PLUGIN . 'luxtools/conf/default.php file needs to define $conf as an array.' ); - if ($conf !== null && $meta !== null) { - foreach ($conf as $key => $value) { - $this->assertArrayHasKey( - $key, - $meta, - 'Key $meta[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'luxtools/conf/metadata.php' - ); + if (file_exists($meta_file)) { + include($meta_file); + + if ($meta === null) { + // If the file exists but does not define $meta, treat it as empty. + $meta = []; } - foreach ($meta as $key => $value) { - $this->assertArrayHasKey( - $key, - $conf, - 'Key $conf[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'luxtools/conf/default.php' - ); - } + $this->assertIsArray( + $meta, + 'The ' . DOKU_PLUGIN . 'luxtools/conf/metadata.php file needs to define $meta as an array.' + ); + $this->assertEmpty( + $meta, + 'luxtools should not expose settings via the Configuration Manager.' + ); } } diff --git a/admin/main.php b/admin/main.php new file mode 100644 index 0000000..4c86353 --- /dev/null +++ b/admin/main.php @@ -0,0 +1,195 @@ +getLang('menu'); + } + + public function getMenuSort() + { + // keep near other plugin tools + return 1011; + } + + public function forAdminOnly() + { + return true; + } + + public function handle() + { + global $INPUT; + + if ($INPUT->str('luxtools_cmd') !== 'save') return; + + if (!checkSecurityToken()) { + msg($this->getLang('err_security'), -1); + return; + } + + $newConf = []; + // Normalize newlines to "\n" for consistent parsing + $paths = $INPUT->str('paths'); + $paths = str_replace(["\r\n", "\r"], "\n", $paths); + $newConf['paths'] = $paths; + $newConf['allow_in_comments'] = (int)$INPUT->bool('allow_in_comments'); + $newConf['defaults'] = $INPUT->str('defaults'); + $newConf['extensions'] = $INPUT->str('extensions'); + $newConf['thumb_placeholder'] = $INPUT->str('thumb_placeholder'); + $newConf['gallery_thumb_scale'] = $INPUT->str('gallery_thumb_scale'); + $newConf['open_service_url'] = $INPUT->str('open_service_url'); + + if ($this->savePluginLocalConf($newConf)) { + msg($this->getLang('saved'), 1); + } else { + msg($this->getLang('err_save'), -1); + } + } + + public function html() + { + global $ID; + + echo '
'; + echo '

' . hsc($this->getLang('settings')) . '

'; + + echo '
'; + echo ''; + echo ''; + echo ''; + echo ''; + echo formSecurityToken(); + + echo '
'; + echo '' . hsc($this->getLang('legend')) . ''; + + // paths: multiline textarea + $paths = (string)$this->getConf('paths'); + echo '
'; + + // allow_in_comments + $checked = $this->getConf('allow_in_comments') ? ' checked="checked"' : ''; + echo '
'; + + // defaults + echo '
'; + + // extensions + echo '
'; + + // thumb_placeholder + echo '
'; + + // gallery_thumb_scale + echo '
'; + + // open_service_url + echo '
'; + + echo ''; + + echo '
'; + echo '
'; + + echo '
'; + } + + /** + * Persist plugin settings to conf/plugins/luxtools.local.php. + * + * @param array $newConf + * @return bool + */ + protected function savePluginLocalConf(array $newConf) + { + if (!defined('DOKU_CONF')) return false; + + $plugin = 'luxtools'; + $confDir = DOKU_CONF . 'plugins/'; + $file = $confDir . $plugin . '.local.php'; + + if (function_exists('io_mkdir_p')) { + io_mkdir_p($confDir); + } elseif (!@is_dir($confDir)) { + @mkdir($confDir, 0777, true); + } + + // Only write known keys; ignore extras. + $lines = ["configKeys as $key) { + if (!array_key_exists($key, $newConf)) continue; + $value = $newConf[$key]; + $lines[] = '$conf[' . var_export($key, true) . '] = ' . $this->exportPhpValue($value, $key) . ';'; + } + $lines[] = ''; + $content = implode("\n", $lines); + + if (function_exists('io_saveFile')) { + return (bool)io_saveFile($file, $content); + } + + return @file_put_contents($file, $content, LOCK_EX) !== false; + } + + /** + * Export a value to PHP code. + * + * We use nowdoc for multiline strings to safely preserve newlines. + * + * @param mixed $value + * @param string $key + * @return string + */ + protected function exportPhpValue($value, string $key): string + { + if (is_bool($value) || is_int($value) || is_float($value) || $value === null) { + return var_export($value, true); + } + + $value = (string)$value; + + if (str_contains($value, "\n") || str_contains($value, "\r")) { + $marker = strtoupper('LUXTOOLS_' . preg_replace('/[^A-Z0-9_]/i', '_', $key) . '_EOT'); + // Extremely unlikely, but avoid delimiter collision. + while (str_contains($value, $marker)) { + $marker .= '_X'; + } + return "<<<'$marker'\n" . $value . "\n$marker"; + } + + return var_export($value, true); + } +} diff --git a/conf/metadata.php b/conf/metadata.php index d1d95bf..cc383ec 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -2,19 +2,10 @@ /** * Metadata for configuration manager plugin - * Additions for the luxtools plugin * - * @author Gina Haeussge + * NOTE: luxtools settings are managed via the plugin's dedicated admin page + * (Admin -> Additional Plugins). Therefore, we intentionally do not expose + * any settings to the Configuration Manager. */ -$meta['paths'] = array(''); -$meta['allow_in_comments'] = array('onoff'); -$meta['defaults'] = array('string'); -$meta['extensions'] = array('string'); - -$meta['thumb_placeholder'] = array('string'); - -// Thumbnail generation scale factor for the {{images>...}} gallery. -$meta['gallery_thumb_scale'] = array('string'); - -$meta['open_service_url'] = array('string'); +$meta = []; diff --git a/lang/de/lang.php b/lang/de/lang.php index 2ba6eaf..a628f56 100644 --- a/lang/de/lang.php +++ b/lang/de/lang.php @@ -16,3 +16,19 @@ $lang['error_outsidejail'] = 'Zugriff verweigert'; $lang['empty_files'] = 'Keine Dateien'; $lang['empty_images'] = 'Keine Bilder'; + +$lang['menu'] = 'luxtools'; +$lang['settings'] = 'luxtools-Einstellungen'; +$lang['legend'] = 'Einstellungen'; +$lang['btn_save'] = 'Speichern'; +$lang['saved'] = 'Einstellungen gespeichert.'; +$lang['err_save'] = 'Einstellungen konnten nicht gespeichert werden. Bitte Schreibrechte für conf/plugins/ prüfen.'; +$lang['err_security'] = 'Sicherheits-Token ungültig. Bitte erneut versuchen.'; + +$lang['paths'] = 'Erlaubte Basis-Pfade (eine pro Zeile oder komma-separiert).'; +$lang['allow_in_comments'] = 'Files-Syntax in Kommentaren erlauben.'; +$lang['defaults'] = 'Standardoptionen (gleiche Syntax wie bei Inline-Konfiguration).'; +$lang['extensions'] = 'Kommagetrennte Liste erlaubter Dateiendungen.'; +$lang['thumb_placeholder'] = 'MediaManager-ID für den Platzhalter der Galerie-Thumbnails.'; +$lang['gallery_thumb_scale'] = 'Skalierungsfaktor für Galerie-Thumbnails. 2 erzeugt schärfere Thumbnails auf HiDPI-Displays (Anzeige bleibt 150×150).'; +$lang['open_service_url'] = 'URL des lokalen Client-Dienstes für {{open>...}} (z.B. http://127.0.0.1:8765).'; diff --git a/lang/de/settings.php b/lang/de/settings.php index a27b5c0..de6e4e4 100644 --- a/lang/de/settings.php +++ b/lang/de/settings.php @@ -1,7 +1,11 @@ ...}} button (e.g. http://127.0.0.1:8765).'; diff --git a/lang/en/settings.php b/lang/en/settings.php index 3206f46..35f8575 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -1,5 +1,6 @@ ...}} syntax implementation lives in syntax/files.php. */ diff --git a/syntax/AbstractSyntax.php b/syntax/AbstractSyntax.php index 3367491..6935531 100644 --- a/syntax/AbstractSyntax.php +++ b/syntax/AbstractSyntax.php @@ -6,7 +6,7 @@ use dokuwiki\plugin\luxtools\Output; use dokuwiki\plugin\luxtools\Path; /** - * LuxTools Plugin: Abstract base class for file-listing syntax handlers. + * luxtools Plugin: Abstract base class for file-listing syntax handlers. * * Provides shared functionality for directory, files, and images syntax. */ diff --git a/syntax/directory.php b/syntax/directory.php index 916f081..5b7b7f6 100644 --- a/syntax/directory.php +++ b/syntax/directory.php @@ -6,7 +6,7 @@ use dokuwiki\plugin\luxtools\Path; require_once(__DIR__ . '/AbstractSyntax.php'); /** - * LuxTools Plugin: Directory syntax. + * luxtools Plugin: Directory syntax. * * Lists the direct children (folders and files) of a given path. * Always renders as a table. diff --git a/syntax/files.php b/syntax/files.php index bf12b2e..c28b4ba 100644 --- a/syntax/files.php +++ b/syntax/files.php @@ -5,7 +5,7 @@ use dokuwiki\plugin\luxtools\Output; require_once(__DIR__ . '/AbstractSyntax.php'); /** - * LuxTools Plugin: Files syntax. + * luxtools Plugin: Files syntax. * * Lists files matching a given glob pattern. */ diff --git a/syntax/images.php b/syntax/images.php index 5957b68..32b3179 100644 --- a/syntax/images.php +++ b/syntax/images.php @@ -5,7 +5,7 @@ use dokuwiki\plugin\luxtools\Output; require_once(__DIR__ . '/AbstractSyntax.php'); /** - * LuxTools Plugin: Image gallery syntax. + * luxtools Plugin: Image gallery syntax. * * Renders a thumbnail gallery of images matching a glob pattern. */ diff --git a/syntax/open.php b/syntax/open.php index 8c94df0..46b3555 100644 --- a/syntax/open.php +++ b/syntax/open.php @@ -3,7 +3,7 @@ use dokuwiki\Extension\SyntaxPlugin; /** - * LuxTools Plugin: Open local path syntax. + * luxtools Plugin: Open local path syntax. * * Renders an inline button. Clicking it triggers client-side JS that attempts * to open the configured path in the default file manager (best-effort).