1
0
mirror of https://github.com/typemill/typemill.git synced 2025-07-26 00:31:38 +02:00

Finish image resize and grayscale in assets

This commit is contained in:
trendschau
2023-08-29 21:36:21 +02:00
parent 022abe132b
commit 38c59f9039
15 changed files with 282 additions and 194 deletions

View File

@@ -1 +1 @@
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","[:contactform :]","----","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.\n* ToDo: Customfields not styled yet.\n* ToDo: Warn if open another block\n* ToDo: finish youtube component","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes\n* ToDo: fix error messages\n* ToDo: Wrong frontend navigation if unpublished pages","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* Handle formdata centrally ???\n* Image generation on the fly\n* Markdown secure rendering\n* Responsive design\n* Captcha integration\n* Fix error api systemnavi\n* Reference feature\n* Typemill Utilities\n* Clear cache\n* Show security Log\n* User search only for +10 users","## Cleanups:","* DONE: Events\n* Error messages\n* Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more "]
["# ToDos Version 2","[TOC]","## System settings","* DONE: Migrate from backend to frontend with vue and api\n* DONE: Redesign\n* DONE: License feature\n* DONE: Enhance with plugins","[:contactform :]","----","## Visual Editor","* DONE: Refactor and redesign\n* DONE: Fix toc component in new block\n* DONE: Fix hr component in new block\n* DONE: finish shortcode component\n* DONE: Fix inline formats\n* DONE: fix lenght of page\n* DONE: Fix design of new block at the end (background color)\n* DONE: Move Block\n* DONE: Fix headline design\n* DONE: Fix save on two enter\n* DONE: fix quote design\n* DONE: Fix toc preview\n* DONE: disable enable \n* DONE: Add load sign (from navigation)\n* DONE: File is not published from tmp to media\/files if you save the block.\n* ToDo: Customfields not styled yet.\n* ToDo: Warn if open another block\n* ToDo: finish youtube component","## Raw Editor","* DONE: Refactor and redesign\n* DONE: Integrate highlighting","## Navigation","* DONE: Refactor and redesign\n* DONE: fix status in navigation\n* DONE: refresh navigation after changes\n* ToDo: fix error messages\n* ToDo: Wrong frontend navigation if unpublished pages","## Publish Controller","* DONE: Refactor and redesign\n* DONE: Create \n* DONE: publish\n* DONE: unpublish\n* DONE: discard\n* DONE: delete\n* DONE: save draft\n* DONE: switch to raw","## Meta Tabs","* DONE: Refactor and redesign\n* DONE: Enhance with plugins","## Medialib","* DONE: Refactor and redesign","## Posts","* DONE: Refactor and redesign","## Plugins","* Asset Class in progress","## Frontend","* DONE: Refactor\n* DONE: Test restrictions","## Other big tasks","* DONE: System setup\n* DONE: Recover Password","## Medium tasks","* DONE: Merge processAssets modell\n* DONE: Table of content duplicated for published pages\n* DONE: Session handling: csrf fail and session start error if restrictions are active\n* DONE: Image and files for meta","## Open tasks","* * * DONE: Sitemap and ping\n* DONE: Version check\n* DONE: Proxy support\n* DONE: SVG checker: https:\/\/github.com\/TribalSystems\/SVG-Sanitizer\n* DONE: Backend form builder\n* Handle formdata centrally ???\n* DONE: Image generation on the fly\n* Markdown secure rendering\n* Responsive design\n* Captcha integration\n* Fix error api systemnavi\n* Bug: Delete folder in base level\n* Reference feature\n* Typemill Utilities\n* Clear cache\n* Show security Log\n* User search only for +10 users","## Cleanups:","* DONE: Events\n* Error messages\n* Translations","## Info: Select userroles","* Userroles for file restriction: in vue-blox-components loaded via api\n* Userroles for userfields: in php model user getUserFields()\n* Userroles for meta: in php controller apiAuthorMeta getMeta()\n* Plugins and themes: in php model extension getThemeDefinitions()","## Info: License Check","* On activation in apiControllerExtension. It checks the license in yaml.\n* In plugin php code with setPremiumLicense\n* In static plugins, it checks manual premium list and method setPremiumLicense and more ","## Plugins","* MAKER: Rebuild search\n* MAKER: Rebuild contactform with shortcode"]

View File

@@ -0,0 +1 @@
["# blog","Content"]

View File

@@ -0,0 +1,20 @@
meta:
navtitle: blog
title: blog
description: Content
heroimage: null
heroimagealt: null
owner: Sebastian
author: null
allowedrole: null
alloweduser: null
manualdate: null
modified: '2023-08-25'
created: '2023-08-25'
time: 20-03-01
reference: null
referencetype: null
hide: false
noindex: false
contains: posts
glossary: null

File diff suppressed because one or more lines are too long

View File

@@ -70,3 +70,9 @@
noindex: false
path: /01-cyanine-theme/03-content-elements.md
keyPath: '1.3'
/blog:
navtitle: blog
hide: false
noindex: false
path: /02-blog
keyPath: 2

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -2,7 +2,9 @@
namespace Typemill;
use Typemill\Models\ProcessImage;
use Typemill\Models\Media;
use Typemill\Models\Meta;
use Typemill\Models\StorageWrapper;
# this class is available to the container and to all plugins
class Assets
@@ -22,7 +24,7 @@ class Assets
$this->svgSymbols = array();
$this->meta = array();
$this->imageUrl = false;
$this->imageFolder = 'original';
$this->imageFolder = 'originalFolder';
}
public function setUri($uri)
@@ -35,117 +37,6 @@ class Assets
$this->baseUrl = $baseUrl;
}
public function image($url)
{
$this->imageUrl = $url;
return $this;
}
public function resize($width,$height)
{
$pathinfo = pathinfo($this->imageUrl);
$extension = strtolower($pathinfo['extension']);
$imageName = $pathinfo['filename'];
$desiredSizes = ['custom' => []];
$resize = '-';
if(is_int($width) && $width < 10000)
{
$resize .= $width;
$desiredSizes['custom']['width'] = $width;
}
$resize .= 'x';
if(is_int($height) && $height < 10000)
{
$resize .= $height;
$desiredSizes['custom']['height'] = $height;
}
$processImage = new ProcessImage($desiredSizes);
$processImage->checkFolders('images');
$imageNameResized = $imageName . $resize;
$imagePathResized = $processImage->customFolder . $imageNameResized . '.' . $extension;
$imageUrlResized = 'media/custom/' . $imageNameResized . '.' . $extension;
if(!file_exists( $imagePathResized ))
{
# if custom version does not exist, use original version for resizing
$imageFolder = ($this->imageFolder == 'original') ? $processImage->originalFolder : $processImage->customFolder;
$imagePath = $imageFolder . $pathinfo['basename'];
$resizedImage = $processImage->generateSizesFromImageFile($imageUrlResized, $imagePath);
$savedImage = $processImage->saveImage($processImage->customFolder, $resizedImage['custom'], $imageNameResized, $extension);
if(!$savedImage)
{
# return old image url without resize
return $this;
}
}
# set folder to custom, so that the next method uses the correct (resized) version
$this->imageFolder = 'custom';
$this->imageUrl = $imageUrlResized;
return $this;
}
public function grayscale()
{
$pathinfo = pathinfo($this->imageUrl);
$extension = strtolower($pathinfo['extension']);
$imageName = $pathinfo['filename'];
$processImage = new ProcessImage([]);
$processImage->checkFolders('images');
$imageNameGrayscale = $imageName . '-grayscale';
$imagePathGrayscale = $processImage->customFolder . $imageNameGrayscale . '.' . $extension;
$imageUrlGrayscale = 'media/custom/' . $imageNameGrayscale . '.' . $extension;
if(!file_exists( $imagePathGrayscale ))
{
# if custom-version does not exist, use live-version for grayscale-manipulation.
$imageFolder = ($this->imageFolder == 'original') ? $processImage->liveFolder : $processImage->customFolder;
$imagePath = $imageFolder . $pathinfo['basename'];
$grayscaleImage = $processImage->grayscale($imagePath, $extension);
$savedImage = $processImage->saveImage($processImage->customFolder, $grayscaleImage, $imageNameGrayscale, $extension);
if(!$savedImage)
{
# return old image url without resize
return $this;
}
}
# set folder to custom, so that the next method uses the correct (resized) version
$this->imageFolder = 'custom';
$this->imageUrl = $imageUrlGrayscale;
return $this;
}
public function src()
{
# when we finish it, we shoud reset all settings
$imagePath = $this->baseUrl . '/' . $this->imageUrl;
$this->imageUrl = false;
$this->imageFolder = 'original';
return $imagePath;
}
public function addCSS($CSS)
{
$CSSfile = $this->getFileUrl($CSS);
@@ -237,11 +128,6 @@ class Assets
}
}
public function addMeta($key,$meta)
{
$this->meta[$key] = $meta;
}
public function renderEditorJS()
{
return implode("\n", $this->editorJS) . implode("\n", $this->editorInlineJS);
@@ -267,21 +153,6 @@ class Assets
return implode('', $this->svgSymbols);
}
public function renderMeta()
{
$metaLines = '';
foreach($this->meta as $meta)
{
$metaLines .= "\n";
$metaLines .= $meta;
}
return $metaLines;
}
/**
* Checks, if a string is a valid internal or external ressource like js-file or css-file
* @params $path string
* @return string or false
*/
public function getFileUrl($path)
{
# check system path of file without parameter for fingerprinting
@@ -301,4 +172,63 @@ class Assets
return false;
}
/**********************
* META FEATURES *
**********************/
public function addMeta($key,$meta)
{
$this->meta[$key] = $meta;
}
public function renderMeta()
{
$metaLines = '';
foreach($this->meta as $meta)
{
$metaLines .= "\n";
$metaLines .= $meta;
}
return $metaLines;
}
/**********************
* IMAGE MANIPULATION *
**********************/
public function image($url)
{
# image url is passed with twig-function
$this->imageUrl = $url;
$this->media = new Media();
return $this;
}
public function resize($width, $height)
{
$this->imageUrl = $this->media->createCustomSize($this->imageUrl, $width, $height);
return $this;
}
public function grayscale()
{
$this->imageUrl = $this->media->createGrayscale($this->imageUrl);
return $this;
}
public function src()
{
# create absolute image url
$absImageUrl = $this->baseUrl . '/' . $this->imageUrl;
# reset image url
$this->imageUrl = false;
return $absImageUrl;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Typemill\Extensions;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Typemill\Models\Meta;
class TwigMetaExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('getPageMeta', array($this, 'getMeta' ))
];
}
public function getMeta($settings, $item)
{
$meta = new Meta();
$metadata = $meta->getMetaData($item);
if(!$metadata OR $metadata['meta']['title'] == '' OR $metadata['meta']['description'] == '')
{
$metadata = $meta->addMetaDefaults($metadata, $item, $settings['author']);
}
return $metadata;
}
}

View File

@@ -3,6 +3,7 @@
namespace Typemill\Models;
use Typemill\Models\Folder;
use Typemill\Models\StorageWrapper;
use Typemill\Models\SvgSanitizer;
use Typemill\Static\Slug;
@@ -444,13 +445,14 @@ class Media
imagesavealpha($resizedImage, true);
}
imagecopyresampled($resizedImage, $image, 0, 0, $x, $y, $desired['width'], $desired['height'], $w, $h);
imagecopyresampled($resizedImage, $image, 0, 0, intval($x), intval($y), $desired['width'], $desired['height'], intval($w), intval($h));
return $resizedImage;
}
public function saveResizedImage($resizedImage, string $destinationfolder, string $extension)
{
# use method in storage class???
$destinationfolder = strtoupper($destinationfolder);
switch($extension)
@@ -484,6 +486,128 @@ class Media
return true;
}
public function createCustomSize($imageUrl, $width = NULL, $height = NULL)
{
$this->setPathInfo($imageUrl);
$resizeName = '-';
if(is_int($width) && $width < 10000)
{
$resizeName .= $width;
$desiredSize['width'] = $width;
}
$resizeName .= 'x';
if(is_int($height) && $height < 10000)
{
$resizeName .= $height;
$desiredSize['height'] = $height;
}
$extension = $this->getExtension();
$originalName = $this->getFilename();
$originalFile = $originalName . '.' . $extension;
$customName = $originalName . $resizeName;
$customFile = $customName . '.' . $extension;
$storage = new StorageWrapper('\Typemill\Models\Storage');
if($storage->checkFile('customFolder', '', $customFile))
{
# we should get the custom folder url dynamically from storage class
return '/media/custom/' . $customFile;
}
# if name is in customfolder (resized already)
if($storage->checkFile('customFolder', '', $originalFile))
{
$imagePath = $storage->getFolderPath('customFolder') . $originalFile;
}
# or in originalfolder (not resized yet)
elseif($storage->checkFile('originalFolder', '', $originalFile))
{
$imagePath = $storage->getFolderPath('originalFolder') . $originalFile;
}
else
{
return 'image not found';
}
$image = $this->createImageFromPath($imagePath, $extension);
$originalSize = $this->getImageSize($image);
$resizedImage = $this->resizeImage($image, $desiredSize, $originalSize);
if($resizedImage && $storage->storeCustomImage($image, $extension, $customName))
{
return '/media/custom/' . $customFile;
}
return 'error resizing image';
}
public function createGrayscale($imageUrl)
{
$this->setPathInfo($imageUrl);
$extension = $this->getExtension();
$originalName = $this->getFilename();
$originalFile = $originalName . '.' . $extension;
$customName = $originalName . '-grayscale';
$customFile = $customName . '.' . $extension;
$storage = new StorageWrapper('\Typemill\Models\Storage');
# if the grayscaled image is there already
if($storage->checkFile('customFolder', '', $customFile))
{
# we should get the custom folder url dynamically from storage class
return '/media/custom/' . $customFile;
}
# if name is in customfolder (resized already)
if($storage->checkFile('customFolder', '', $originalFile))
{
$imagePath = $storage->getFolderPath('customFolder') . $originalFile;
}
# or in originalfolder (not resized yet)
elseif($storage->checkFile('originalFolder', '', $originalFile))
{
$imagePath = $storage->getFolderPath('originalFolder') . $originalFile;
}
else
{
return 'image not found';
}
$image = $this->createImageFromPath($imagePath, $extension);
imagefilter($image, IMG_FILTER_GRAYSCALE);
if($storage->storeCustomImage($image, $extension, $customName))
{
return '/media/custom/' . $customFile;
}
return 'error grayscaling image';
}
public function createImageFromPath($imagePath, $extension)
{
switch($extension)
{
case 'gif': $image = imagecreatefromgif($imagePath); break;
case 'jpg' :
case 'jpeg': $image = imagecreatefromjpeg($imagePath); break;
case 'png': $image = imagecreatefrompng($imagePath); break;
case 'webp': $image = imagecreatefromwebp($imagePath); break;
default: return 'image type not supported';
}
return $image;
}

View File

@@ -648,6 +648,37 @@ class Storage
return false;
}
public function storeCustomImage($image, $extension, $imageName)
{
switch($extension)
{
case "png":
$storedImage = imagepng( $image, $this->customFolder . $imageName . '.png', 9 );
break;
case "gif":
$storedImage = imagegif( $image, $this->customFolder . $imageName . '.gif' );
break;
case "webp":
$storedImage = imagewebp( $image, $this->customFolder . $imageName . '.webp', 80);
break;
case "jpg":
case "jpeg":
$storedImage = imagejpeg( $image, $this->customFolder . $imageName . '.' . $extension, 80);
break;
default:
$storedImage = false;
}
if(!$storedImage)
{
$this->errors[] = "Could not store the custom size of $imageName";
return false;
}
return true;
}
public function deleteImage($name)
{
# validate name

View File

@@ -32,6 +32,7 @@ use Typemill\Extensions\TwigUrlExtension;
use Typemill\Extensions\TwigUserExtension;
use Typemill\Extensions\TwigLanguageExtension;
use Typemill\Extensions\TwigMarkdownExtension;
use Typemill\Extensions\TwigMetaExtension;
use Typemill\Extensions\TwigCaptchaExtension;
$timer = [];
@@ -302,6 +303,7 @@ $container->set('view', function() use ($settings, $csrf, $urlinfo, $translation
$twig->addExtension(new TwigUrlExtension($urlinfo));
$twig->addExtension(new TwigLanguageExtension( $translations ));
$twig->addExtension(new TwigMarkdownExtension());
$twig->addExtension(new TwigMetaExtension());
$twig->addExtension(new TwigCaptchaExtension());
# start csrf only if session is active