Skip to content

Instantly share code, notes, and snippets.

@dpilafian
Last active April 11, 2024 01:53
Show Gist options
  • Save dpilafian/930e1677d0c08eed3c39f04d32d7bf19 to your computer and use it in GitHub Desktop.
Save dpilafian/930e1677d0c08eed3c39f04d32d7bf19 to your computer and use it in GitHub Desktop.
A good looking replacement for Apache DirectoryIndex
<!doctype html>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Folder Listing -->
<!-- v1.3.2 (April 10, 2024) -->
<!-- A good looking replacement for Directory Listings: -->
<!-- Rename this file to "index.php" and copy it into a web -->
<!-- server directory to enable browsing on that directory. -->
<!-- Requirement: -->
<!-- Apache HTTP Server Project with php_module enabled -->
<!-- (see: [PKG-INSTALL-HOME]/etc/httpd/httpd.conf) -->
<!-- Example page: -->
<!-- https://centerkey.com/files -->
<!-- WTFPL -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<html lang=en>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1">
<meta name=robots content="index, follow">
<meta name=description content="Folder listing for <?=basename(__DIR__)?>">
<meta name=apple-mobile-web-app-title content="<?=basename(__DIR__)?>">
<title>Folder: <?=basename(__DIR__)?></title>
<link rel=icon href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5/svgs/solid/folder.svg>
<link rel=apple-touch-icon href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5/svgs/solid/folder.svg>
<link rel=mask-icon href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5/svgs/solid/folder.svg color=darkgoldenrod>
<link rel=preconnect href=https://fonts.googleapis.com>
<link rel=preconnect href=https://fonts.gstatic.com crossorigin>
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5/css/all.min.css>
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/dna-engine@3.1/dist/dna-engine.css>
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/web-ignition@2.1/dist/reset.min.css>
<style>
body { color: dimgary; background-color: white; margin: 0px 20px; }
body >main { min-height: auto; }
body >main h1 { color: cadetblue; }
body >main h1+h2 { text-shadow: none; }
body >main a { color: darkgray; border-color: cadetblue; }
body >main a:hover { background-color: cadetblue; outline-color: cadetblue; }
body >main ul.simple-text { margin: 0px 0px 30px 15px; }
body >main ul.simple-text li { display: flex; align-items: center; margin-bottom: 10px; }
body >main ul.simple-text li i.font-icon { width: 1.8em; text-align: center; color: darkslateblue; }
body >main ul.simple-text li i.font-icon[data-icon=arrow-alt-circle-up] { color: dimgray; }
body >main ul.simple-text li i.font-icon[data-icon=folder] { color: darkgoldenrod; }
body >main ul.simple-text li i.font-icon[data-icon=external-link-square-alt] { color: brown; }
body >main p a { margin-right: 8px; }
@media (max-width: 667px) { /* selects iPhone 6/6s/7/8/SE2/SE3 landscape and anything narrower */
body { margin: 0px; }
body >main h1 { font-size: 1.7em; }
body >main ul.simple-text { margin-left: 0px; }
}
@media (prefers-color-scheme: dark) { /* dark mode */
:root { --charcoal: #222222; }
body { color: silver; background-color: var(--charcoal); }
body >main h1 { color: lightseagreen; }
body >main a { color: gainsboro; }
body >main ul.simple-text li i.font-icon { color: mediumslateblue; }
body >main ul.simple-text li i.font-icon[data-icon=arrow-alt-circle-up] { color: silver; }
body >main ul.simple-text li i.font-icon[data-icon=folder] { color: goldenrod; }
body >main ul.simple-text li i.font-icon[data-icon=external-link-square-alt] { color: crimson; }
}
</style>
<script defer src=https://cdn.jsdelivr.net/npm/dna-engine@3.1/dist/dna-engine.min.js></script>
<script defer src=https://cdn.jsdelivr.net/npm/web-ignition@2.1/dist/lib-x.min.js></script>
<script data-on-load=displayPath>
const displayPath = () => {
// Show the URL of the current folder.
const subheader = globalThis.document.querySelector('main >h2');
const homeIcon = globalThis.document.querySelector('.home-link');
subheader.textContent = globalThis.location.origin + globalThis.location.pathname;
homeIcon.href = globalThis.location.origin;
};
</script>
<?php
if (!function_exists("str_ends_with")) {
function str_ends_with($haystack, $needle) {
return substr($haystack, -strlen($needle)) === $needle;
}
}
function showFile($file) {
// Don't display hidden files, php files, link files, or folders.
$extension = pathinfo($file, PATHINFO_EXTENSION);
$extOk = !in_array($extension, array("php"));
$isLink = str_ends_with($file, ".link.md");
return !is_dir($file) && $file[0] !== "." && $file !== "error_log" && $extOk && !$isLink;
}
function fileIcon($fileExtension) {
$fileTypes = array(
"gif" => "file-image",
"html" => "file-code",
"jpeg" => "file-image",
"jpg" => "file-image",
"md" => "file-pen",
"mp3" => "file-audio",
"pdf" => "file-pdf",
"png" => "file-image",
"svg" => "file-image",
"txt" => "file-lines",
"xml" => "file-code",
"zip" => "file-zipper",
);
return array_key_exists($fileExtension, $fileTypes) ? $fileTypes[$fileExtension] : "file";
};
function toHtml($file) {
// Create the <li> string to render a line representing a file or folder.
$isFolder = is_dir($file);
$icon = $isFolder ? "folder" : fileIcon(pathinfo($file, PATHINFO_EXTENSION));
$title = $file;
if (!$isFolder && str_ends_with($file, ".link.md")) {
// Looks for the ".link.md" extension and reads the markdown syntax for a
// hyperlink. For example, the file "example-website.link.md" could have
// one line of text like "[Click me](https://example.org)".
$icon = "external-link-square-alt";
$md = preg_split("/[\[\]()]/", htmlspecialchars(file_get_contents($file)));
$file = $md[3];
$url = parse_url($file);
$relLinkHost = $_SERVER["HTTP_HOST"] ?: $_SERVER["SERVER_NAME"];
$host = isset($url["host"]) ? $url["host"] : $relLinkHost;
$title = $md[1] . " [" . $host . "]";
}
return "<li><a href='$file'><i data-icon=$icon></i></a> <a href='$file'>$title</a></li>";
}
?>
</head>
<body>
<main>
<h1>Folder Listing</h1>
<h2>[URL]</h2>
<ul class=simple-text>
<li><a href=..><i data-icon=arrow-alt-circle-up></i></a> <a href=..>Parent folder</a></li>
<?=implode(PHP_EOL, array_map("toHtml", glob("*", GLOB_ONLYDIR)))?>
<?=implode(PHP_EOL, array_map("toHtml", glob("*.link.md")))?>
<?=implode(PHP_EOL, array_map("toHtml", array_filter(glob("*"), "showFile")))?>
</ul>
<p>
<a href=# class=home-link><i data-icon=home></i></a>
<a href=https://gist.github.com/dpilafian/930e1677d0c08eed3c39f04d32d7bf19><i data-brand=github-alt></i></a>
</p>
</main>
</body>
</html>
#!/bin/bash
##################
# Folder Listing #
# WTFPL #
##################
# To make this file runnable:
# $ chmod +x *.sh.command
banner="Folder Listing"
projectHome=$(cd $(dirname $0); pwd)
pkgInstallHome=$(dirname $(dirname $(which httpd)))
apacheCfg=$pkgInstallHome/etc/httpd
apacheLog=$pkgInstallHome/var/log/httpd/error_log
webDocRoot=$(grep ^DocumentRoot $apacheCfg/httpd.conf | awk -F'"' '{ print $2 }')
displayIntro() {
cd $projectHome
echo
echo $banner
echo $(echo $banner | sed s/./=/g)
pwd
version=$(grep "<\!\-\- v" *.php | awk '{ print $2 }')
echo $version
echo
}
lintPhp() {
cd $projectHome
echo "Linting:"
php --syntax-check *.php
echo
}
publishWebFiles() {
cd $projectHome
publishSite=$webDocRoot/centerkey.com
publishFolder=$publishSite/files
publish() {
echo "Publishing:"
mkdir -p $publishFolder
cp -v *.php $publishFolder/index.php
echo
}
test -w $publishSite && publish
}
launchBrowser() {
cd $projectHome
url=https://centerkey.com/files
test -w $publishSite && url=http://localhost/centerkey.com/files
echo "Opening:"
echo $url
sleep 2
open $url
echo
}
displayIntro
lintPhp
publishWebFiles
launchBrowser
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment