improve directory listing
Some checks failed
DokuWiki Default Tasks / all (push) Has been cancelled

This commit is contained in:
2026-01-06 11:38:30 +01:00
parent 41580fa010
commit a835f76f90
8 changed files with 107 additions and 15 deletions

View File

@@ -13,6 +13,9 @@ class Crawler
/** @var bool */
protected $sortreverse = false;
/** @var bool */
protected $foldersFirst = false;
/** @var string[] patterns to ignore */
protected $ignore = [];
@@ -41,6 +44,11 @@ class Crawler
$this->sortreverse = $sortreverse;
}
public function setFoldersFirst($foldersFirst)
{
$this->foldersFirst = (bool)$foldersFirst;
}
/**
* Does a (recursive) crawl for finding files based on a given pattern.
* Based on a safe glob reimplementation using fnmatch and opendir.
@@ -204,13 +212,41 @@ class Crawler
$callback = [$this, 'compare' . ucfirst($this->sortby)];
if (!is_callable($callback)) return $items;
// Optional grouping: keep directories before files.
// Implement reverse ordering by inverting comparisons instead of array_reverse(),
// so the directory-first grouping stays intact.
if ($this->foldersFirst) {
usort($items, function ($a, $b) use ($callback) {
$aIsDir = $this->isDirectoryItem($a);
$bIsDir = $this->isDirectoryItem($b);
if ($aIsDir !== $bIsDir) {
return $aIsDir ? -1 : 1;
}
$cmp = call_user_func($callback, $a, $b);
if ($this->sortreverse) $cmp = -$cmp;
return $cmp;
});
} else {
usort($items, $callback);
if ($this->sortreverse) {
$items = array_reverse($items);
}
}
return $items;
}
/**
* Detect whether an item represents a directory.
* Supports both crawl() results (children tree) and listDirectory() results (isdir).
*/
protected function isDirectoryItem($item)
{
if (!is_array($item)) return false;
if (!empty($item['isdir'])) return true;
return array_key_exists('children', $item) && $item['children'] !== false;
}
/**
* Check if a file is allowed by the configured extensions
*

View File

@@ -265,8 +265,26 @@ class Output
$renderer->table_open($columns);
if ($params['tableheader']) {
$hasOpenLocation = isset($params['openlocation']) && is_string($params['openlocation']) && trim($params['openlocation']) !== '';
$hasHeader = !empty($params['tableheader']);
if ($hasOpenLocation || $hasHeader) {
$renderer->tablethead_open();
// Small row above the header with an "Open Location" link.
if ($hasOpenLocation && ($renderer instanceof \Doku_Renderer_xhtml)) {
$openItem = [
'name' => $this->getLang('openlocation'),
'path' => $params['openlocation'],
'isdir' => true,
];
/** @var \Doku_Renderer_xhtml $renderer */
$renderer->doc .= '<tr class="luxtools-openlocation-row"><td colspan="' . (int)$columns . '">';
$this->renderDirectoryLink($openItem);
$renderer->doc .= '</td></tr>';
}
if ($hasHeader) {
$renderer->tablerow_open();
$renderer->tableheader_open();
@@ -286,6 +304,8 @@ class Output
}
$renderer->tablerow_close();
}
$renderer->tablethead_close();
}

View File

@@ -10,5 +10,6 @@
$lang['filename'] = 'Dateiname';
$lang['filesize'] = 'Dateigröße';
$lang['lastmodified'] = 'Letzte Änderung';
$lang['openlocation'] = 'Ort öffnen';
$lang['error_nomatch'] = 'Keine Treffer';
$lang['error_outsidejail'] = 'Zugriff verweigert';

View File

@@ -10,5 +10,6 @@
$lang['filename'] = 'Filename';
$lang['filesize'] = 'Filesize';
$lang['lastmodified'] = 'Last modified';
$lang['openlocation'] = 'Open Location';
$lang['error_nomatch'] = 'No match';
$lang['error_outsidejail'] = 'Access denied';

View File

@@ -9,5 +9,6 @@
$lang['filename'] = 'Bestandsnaam';
$lang['filesize'] = 'Bestandsgrootte';
$lang['lastmodified'] = 'Laatst gewijzigd';
$lang['openlocation'] = 'Locatie openen';
$lang['error_nomatch'] = 'Niets gevonden';
$lang['error_outsidejail'] = 'Toegang geweigerd';

19
style.css Normal file
View File

@@ -0,0 +1,19 @@
/* LuxTools plugin styles
* Keep this minimal and scoped to the plugin container.
*/
/* DokuWiki often highlights rows on hover. Avoid highlighting header rows. */
div.filetools-plugin table thead tr:hover > * {
background-color: @ini_background_alt !important;
}
/* "Open Location" row above the header should be visually smaller. */
div.filetools-plugin table thead tr.luxtools-openlocation-row td {
font-size: 80%;
padding-top: 0.2em;
padding-bottom: 0.2em;
}
div.filetools-plugin table thead tr.luxtools-openlocation-row:hover td {
background-color: @ini_background !important;
}

View File

@@ -165,6 +165,7 @@ abstract class syntax_plugin_luxtools_abstract extends SyntaxPlugin
'order' => 'asc',
'style' => 'list',
'tableheader' => 0,
'foldersfirst' => 0,
'recursive' => 0,
'titlefile' => '_title.txt',
'cache' => 0,
@@ -222,6 +223,7 @@ abstract class syntax_plugin_luxtools_abstract extends SyntaxPlugin
$crawler = new Crawler($this->getConf('extensions'));
$crawler->setSortBy($params['sort']);
$crawler->setSortReverse($params['order'] === 'desc');
$crawler->setFoldersFirst(($params['foldersfirst'] ?? 0) != 0);
return $crawler;
}

View File

@@ -13,6 +13,15 @@ require_once(__DIR__ . '/AbstractSyntax.php');
*/
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
{
@@ -35,6 +44,9 @@ class syntax_plugin_luxtools_directory extends syntax_plugin_luxtools_abstract
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'],