diff --git a/index.php b/index.php
index 03d8371..8af58f6 100644
--- a/index.php
+++ b/index.php
@@ -39,6 +39,7 @@ $routes = [
//'/profile/([a-zA-Z0-9_-]+)' => 'profile.php',
'/conv/([a-zA-Z0-9]{7})' => 'conv.php', // matches only twtHash of exactly 7 alphanumeric characters
'/post/([a-zA-Z0-9]{7})' => 'post.php', // matches only twtHash of exactly 7 alphanumeric characters
+ //'/thumb' => 'thumb.php',
'/webmention' => 'webmention_endpoint.php',
];
diff --git a/libs/Cropper.php b/libs/Cropper.php
new file mode 100644
index 0000000..59ed6e0
--- /dev/null
+++ b/libs/Cropper.php
@@ -0,0 +1,297 @@
+
+ * @package CoffeeCode\Cropper
+ */
+class Cropper
+{
+ /** @var string */
+ private string $cachePath;
+
+ /** @var string */
+ private string $imagePath;
+
+ /** @var string */
+ private string $imageName;
+
+ /** @var string */
+ private string $imageMime;
+
+ /** @var int */
+ private int $quality;
+
+ /** @var int */
+ private int $compressor;
+
+ /**@var bool */
+ private bool $webP;
+
+ /**
+ * Allow jpg and png to thumb and cache generate
+ * @var array allowed media types
+ */
+ private static array $allowedExt = ['image/jpeg', "image/png"];
+
+ /** @var ConversionFailedException */
+ public ConversionFailedException $exception;
+
+ /**
+ * Cropper constructor.
+ * compressor must be 1-9
+ * quality must be 1-100
+ *
+ * @param string $cachePath
+ * @param int $quality
+ * @param int $compressor
+ * @param bool $webP
+ * @throws Exception
+ */
+ public function __construct(string $cachePath, int $quality = 75, int $compressor = 5, bool $webP = false)
+ {
+ $this->cachePath = $cachePath;
+ $this->quality = $quality;
+ $this->compressor = $compressor;
+ $this->webP = $webP;
+
+ if (!file_exists($this->cachePath) || !is_dir($this->cachePath)) {
+ if (!mkdir($this->cachePath, 0755, true)) {
+ throw new Exception("Could not create cache folder");
+ }
+ }
+ }
+
+ /**
+ * Make an thumb image
+ *
+ * @param string $imagePath
+ * @param int $width
+ * @param int|null $height
+ * @return null|string
+ */
+ public function make(string $imagePath, int $width, int $height = null): ?string
+ {
+ if (!file_exists($imagePath)) {
+ return "Image not found";
+ }
+
+ $this->imagePath = $imagePath;
+ $this->imageName = $this->name($this->imagePath, $width, $height);
+ $this->imageMime = mime_content_type($this->imagePath);
+
+ if (!in_array($this->imageMime, self::$allowedExt)) {
+ return "Not a valid JPG or PNG image";
+ }
+
+ return $this->image($width, $height);
+ }
+
+ /**
+ * @param int $width
+ * @param int|null $height
+ * @return string|null
+ */
+ private function image(int $width, int $height = null): ?string
+ {
+ $imageWebP = "{$this->cachePath}/{$this->imageName}.webp";
+ $imageExt = "{$this->cachePath}/{$this->imageName}." . pathinfo($this->imagePath)['extension'];
+
+ if ($this->webP && file_exists($imageWebP) && is_file($imageWebP)) {
+ return $imageWebP;
+ }
+
+ if (file_exists($imageExt) && is_file($imageExt)) {
+ return $imageExt;
+ }
+
+ return $this->imageCache($width, $height);
+ }
+
+ /**
+ * @param string $name
+ * @param int|null $width
+ * @param int|null $height
+ * @return string
+ */
+ protected function name(string $name, int $width = null, int $height = null): string
+ {
+ $filterName = mb_convert_encoding(htmlspecialchars(mb_strtolower(pathinfo($name)["filename"])), 'ISO-8859-1',
+ 'UTF-8');
+ $formats = mb_convert_encoding('ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜüÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûýýþÿRr"!@#$%&*()_-+={[}]/?;:.,\\\'<>°ºª',
+ 'ISO-8859-1', 'UTF-8');
+ $replace = 'aaaaaaaceeeeiiiidnoooooouuuuuybsaaaaaaaceeeeiiiidnoooooouuuyybyrr ';
+ $trimName = trim(strtr($filterName, $formats, $replace));
+ $name = str_replace(["-----", "----", "---", "--"], "-", str_replace(" ", "-", $trimName));
+
+ $hash = $this->hash($this->imagePath);
+ $widthName = ($width ? "-{$width}" : "");
+ $heightName = ($height ? "x{$height}" : "");
+
+ return "{$name}{$widthName}{$heightName}-{$hash}";
+ }
+
+ /**
+ * @param string $path
+ * @return string
+ */
+ protected function hash(string $path): string
+ {
+ return hash("crc32", pathinfo($path)['basename']);
+ }
+
+ /**
+ * Clear cache
+ *
+ * @param string|null $imagePath
+ * @example $t->flush("images/image.jpg"); clear image name and variations size
+ * @example $t->flush(); clear all image cache folder
+ */
+ public function flush(string $imagePath = null): void
+ {
+ foreach (scandir($this->cachePath) as $file) {
+ $file = "{$this->cachePath}/{$file}";
+ if ($imagePath && strpos($file, $this->hash($imagePath))) {
+ $this->imageDestroy($file);
+ } elseif (!$imagePath) {
+ $this->imageDestroy($file);
+ }
+ }
+ }
+
+ /**
+ * @param int $width
+ * @param int|null $height
+ * @return null|string
+ */
+ private function imageCache(int $width, int $height = null): ?string
+ {
+ list($src_w, $src_h) = getimagesize($this->imagePath);
+ $height = ($height ?? ($width * $src_h) / $src_w);
+
+ $src_x = 0;
+ $src_y = 0;
+
+ $cmp_x = $src_w / $width;
+ $cmp_y = $src_h / $height;
+
+ if ($cmp_x > $cmp_y) {
+ $src_x = round(($src_w - ($src_w / $cmp_x * $cmp_y)) / 2);
+ $src_w = round($src_w / $cmp_x * $cmp_y);
+ } elseif ($cmp_y > $cmp_x) {
+ $src_y = round(($src_h - ($src_h / $cmp_y * $cmp_x)) / 2);
+ $src_h = round($src_h / $cmp_y * $cmp_x);
+ }
+
+ $height = (int)$height;
+ $src_x = (int)$src_x;
+ $src_y = (int)$src_y;
+ $src_w = (int)$src_w;
+ $src_h = (int)$src_h;
+
+ if ($this->imageMime == "image/jpeg") {
+ return $this->fromJpg($width, $height, $src_x, $src_y, $src_w, $src_h);
+ }
+
+ if ($this->imageMime == "image/png") {
+ return $this->fromPng($width, $height, $src_x, $src_y, $src_w, $src_h);
+ }
+
+ return null;
+ }
+
+ /**
+ * @param string $imagePatch
+ */
+ private function imageDestroy(string $imagePatch): void
+ {
+ if (file_exists($imagePatch) && is_file($imagePatch)) {
+ unlink($imagePatch);
+ }
+ }
+
+ /**
+ * @param int $width
+ * @param int $height
+ * @param int $src_x
+ * @param int $src_y
+ * @param int $src_w
+ * @param int $src_h
+ * @return string
+ */
+ private function fromJpg(int $width, int $height, int $src_x, int $src_y, int $src_w, int $src_h): string
+ {
+ $thumb = imagecreatetruecolor($width, $height);
+ $source = imagecreatefromjpeg($this->imagePath);
+
+ imagecopyresampled($thumb, $source, 0, 0, $src_x, $src_y, $width, $height, $src_w, $src_h);
+ imagejpeg($thumb, "{$this->cachePath}/{$this->imageName}.jpg", $this->quality);
+
+ imagedestroy($thumb);
+ imagedestroy($source);
+
+ if ($this->webP) {
+ return $this->toWebP("{$this->cachePath}/{$this->imageName}.jpg");
+ }
+
+ return "{$this->cachePath}/{$this->imageName}.jpg";
+ }
+
+ /**
+ * @param int $width
+ * @param int $height
+ * @param int $src_x
+ * @param int $src_y
+ * @param int $src_w
+ * @param int $src_h
+ * @return string
+ */
+ private function fromPng(int $width, int $height, int $src_x, int $src_y, int $src_w, int $src_h): string
+ {
+ $thumb = imagecreatetruecolor($width, $height);
+ $source = imagecreatefrompng($this->imagePath);
+
+ imagealphablending($thumb, false);
+ imagesavealpha($thumb, true);
+ imagecopyresampled($thumb, $source, 0, 0, $src_x, $src_y, $width, $height, $src_w, $src_h);
+ imagepng($thumb, "{$this->cachePath}/{$this->imageName}.png", $this->compressor);
+
+ imagedestroy($thumb);
+ imagedestroy($source);
+
+ if ($this->webP) {
+ return $this->toWebP("{$this->cachePath}/{$this->imageName}.png");
+ }
+
+ return "{$this->cachePath}/{$this->imageName}.png";
+ }
+
+ /**
+ * @param string $image
+ * @param bool $unlinkImage
+ * @return string
+ */
+ public function toWebP(string $image, $unlinkImage = true): string
+ {
+ try {
+ $webPConverted = pathinfo($image)["dirname"] . "/" . pathinfo($image)["filename"] . ".webp";
+ WebPConvert::convert($image, $webPConverted, ["default-quality" => $this->quality]);
+
+ if ($unlinkImage) {
+ unlink($image);
+ }
+
+ return $webPConverted;
+ } catch (ConversionFailedException $exception) {
+ $this->exception = $exception;
+ return $image;
+ }
+ }
+}
diff --git a/libs/Slimdown.php b/libs/Slimdown.php
index 6e2c8d2..60b2080 100644
--- a/libs/Slimdown.php
+++ b/libs/Slimdown.php
@@ -115,6 +115,7 @@ class Slimdown {
// Substitute _ and * in links so they don't break the URLs
$link = str_replace (['_', '*'], ['{^^^}', '{~~~}'], $link);
return sprintf ('
', $link, $text);
+ //return sprintf ('
', $link, $text); // added support for thumbnail generation on the fly
}
private static function fix_link ($regs) {
diff --git a/style.css b/style.css
index 8154c09..d00853c 100644
--- a/style.css
+++ b/style.css
@@ -256,6 +256,11 @@ nav.pagnation {
/* === REFRESH === */
+progress,
+progress::-moz-progress-bar {
+/* border: revert;*/
+}
+
#refreshInfo {
display: block;
text-align: center:
@@ -297,4 +302,4 @@ footer {
border-top: thin solid grey;
margin-top: 1rem;
text-align: center;
-}
\ No newline at end of file
+}
diff --git a/libs/__Thumbnail.php b/views/__Thumbnail.php
similarity index 95%
rename from libs/__Thumbnail.php
rename to views/__Thumbnail.php
index bcef53a..38f537f 100644
--- a/libs/__Thumbnail.php
+++ b/views/__Thumbnail.php
@@ -42,15 +42,16 @@ function createThumbnail($src, $dest, $thumbHeight) {
return true;
}
-$galleryDir = "gallery/";
-$thumbDir = "thumb/";
-$allowedExtensions = array("jpg", "jpeg", "png", "gif", "webp"); // Remove "mp4" from allowed extensions
+// $galleryDir = "gallery/";
+$thumbDir = "private/thumb/";
+$allowedExtensions = array("jpg", "jpeg", "png", "gif", "webp");
// Check and create the "thumb" directory if it doesn't exist
if (!file_exists($thumbDir)) {
mkdir($thumbDir);
}
+/*
$galleryFiles = scandir($galleryDir);
foreach ($galleryFiles as $file) {
@@ -75,4 +76,4 @@ foreach ($galleryFiles as $file) {
}
}
-?>
\ No newline at end of file
+*/
diff --git a/views/__thumb.php b/views/__thumb.php
new file mode 100644
index 0000000..ce6fba0
--- /dev/null
+++ b/views/__thumb.php
@@ -0,0 +1,211 @@
+
+ *
+ * @see https://gist.github.com/dmkuznetsov/f0038b578c1cb6ccd44d
+ * @license MIT
+ */
+define('DEFAULT_WIDTH', 300);
+define('DEFAULT_HEIGHT', 300);
+define('DEFAULT_QUALITY', 100);
+define('CACHE_DIR', rtrim(sys_get_temp_dir(), '/') . '/thumbnails/');
+define('DOCUMENT_ROOT', $_SERVER['DOCUMENT_ROOT']);
+
+//if (!isset($_GET['image'], $_GET['width'], $_GET['height'])) {
+if (!isset($_GET['image'])) {
+ header('HTTP/1.1 400 Bad Request');
+ //echo 'Error: no image/width/height was specified';
+ echo 'Error: no image was specified';
+ exit();
+}
+
+// Made width and height optional and define their defaults.
+// by: sp@darch.dk (with help from chatGPT, 2024-02-03)
+if (!isset($_GET['width'], $_GET['height'])) {
+ $resizeWidth = $width = DEFAULT_WIDTH;
+ $resizeHeight = $height = DEFAULT_HEIGHT;
+} else {
+ $resizeWidth = $width = (int)$_GET['width'];
+ $resizeHeight = $height = (int)$_GET['height'];
+}
+
+$quality = (isset($_GET['quality'])) ? (int)$_GET['quality'] : DEFAULT_QUALITY;
+$nocache = isset($_GET['nocache']);
+
+if (!is_dir(CACHE_DIR)) {
+ mkdir(CACHE_DIR, 0755, true);
+}
+
+// Make sure we can read and write the cache directory
+if (!is_readable(CACHE_DIR)) {
+ header('HTTP/1.1 500 Internal Server Error');
+ echo 'Error: the cache directory is not readable';
+ exit();
+} else if (!is_writable(CACHE_DIR)) {
+ header('HTTP/1.1 500 Internal Server Error');
+ echo 'Error: the cache directory is not writable';
+ exit();
+}
+
+$originalImageSource = (string)$_GET['image'];
+$image = getImagePath($originalImageSource);
+
+if ($image[0] != '/' || strpos(dirname($image), ':') || preg_match('/(\.\.|<|>)/', $image)) {
+ header('HTTP/1.1 400 Bad Request');
+ echo 'Error: malformed image path. Image paths must begin with \'/\'';
+ exit();
+}
+
+if (!$image || !file_exists($image)) {
+ header('HTTP/1.1 404 Not Found');
+ echo 'Error: image does not exist: ' . $image;
+ exit();
+}
+
+$size = getimagesize($image);
+$mime = $size['mime'];
+
+if (substr($mime, 0, 6) != 'image/') {
+ header('HTTP/1.1 400 Bad Request');
+ echo 'Error: requested file is not an accepted type: ' . $image;
+ exit();
+}
+
+$originalWidth = $size[0];
+$originalHeight = $size[1];
+$thumbHash = md5($image . $width . $height . $quality);
+$thumbPath = CACHE_DIR . $thumbHash;
+
+if (!$nocache && file_exists($thumbPath)) {
+ $imageModified = filemtime($image);
+ $thumbModified = filemtime($thumbPath);
+ if ($imageModified < $thumbModified) {
+ $data = file_get_contents($thumbPath);
+ findBrowserCache(md5($data), gmdate('D, d M Y H:i:s', $thumbModified) . ' GMT');
+ header("Content-type: $mime");
+ header('Content-Length: ' . strlen($data));
+ echo $data;
+ exit();
+ }
+}
+
+$saveAlphaChannel = false;
+$imageCreationFunction = 'imagecreatefromjpeg';
+$imageOutputFunction = 'imagejpeg';
+$dst = imagecreatetruecolor($width, $height);
+switch ($size['mime']) {
+ case 'image/gif':
+ $imageCreationFunction = 'imagecreatefromgif';
+ $imageOutputFunction = 'imagepng';
+ $quality = round(10 - ($quality / 10));
+ $saveAlphaChannel = true;
+ break;
+ case 'image/x-png':
+ case 'image/png':
+ $imageCreationFunction = 'imagecreatefrompng';
+ $imageOutputFunction = 'imagepng';
+ $quality = round(10 - ($quality / 10));
+ $saveAlphaChannel = true;
+ break;
+}
+$src = $imageCreationFunction($image);
+
+if ($saveAlphaChannel) {
+ imagealphablending($dst, false);
+ imagesavealpha($dst, true);
+}
+
+$ratio = $originalWidth / $originalHeight;
+if ($width / $height <= $ratio) {
+ $resizeWidth = $height * $ratio;
+} else {
+ $resizeHeight = $width / $ratio;
+}
+
+// This is where the image is being resized
+imagecopyresampled($dst, $src, 0, 0, 0, 0, $resizeWidth, $resizeHeight, $originalWidth, $originalHeight);
+
+$imageOutputFunction($dst, $thumbPath, $quality);
+ob_start();
+$imageOutputFunction($dst, null, $quality);
+$data = ob_get_contents();
+ob_end_clean();
+
+imagedestroy($src);
+imagedestroy($dst);
+
+if ($nocache && file_exists($image)) {
+ unlink($image);
+}
+
+findBrowserCache(md5($data), gmdate('D, d M Y H:i:s', filemtime($thumbPath)) . ' GMT');
+header("Content-type: $mime");
+header('Content-Length: ' . strlen($data));
+echo $data;
+exit;
+
+function getImagePath($image)
+{
+ if (!filter_var($image, FILTER_VALIDATE_URL)) {
+ $image = DOCUMENT_ROOT . '/' . ltrim($image, '/');
+ } else {
+ $ext = pathinfo(preg_replace('/^(s?f|ht)tps?:\/\/[^\/]+/i', '', $image), PATHINFO_EXTENSION);
+ $tmpImagePath = CACHE_DIR . md5($image);
+ if ($ext) {
+ $tmpImagePath .= '.' . $ext;
+ }
+ if (!file_exists($tmpImagePath)) {
+ $imageContent = file_get_contents($image);
+ if (!$imageContent) {
+ header('HTTP/1.1 400 Bad Request');
+ echo 'Error: can not download image';
+ exit();
+ } else {
+ file_put_contents($tmpImagePath, $imageContent);
+ }
+ }
+ $image = $tmpImagePath;
+ }
+ return $image;
+}
+
+
+function findBrowserCache($tag, $lastModified)
+{
+ header("Last-Modified: $lastModified");
+ header("ETag: \"{$tag}\"");
+
+ $ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
+ stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) :
+ false;
+
+ $ifModifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
+ stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :
+ false;
+
+ if (!$ifModifiedSince && !$ifNoneMatch) {
+ return;
+ }
+
+ if ($ifNoneMatch && $ifNoneMatch != $tag && $ifNoneMatch != '"' . $tag . '"') {
+ return;
+ }
+
+ if ($ifModifiedSince && $ifModifiedSince != $lastModified) {
+ return;
+ }
+
+ header('HTTP/1.1 304 Not Modified');
+ exit();
+}
\ No newline at end of file
diff --git a/libs/thumb.php b/views/__thumb_dm.php
similarity index 100%
rename from libs/thumb.php
rename to views/__thumb_dm.php
diff --git a/views/gallery.php b/views/gallery.php
index b209712..5c4130c 100644
--- a/views/gallery.php
+++ b/views/gallery.php
@@ -1,9 +1,15 @@
@@ -41,6 +47,42 @@ foreach ($twts as $twt) {
foreach ($img_array as $img) {
echo ''.$img[0].'';
}
+
+/*
+ $doc = new DOMDocument();
+ $doc->loadHTML($twt->content);
+ $img_array = $doc->getElementsByTagName('img');
+
+ foreach ($img_array as $img) {
+ $url = $img->getAttribute('src');
+ $alt = $img->getAttribute('alt');
+ //echo "
Title:" . $img->getAttribute('title');
+
+ echo "
";
+ }
+
+/*
+ foreach ($img_array as $img) {
+ echo $img[0];
+ $extension = pathinfo($img, PATHINFO_EXTENSION);
+ if (in_array(strtolower($extension), $allowedExtensions)) {
+ $imageURL = $galleryDir . $img;
+ $thumbURL = $thumbDir . $img;
+
+ if (!file_exists($thumbURL)) {
+ createThumbnail($imageURL, $thumbURL, 200); // The thumbnail will have a height of 200 pixels.
+ }
+
+ echo '
+
+ ';
+ }
+
+
+ }
+*/
+
+
}
?>