From fb1d1557f416376a5a194efdb6d462dfb2e7b90d Mon Sep 17 00:00:00 2001 From: Knah Tsaeb Date: Fri, 4 Oct 2024 16:25:50 +0200 Subject: [PATCH] Add atom feed --- Dockerfile | 8 +-- README.md | 28 ++++++----- app/Cache.php | 36 +++++++++++++- app/FileAndDir.php | 38 ++++++++++++++- public/createthumb.php | 10 ++-- public/feed.php | 84 ++++++++++++++++++++++++++++++++ public/index.php | 25 ++++++---- public/templates/board/board.php | 14 ++++-- 8 files changed, 206 insertions(+), 37 deletions(-) create mode 100644 public/feed.php diff --git a/Dockerfile b/Dockerfile index a9cdf88..88f28ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM ubuntu/apache2 MAINTAINER Knah Tsaeb -LABEL version="0.1.0" +LABEL version="0.2.0" LABEL description="Apache 2 / PHP / NanoGal" ENV DEBIAN_FRONTEND=noninteractive @@ -30,7 +30,7 @@ WORKDIR /var/www/ ENTRYPOINT "start.sh" # Build image -# docker build -t nanogal:0.1.0 . +# docker build -t nanogal:0.2.0 . # Run container -# docker run -v nanogal_datas:/var/www/datas:ro -v nanogal_photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.1.0 -# docker run -v /opt/nanogal/datas:/var/www/datas:ro -v /opt/nanogal/photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.1.0 \ No newline at end of file +# docker run -v nanogal_datas:/var/www/datas:ro -v nanogal_photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.2.0 +# docker run -v /opt/nanogal/datas:/var/www/datas:ro -v /opt/nanogal/photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.2.0 \ No newline at end of file diff --git a/README.md b/README.md index dea9071..c503751 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Image use ```shell wget https://forge.leslibres.org/Knah-Tsaeb/NanoGal/raw/branch/main/Dockerfile -docker buildx build --no-cache -t nanogal:0.1.0 . +docker buildx build --no-cache -t nanogal:0.2.0 . ``` #### Start container @@ -43,13 +43,13 @@ docker buildx build --no-cache -t nanogal:0.1.0 . You can use native docker volume. ```shell -docker run -v nanogal_datas:/var/www/datas:ro -v nanogal_photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.1.0 +docker run -v nanogal_datas:/var/www/datas:ro -v nanogal_photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.2.0 ``` Or use absolute path ```shell -docker run -v /opt/nanogal/datas:/var/www/datas:ro -v /opt/nanogal/photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.1.0 +docker run -v /opt/nanogal/datas:/var/www/datas:ro -v /opt/nanogal/photos:/var/www/public/photos:ro -e TZ=UTC -p 8187:80 --name nanogal nanogal:0.2.0 ``` ### Classic way @@ -70,8 +70,8 @@ If you want, you can delete this data. Under GNU/Linux you can install `exiftool ### Adding your photos -- Simply add your photos to the `photos` directory via FTP or SFTP. -- You can create as many subdirectories as you want. +- Simply add your photos to the `photos` directory via FTP or SFTP or use sync app (look idea section). +- You can create any subdirectories as you want. ### Adding a comment to a gallery @@ -111,7 +111,8 @@ $userConfig = [ 'thumbSize' => 250, // Thumbnail height/width (square thumbs) 'displayExifInfo' => false, // Display Exif info in caption 'disableCache' => false, // Enable or disable cache - 'showShareLink' => false // Show link for thumb, full, markdown link (thumb + link to full) + 'showShareLink' => false, // Show link for thumb, full, markdown link (thumb + link to full) + 'nbItemsAtom' => 25 // Number of item in atom feed ]; ``` @@ -129,12 +130,13 @@ $userConfig = [ | displayExifInfo | bool | false | Display Exif info in caption | | disableCache | bool | false | Enable or disable cache | | showShareLink | boll | false | Show share link for thumb, full, markdown link (thumb + link to full) | +| nbItemsAtom | int | 25 | Number of item in atom feed #### Notes If you change the thumbsize, thumbnails are not re-create. If the difference between older and newer size are small, I think you don't need regen all thumb. But if you prefer, you can delete `cache/thumbs/*` for force re-create thumbnails. -If you change any parameter, the cache file (html file) are delete and recreate. +If you change any parameter, the cache file (html file and atom file) are delete and recreate. ## Ideas @@ -145,12 +147,12 @@ If you have NextCloud installed on your server, you can use it to manage your Na In the NextCloud settings: Settings > Administration > External Storage: -| Option | Description | -| --- | --- | -| Folder Name | The name of the folder as it will appear in NextCloud. | -| External Storage | Choose Local | -| Authentication | None | -| Configuration | Enter the path where your images are stored (the path to the NanoGal /photos directory) | +| Option | Description | +| --- | --- | +| Folder Name | The name of the folder as it will appear in NextCloud. | +| External Storage | Choose Local | +| Authentication | None | +| Configuration | Enter the path where your images are stored (the path to the NanoGal /photos directory) | Don't forget to disable encrypt for this folder. diff --git a/app/Cache.php b/app/Cache.php index c6abac5..5814917 100644 --- a/app/Cache.php +++ b/app/Cache.php @@ -10,6 +10,7 @@ class Cache { private $counter = []; private $fileName = '../cache/cache.json'; private $fileCache; + static $atomFileName = '../cache/feed.atom'; private $currentConfig; private $saveConfig; @@ -101,7 +102,7 @@ class Cache { $this->fileCache[$this->currentDir] = -1; } - if ($this->counter[$this->currentDir] !== $this->fileCache[$this->currentDir]) { + if (!empty($this->counter[$this->currentDir]) && ($this->counter[$this->currentDir] !== $this->fileCache[$this->currentDir])) { $this->fileCache = array_merge($this->fileCache, $this->counter); return true; } @@ -172,4 +173,37 @@ class Cache { } } } + + /** + * Deleting the Atom feed cache file if exists. + * + * @return void + */ + static function clearAtomFeed(): void { + if (file_exists(self::$atomFileName)) { + unlink(self::$atomFileName); + } + } + + /** + * Retrieves the Atom feed cache content if file exists. + * + * @return string|false Returns the content of the Atom feed cache file, or false if the file does not exist. + */ + static function getAtomFeed(): string|false { + if (file_exists(self::$atomFileName)) { + return file_get_contents(self::$atomFileName); + } + return false; + } + + /** + * Writes content to the Atom feed cache file. + * + * @param string $content The content to be written to the Atom feed file. + * @return void + */ + static function setAtomFeed(string $content): void { + file_put_contents(self::$atomFileName, $content); + } } diff --git a/app/FileAndDir.php b/app/FileAndDir.php index 5012eba..e545153 100644 --- a/app/FileAndDir.php +++ b/app/FileAndDir.php @@ -199,6 +199,35 @@ class FileAndDir { } } + /** + * 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. * @@ -285,9 +314,16 @@ class FileAndDir { $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' => $this->currentDir . '/' . $imageName + 'filename' => $path ), '', '&' diff --git a/public/createthumb.php b/public/createthumb.php index b7a3a8e..2d42134 100644 --- a/public/createthumb.php +++ b/public/createthumb.php @@ -15,10 +15,10 @@ if (file_exists('../datas/config.php')) { $config = array_merge($config, $userConfig); } -$get_filename = $_GET['filename']; +$getFilename = $_GET['filename']; $baseDir = getcwd(); -if (!Utils::isPathAuthorized($get_filename)) { +if (!Utils::isPathAuthorized($getFilename)) { die("ERROR 01: Unauthorized access!"); } @@ -26,7 +26,7 @@ if (!is_dir('../cache/thumbs') && is_writable('.')) { mkdir('../cache/thumbs', 0700); } -$pathInfos = pathinfo($get_filename); +$pathInfos = pathinfo($getFilename); $dirname = '../cache/' . str_replace('photos', 'thumbs', $pathInfos['dirname']); $filename = $pathInfos['filename']; $thumbname = '../cache/' . $dirname . '/' . $filename . '.' . $pathInfos['extension'] . '.webp'; @@ -40,7 +40,7 @@ if (file_exists($thumbname)) { exit; } -if (!is_readable($get_filename) || !is_file($get_filename)) { +if (!is_readable($getFilename) || !is_file($getFilename)) { header('Content-type: image/svg+xml'); $cannotopenImg = file_get_contents('assets/images/cannotopen.svg'); echo $cannotopenImg; @@ -55,7 +55,7 @@ if (!file_exists($dirname)) { $image = new Zebra_Image(); $image->auto_handle_exif_orientation = true; -$image->source_path = $get_filename; +$image->source_path = $getFilename; $image->target_path = $thumbname; if (!$image->resize($config['thumbSize'], $config['thumbSize'], ZEBRA_IMAGE_CROP_CENTER)) { diff --git a/public/feed.php b/public/feed.php new file mode 100644 index 0000000..fea8a76 --- /dev/null +++ b/public/feed.php @@ -0,0 +1,84 @@ + 'board', // Template filename (must be placed in 'public/templates' folder) + 'title' => 'NanoGal', // Text to be displayed in browser titlebar + 'description' => 'My gallery', // Use in meta tag "description" + 'author' => 'NanoGal', // Your name + 'skipObjects' => ['comment.html', '.gitkeep', 'aFolder', 'aFile.ext'], //Those files and folders will not be displayed (affects the page and the RSS feed) + 'imageCaptionPosition' => 'right', // Position of caption in lightbox + 'sortBy' => 'name', // Sort by name or date + 'orderBy' => 'desc', // Order by asc or desc + 'thumbSize' => 250, // Thumbnail height/width (square thumbs) + 'displayExifInfo' => false, // Display Exif info in caption + 'disableCache' => false, // Enable or disable cache + 'showShareLink' => false, // Show link for thumb, full, markdown link (thumb + link to full) + 'nbItemsAtom' => 25 // Number of item in atom feed +]; + +if (file_exists('../datas/config.php')) { + include '../datas/config.php'; + $config = array_merge($config, $userConfig); +} + +header('Content-Type: text/xml'); + +if ($config['disableCache'] === false) { + $cachedFeed = Cache::getAtomFeed(); + if ($cachedFeed) { + echo $cachedFeed; + exit(); + } +} else { + Cache::clearAtomFeed(); +} + +$gallery_link = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST']; + +$fileList = new FileAndDir('', '/', $config); +$filterList = $fileList->makeMoreRecentFile(); + +$date = date_create(); +$updated = $date->format(DateTimeInterface::ATOM); + +$atom = ' + + ' . $config['title'] . ' + + + ' . $gallery_link . ' + assets/favicons/favicon-96x96.png + assets/favicons/favicon-310x310.png + NanoGal + + ' . $config['author'] . ' + + ' . $updated . ''; + +foreach ($filterList as $value) { + + $atom .= ' + + ' . basename($value['url']) . ' + + ' . $value['url'] . ' + ' . date(DATE_ATOM, $value['tdate']) . ' + ' . date(DATE_ATOM, $value['tdate']) . ' + + + ]]> + + '; +} +$atom .= ""; +if ($config['disableCache'] === false) { + Cache::setAtomFeed($atom); +} +echo $atom; diff --git a/public/index.php b/public/index.php index 427c578..60bafcf 100644 --- a/public/index.php +++ b/public/index.php @@ -1,4 +1,5 @@ 'board', // Template filename (must be placed in 'templates' folder) + 'templateFile' => 'board', // Template filename (must be placed in 'public/templates' folder) 'title' => 'NanoGal', // Text to be displayed in browser titlebar 'description' => 'My gallery', // Use in meta tag "description" 'author' => 'NanoGal', // Your name @@ -26,9 +27,11 @@ $userConfig = [ 'imageCaptionPosition' => 'right', // Position of caption in lightbox 'sortBy' => 'name', // Sort by name or date 'orderBy' => 'desc', // Order by asc or desc + 'thumbSize' => 250, // Thumbnail height/width (square thumbs) 'displayExifInfo' => false, // Display Exif info in caption - 'thumbSize' => 300, // Thumbnail height/width (square thumbs) 'disableCache' => false, // Enable or disable cache + 'showShareLink' => false, // Show link for thumb, full, markdown link (thumb + link to full) + 'nbItemsAtom' => 25 // Number of item in atom feed ]; */ $config = [ @@ -43,7 +46,8 @@ $config = [ 'thumbSize' => 250, // Thumbnail height/width (square thumbs) 'displayExifInfo' => false, // Display Exif info in caption 'disableCache' => false, // Enable or disable cache - 'showShareLink' => false // Show link for thumb, full, markdown link (thumb + link to full) + 'showShareLink' => false, // Show link for thumb, full, markdown link (thumb + link to full) + 'nbItemsAtom' => 25 // Number of item in atom feed ]; if (file_exists('../datas/config.php')) { @@ -55,7 +59,7 @@ $messages = ''; if (!function_exists('exif_read_data') && $config['displayExifInfo'] === true) { $display_exif = 0; - $messages = "Error: PHP EXIF is not available. Set $display_exif = 0; in config.php to remove this message"; + $messages = "Error: PHP EXIF is not available. Set $display_exif = 0; in config.php to remove this message"; } $requestedDir = ''; @@ -73,12 +77,15 @@ if ($config['disableCache'] === false) { } if ($cache->changeFile() || $cache->changeConf() || !file_exists($cacheHash . '.html') || $config['disableCache'] === true) { - if ($cache->changeConf()) { $cache->clearCache(); } - if ($config['disableCache'] !== true) { + if($cache->changeFile() || $cache->changeConf()){ + Cache::clearAtomFeed(); + } + + if ($config['disableCache'] === false) { $cache->save(); } @@ -90,12 +97,12 @@ if ($cache->changeFile() || $cache->changeConf() || !file_exists($cacheHash . '. ob_start(); $userCss = ''; - // If user set personal css rule, load it + if (file_exists('../datas/user.css')) { - $userCss = ''; + $userCss = ''; } - // If template exist load it or load default template + if (file_exists('templates/' . $config['templateFile'] . '/' . $config['templateFile'] . '.php')) { require 'templates/' . $config['templateFile'] . '/' . $config['templateFile'] . '.php'; } else { diff --git a/public/templates/board/board.php b/public/templates/board/board.php index 1ba3757..51e155c 100644 --- a/public/templates/board/board.php +++ b/public/templates/board/board.php @@ -7,11 +7,17 @@ use App\FileAndDir; + <?= $config['title']; ?> + - <?= $config['title']; ?> + + + + + @@ -26,9 +32,9 @@ use App\FileAndDir; - - - + + +