Files
luxtools-plugin/syntax/directory.php
2026-01-22 20:27:47 +01:00

130 lines
4.5 KiB
PHP

<?php
use dokuwiki\plugin\luxtools\Output;
use dokuwiki\plugin\luxtools\Path;
require_once(__DIR__ . '/AbstractSyntax.php');
/**
* luxtools Plugin: Directory syntax.
*
* Lists the direct children (folders and files) of a given path.
* Always renders as a table.
* Also accepts the 'files' keyword for backwards compatibility with glob patterns.
*/
class syntax_plugin_luxtools_directory extends syntax_plugin_luxtools_abstract
{
/** @inheritdoc */
protected function getDefaultParams(): array
{
return [
// Directory listings should group folders before files by default.
'foldersfirst' => 1,
];
}
/** @inheritdoc */
protected function getSyntaxKeyword(): string
{
return 'directory';
}
/** @inheritdoc */
public function connectTo($mode)
{
// Accept both {{directory>...}} and {{files>...}} for backwards compatibility
$this->Lexer->addSpecialPattern('\{\{directory>.+?\}\}', $mode, 'plugin_luxtools_directory');
$this->Lexer->addSpecialPattern('\{\{files>.+?\}\}', $mode, 'plugin_luxtools_directory');
}
/** @inheritdoc */
public function handle($match, $state, $pos, Doku_Handler $handler)
{
// Detect which keyword was used
$keyword = 'directory';
if (str_starts_with($match, '{{files>')) {
$keyword = 'files';
}
$match = substr($match, strlen('{{' . $keyword . '>'), -2);
[$path, $flags] = array_pad(explode('&', $match, 2), 2, '');
$params = $this->parseFlags($flags);
$pathData = $this->processPath($path);
// Store the original keyword to determine processing mode
$pathData['isGlobPattern'] = ($keyword === 'files');
return ['pathData' => $pathData, 'params' => $params];
}
/** @inheritdoc */
protected function processPath(string $path): array
{
// Check if path contains glob characters (*, ?, [, ])
$hasGlob = (str_contains($path, '*') || str_contains($path, '?') ||
str_contains($path, '[') || str_contains($path, ']'));
if ($hasGlob) {
// Process as glob pattern (old files syntax)
[$base, $pattern] = $this->separatePathAndPattern($path);
return ['base' => $base, 'pattern' => $pattern, 'isGlobPattern' => true];
} else {
// Process as directory path
$path = Path::cleanPath($path, true);
return ['path' => $path, 'isGlobPattern' => false];
}
}
/** @inheritdoc */
protected function doRender(string $format, \Doku_Renderer $renderer, array $pathData, array $params): bool
{
$isGlobPattern = $pathData['isGlobPattern'] ?? false;
if ($isGlobPattern && isset($pathData['base'], $pathData['pattern'])) {
// Old files syntax behavior: crawl with glob pattern
$pathInfo = $this->getPathInfoSafe($pathData['base'], $renderer);
if ($pathInfo === false) {
return true;
}
$crawler = $this->createCrawler($params);
$result = $crawler->crawl(
$pathInfo['root'],
$pathInfo['local'],
$pathData['pattern'],
$params['recursive'],
$params['titlefile']
);
// Pass the base directory as openlocation so the "Open Location" link is displayed.
$params['openlocation'] = $pathInfo['root'] . $pathInfo['local'];
$output = new Output($renderer, $pathInfo['root'], $pathInfo['web'], $result, $this);
$output->renderAsTable($params);
} else {
// Normal directory listing behavior
$pathInfo = $this->getPathInfoSafe($pathData['path'], $renderer);
if ($pathInfo === false) {
return true;
}
// Provide the current directory path so Output can render the "Open Location" link.
$params['openlocation'] = $pathInfo['root'] . $pathInfo['local'];
$crawler = $this->createCrawler($params);
$items = $crawler->listDirectory(
$pathInfo['root'],
$pathInfo['local'],
$params['titlefile']
);
// Render the table even if empty so the "Open Location" link is displayed.
$output = new Output($renderer, $pathInfo['root'], $pathInfo['web'], $items, $this);
$output->renderAsFlatTable($params);
}
return true;
}
}