['sh', 'html', 'js', 'vbs', 'exe', 'md'], 'txt' => ['txt', 'md'], 'img' => ['jpeg', 'jpg', 'jpe', 'bmp', 'webp', 'gif', 'png'], 'pdf' => ['pdf'], 'zip' => ['7z', 'zip', 'gz', 'tar', 'rar', 'r[0-9]{2,}'], 'doc' => ['odt', 'doc', 'docx'], 'pres' => ['odp', 'ppt', 'pptx'], 'spread' => ['ods', 'xls', 'xlsx'], 'video' => ['ogv', 'mp4', 'mpg', 'mpeg', 'mov', 'avi', 'wmv', 'flv', 'webm'], 'audio' => ['aiff', 'aif', 'wma', 'aac', 'flac', 'mp3', 'ogg', 'm4a'], ]; private $appUrl; /** * Initializes the object with directory paths and configuration settings. * * This constructor initializes the object with the provided directory paths and configuration. * It verifies that the given paths are authorized using utility methods. If any path is unauthorized, * the script terminates with an error message. * * @param string $dir The current directory path to be set. * @param string $requestedDir The requested directory path to be set, relative to the 'photos' directory. * @param array $config Configuration settings to be used by the object. * * @throws \Exception If an unauthorized access attempt is detected. */ function __construct(string $dir, string $requestedDir, array $config) { if (!Utils::isPathAuthorized($dir)) { die("ERROR 02: Unauthorized access!"); } if (!Utils::isPathAuthorized('photos' . $requestedDir)) { die("ERROR 03: Unauthorized access!"); } $this->currentDir = $dir; $this->requestedDir = $requestedDir; $this->config = $config; if ($config['showShareLink'] === true) { $this->appUrl = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME']; } } /** * Generates a breadcrumb navigation based on the requested directory. * * This method creates an HTML breadcrumb navigation trail from the requested directory path. * It links each directory level, except for the last one, which is displayed as plain text. * If the requested directory is empty or set to "photos", it returns an empty string. * * @return string The HTML markup for the breadcrumb navigation, or an empty string if no breadcrumb is needed. */ public function makeBreadcrumb(): string { if ($this->requestedDir !== '' && $this->requestedDir !== 'photos') { $breadcrumb_navigation = 'Home > '; $navitems = explode("/", htmlspecialchars($this->requestedDir)); $path = ''; foreach ($navitems as $index => $item) { if ($index === 0) continue; $path .= $item; if ($index === count($navitems) - 1) { $breadcrumb_navigation .= htmlspecialchars($item); } else { $breadcrumb_navigation .= '' . htmlspecialchars($item) . ' > '; $path .= '/'; } } return $breadcrumb_navigation; } else { return ''; } } /** * Checks if the specified file is readable and updates a global message if it is not. * * This function verifies if the given file has read permissions. If the file is not * readable, it sets a global `$messages` variable with a warning message and a link * to a guide on how to change file permissions. * * @param string $file The path to the file or directory to check. * * @global string $messages A global variable used to store the error message if permissions are incorrect. */ function checkpermissions(string $file): void { global $messages; if (!is_readable($file)) { $messages = "At least one file or folder has wrong permissions. " . "Learn how to " . "set file permissions"; } } /** * Lists directories and files within the current directory. * * This method scans the current directory and generates a list of directories and files. * Directories and files are categorized, and thumbnails are created or fetched based on * the file type. The results are sorted and returned in an associative array containing * two keys: 'dir' for directories and 'file' for files. If the directory cannot be opened, * the script terminates with an error message. * * @return array An associative array with two keys: * - 'dir': An array of directories with details like name, thumbnail URL, link, type, date, and size. * - 'file': An array of files with details like name, thumbnail URL, link, type, date, size, data attributes, and image captions. * * @throws RuntimeException If the current directory cannot be opened. */ function listDirs(): array { $listDir = $listFile = []; if (is_dir($this->currentDir) && $handle = opendir($this->currentDir)) { while (false !== ($file = readdir($handle))) { if (in_array($file, $this->config['skipObjects'])) { continue; } if (is_dir($this->currentDir . '/' . $file)) { if ($file != "." && $file != "..") { if ($this->defineThumbnailDir($file)) { $thumb = $this->makeFolderThumbUrl($file); } else { $thumb = $this->getfirstImage($file); } $listDir[] = [ 'name' => $file, 'thumb' => $thumb, 'link' => '?dir=' . urlencode($this->requestedDir . '/' . $file), 'type' => 'folder', 'date' => date("Y-m-d", filemtime($this->currentDir . '/' . $file)), 'size' => filesize($this->currentDir . '/' . $file), 'dataAttr' => null, 'imgCaption' => null ]; } } if (is_file($this->currentDir . '/' . $file) && $file != "." && $file != ".." && $file != "folder.jpg") { $extension = $this->getExtension($file); if ($extension === 'deny') { continue; } if ($extension !== 'img') { $thumb = '/assets/images/filetype_' . $extension . '.svg'; } else { $thumb = $this->makeImageThumbUrl($file); } $caption = $this->computeCaption($file); $listFile[] = [ 'name' => pathinfo($file, PATHINFO_FILENAME), 'thumb' => $thumb, 'link' => $this->currentDir . '/' . $file, 'type' => $extension, 'date' => date("Y-m-d", filemtime($this->currentDir . '/' . $file)), 'size' => filesize($this->currentDir . '/' . $file), 'dataAttr' => $caption['dataAttr'], 'imgCaption' => $caption['content'] ]; } } closedir($handle); $order = $this->config['orderBy'] === 'desc' ? 1 : -1; usort($listDir, function ($a, $b) use ($order) { return $order * strcmp(strtolower($a[$this->config['sortBy']]), strtolower($b[$this->config['sortBy']])); }); usort($listFile, function ($a, $b) use ($order) { return $order * strcmp(strtolower($a[$this->config['sortBy']]), strtolower($b[$this->config['sortBy']])); }); return ['dir' => $listDir, 'file' => $listFile]; } else { die("ERROR: Could not open " . htmlspecialchars(stripslashes($this->currentDir)) . " for reading!"); } } /** * Retrieves a list of the most recent image files, sorted by modification time. * * @return array An array of image files with their URLs and modification times. */ public function makeMoreRecentFile(): array { $allFile = []; $directory = new \RecursiveDirectoryIterator('photos/'); foreach (new \RecursiveIteratorIterator($directory) as $file) { if (in_array($directory->getFilename(), $this->config['skipObjects'])) { continue; } if (in_array($file->getExtension(), $this->fileExtensions['img'])) { $allFile[] = [ 'url' => $this->appUrl . '/' . $file->getPathname(), 'thumb' => $this->appUrl . '/' . $this->makeImageThumbUrl($file->getPathname()), 'tdate' => $file->getMTime(), ]; } } $order = $this->config['orderBy'] === 'asc' ? 1 : -1; usort($allFile, function ($a, $b) use ($order) { return $order * ($a['tdate'] - $b['tdate']); }); return array_slice($allFile, 0, $this->config['nbItemsAtom'], true); } /** * Determines the type of file based on its extension. * * This method checks the file extension against a predefined list of known file types * and returns a category name corresponding to the file type. If the extension does not match * any known types, it returns 'other'. * * @param string $file The name of the file whose extension is to be checked. * * @return string The category name of the file type based on its extension. Possible values * include 'img', 'md', 'pdf', 'zip', 'rar', 'tar', 'gzip', 'doc', 'pres', * 'ods', 'video', 'audio', or 'other' if the extension is not recognized. */ private function getExtension(string $file): string { if (!is_readable($this->currentDir . '/' . $file) || !is_file($this->currentDir . '/' . $file)) { return 'broken'; } $fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); foreach ($this->fileExtensions as $type => $extensions) { foreach ($extensions as $ext) { if (preg_match("/^$ext$/i", $fileExtension)) { return $type; } } } return 'other'; } /** * Checks if a thumbnail image exists in the specified directory. * * This method determines if a `folder.jpg` file exists in the given directory * within the current directory. It returns `true` if the file is found, indicating * that the directory has a defined thumbnail; otherwise, it returns `false`. * * @param string $dir The name of the directory to check for a thumbnail. * * @return bool `true` if the `folder.jpg` file exists in the directory, `false` otherwise. */ private function defineThumbnailDir(string $dir): bool { if (file_exists($this->currentDir . '/' . $dir . '/folder.jpg')) { return true; } else { return false; } } /** * Generates a URL for the folder thumbnail image. * * This method constructs a URL to request the creation of a thumbnail image for a * specified folder. It uses the `createthumb.php` script with query parameters for * the thumbnail image file and its size. * * @param string $file The name of the folder for which to create a thumbnail. * * @return string The URL to request the creation of the folder thumbnail image. */ private function makeFolderThumbUrl(string $file): string { $imgParams = http_build_query( array( 'filename' => "$this->currentDir/$file/folder.jpg" ), '', '&' ); return 'createthumb.php?' . $imgParams; } /** * Generates a URL for the image thumbnail. * * This method constructs a URL to request the creation of a thumbnail image for * a given file if its extension is recognized as an image format. If the file's * extension is not a recognized image format, it returns a URL to a default image. * * @param string $file The name of the image file for which to create a thumbnail. * * @return string The URL to request the creation of the image thumbnail or a default image URL if the extension is not recognized. */ private function makeImageThumbUrl(string $file): string { $pathinfo = pathinfo($file); $ext = strtolower($pathinfo['extension']); if (in_array($ext, $this->fileExtensions['img'])) { $imageName = $file; if(empty($this->currentDir)){ $path = $imageName; } else { $path = $this->currentDir . '/' . $imageName; } $imgParams = 'createthumb.php?' . http_build_query( array( 'filename' => $path ), '', '&' ); } else { $imgParams = 'assets/images/default.svg'; } return $imgParams; } /** * Retrieves the URL for the first image found in the specified directory. * * This method scans the given directory for image files with recognized extensions. * It returns a URL for the thumbnail of the first image found. If no image is found * or if the directory does not exist, it returns a URL to a default image. * * @param string $dir The name of the directory to scan for images. * * @return string The URL for the thumbnail of the first image found, or a URL to a default image if no image is found or the directory does not exist. */ private function getFirstImage(string $dir): string { $fullPath = $this->currentDir . '/' . $dir; $imageName = 'assets/images/default.svg'; if (!is_dir($fullPath)) { return $imageName; } if ($handle = opendir($fullPath)) { while (false !== ($file = readdir($handle))) { if ($file[0] == '.') { continue; } $pathinfo = pathinfo($file); if (empty($pathinfo['extension'])) { continue; } $ext = strtolower($pathinfo['extension']); if (in_array($ext, $this->fileExtensions['img'])) { $imageName = 'createthumb.php?' . http_build_query( array( 'filename' => $fullPath . '/' . $file ), '', '&' ); closedir($handle); return $imageName; } } closedir($handle); } return $imageName; } /** * Retrieves and converts the content of a Markdown note in the current directory. * * This method checks if a `note.md` file exists in the current directory. If the file * is found, its content is read and converted from Markdown to HTML using the * `MarkdownConverter` with specified extensions. If the file does not exist, an * empty string is returned. * * @return string The HTML content of the Markdown file, or an empty string if the file does not exist. */ public function getFolderNote(): string { if (file_exists($this->currentDir . '/note.md')) { $content = file_get_contents($this->currentDir . '/note.md'); $config = []; $environment = new Environment($config); $environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new TaskListExtension()); $environment->addExtension(new AutolinkExtension()); $converter = new MarkdownConverter($environment); return $converter->convert($content)->getContent(); } else { return ''; } } /** * Computes the caption for an image by combining Markdown content and EXIF data. * * This method checks for the presence of a Markdown file and EXIF data associated with * the given image. It combines both sources of information, converting the Markdown content * to HTML and appending EXIF data if available. The result is a caption that can be displayed * in a Lightbox gallery, with the relevant CSS class and HTML content. * * @param string $img The name of the image for which to compute the caption. * * @return array An associative array containing: * - `dataAttr` (string): A data attribute for the caption's CSS class, used by the Lightbox. * - `content` (string): The HTML content of the caption, including Markdown and EXIF data if available. */ private function computeCaption(string $img): array { $mdContent = $exifData = $shareLink = $listLink = ''; if (file_exists($this->currentDir . '/' . $img . '.md')) { $mdContent = file_get_contents($this->currentDir . '/' . $img . '.md'); } if ($this->config['displayExifInfo']) { $exifData = $this->computeExifData($this->currentDir . '/' . $img); } if ($this->config['showShareLink'] === true) { $shareLink = '## Share link'; $listLink = '
'; } if (empty($exifData) && empty($mdContent) && empty($shareLink)) { return [ 'dataAttr' => '', 'content' => '' ]; } $id = uniqid(); $environment = new Environment(); $environment->addExtension(new CommonMarkCoreExtension()); $environment->addExtension(new TaskListExtension()); $environment->addExtension(new AutolinkExtension()); $converter = new MarkdownConverter($environment); if (!empty($exifData)) { $exifData = "\n" . '## Exif' . "\n" . $exifData; } $mdContent = $mdContent . "\n" . $exifData . "\n" . $shareLink; $captionData = [ 'dataAttr' => 'description: .custom-' . $id, 'content' => '