mirror of
https://github.com/HaschekSolutions/pictshare.git
synced 2025-11-11 10:46:22 +00:00
added support for dynamic content controllers
Also implemented a placeholder generator to showcase it
This commit is contained in:
@@ -56,6 +56,7 @@ Then open http://localhost:8080 in your browser
|
||||
|
||||
## New Features in v2
|
||||
|
||||
- Generate placeholder images by specifying the size in the URL. [example](https://pictshare.net/placeholder/555x250/color-white-blue)
|
||||
- Added support for external storage
|
||||
- [Encryption of files in external storage](/rtfm/ENCRYPTION.md)
|
||||
- Added text hosting (like pastebin)
|
||||
@@ -105,13 +106,13 @@ Read [here](/rtfm/CONFIG.md) what those options do
|
||||
- [x] MASTER_DELETE_CODE
|
||||
- [x] MASTER_DELETE_IP
|
||||
- [x] UPLOAD_FORM_LOCATION
|
||||
- [x] S3 Backend
|
||||
- [ ] UPLOAD_QUOTA
|
||||
- [ ] UPLOAD_CODE
|
||||
- [ ] LOW_PROFILE
|
||||
- [ ] IMAGE_CHANGE_CODE
|
||||
- [ ] MAX_RESIZED_IMAGES
|
||||
- [ ] ALLOW_BLOATING
|
||||
- [ ] BACKBLAZE
|
||||
|
||||
### Image hosting
|
||||
- [x] Resizing
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
class ImageController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('png','bmp','gif','jpg','jpeg','x-png','webp');}
|
||||
|
||||
|
||||
Binary file not shown.
52
content-controllers/placeholder/placeholder.controller.php
Normal file
52
content-controllers/placeholder/placeholder.controller.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
class PlaceholderController implements ContentController
|
||||
{
|
||||
public const ctype = 'dynamic';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('placeholder');}
|
||||
|
||||
public function handleHash($hash,$url)
|
||||
{
|
||||
$path = ROOT.DS.'data'.DS.$hash.DS.$hash;
|
||||
|
||||
include_once(dirname(__FILE__).DS.'placeholdergenerator.php');
|
||||
$pg = new PlaceholderGenerator();
|
||||
|
||||
foreach($url as $u)
|
||||
{
|
||||
if(isSize($u))
|
||||
$modifiers['size'] = $u;
|
||||
if(startsWith($u,'color-'))
|
||||
{
|
||||
$u = substr($u,6);
|
||||
$colors = explode('-',$u);
|
||||
foreach($colors as $c)
|
||||
if(isColor($c))
|
||||
$modifiers['colors'][] = (ctype_xdigit($c)?$c:color_name_to_hex($c));
|
||||
|
||||
if(count($modifiers['colors'])>4)
|
||||
$modifiers['colors'] = array_slice($modifiers['colors'],0,4);
|
||||
}
|
||||
}
|
||||
|
||||
$img = $pg->generateImage($modifiers);
|
||||
|
||||
$img = $pg->gradient($img, $modifiers['colors']);
|
||||
$img = $pg->addSizeText($img,$modifiers);
|
||||
|
||||
header ("Content-type: image/jpeg");
|
||||
header ("ETag: $hash");
|
||||
header('Cache-control: public, max-age=31536000');
|
||||
|
||||
imagejpeg($img,null,(defined('JPEG_COMPRESSION')?JPEG_COMPRESSION:90));
|
||||
}
|
||||
|
||||
public function handleUpload($tmpfile,$hash=false)
|
||||
{
|
||||
return array('status'=>'err','hash'=>$hash,'reason'=>'Cannot upload to placeholder image');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
88
content-controllers/placeholder/placeholdergenerator.php
Normal file
88
content-controllers/placeholder/placeholdergenerator.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
class PlaceholderGenerator {
|
||||
|
||||
function generateImage($modifiers)
|
||||
{
|
||||
$size = ($modifiers['size']?:'800x600');
|
||||
|
||||
$sd = sizeStringToWidthHeight($size);
|
||||
$width = $sd['width'];
|
||||
$height = $sd['height'];
|
||||
|
||||
$im = imagecreatetruecolor($width, $height);
|
||||
|
||||
return $im;
|
||||
}
|
||||
|
||||
function addSizeText($im,$modifiers)
|
||||
{
|
||||
$size = imagesx($im).'x'.imagesy($im);
|
||||
$text = $size;
|
||||
//add the size as text in the center of the image
|
||||
$textcolor = imagecolorallocate($im, 0, 0, 0);
|
||||
$font = dirname(__FILE__).DS.'fonts/RonysiswadiArchitect5-1GErv.ttf';
|
||||
//calculate the size of the text to make sure it will alway be visible
|
||||
$fontsize = 20;
|
||||
$textsize = imagettfbbox($fontsize, 0, $font, $text);
|
||||
$scaleX = imagesx($im) / ($textsize[2] - $textsize[0] + 25);
|
||||
$scaleY = imagesy($im) / ($textsize[1] - $textsize[7] + 25);
|
||||
|
||||
$scale = min($scaleX,$scaleY);
|
||||
|
||||
$fontsize = 20 * $scale;
|
||||
$textsize = imagettfbbox($fontsize, 0, $font, $text);
|
||||
$textwidth = $textsize[2] - $textsize[0];
|
||||
$textheight = $textsize[1] - $textsize[7];
|
||||
|
||||
if($textwidth > imagesx($im) || $textheight > imagesy($im))
|
||||
return $im;
|
||||
|
||||
$x = (imagesx($im) - $textwidth) / 2;
|
||||
$y = (imagesy($im) - $textheight) / 2 + $textheight;
|
||||
imagettftext($im, $fontsize, 0, $x, $y, $textcolor, $font, $text);
|
||||
|
||||
return $im;
|
||||
}
|
||||
|
||||
function gradient($im, $c) {
|
||||
|
||||
$w = imagesx($im);
|
||||
$h = imagesy($im);
|
||||
|
||||
if(!$c[0]) $c = ['ffffff','ffffff','ffffff','ffffff'];
|
||||
else if(!$c[1]) $c = [$c[0],$c[0],$c[0],$c[0]];
|
||||
else if(!$c[2]) $c = [$c[0],$c[0],$c[1],$c[1]];
|
||||
else if(!$c[3]) $c = [$c[0],$c[1],$c[2],$c[0]];
|
||||
|
||||
for($i=0;$i<=3;$i++) {
|
||||
$c[$i]=$this->hex2rgb($c[$i]);
|
||||
}
|
||||
|
||||
$rgb=$c[0]; // start with top left color
|
||||
for($x=0;$x<=$w;$x++) { // loop columns
|
||||
for($y=0;$y<=$h;$y++) { // loop rows
|
||||
// set pixel color
|
||||
$col=imagecolorallocate($im,$rgb[0],$rgb[1],$rgb[2]);
|
||||
imagesetpixel($im,$x-1,$y-1,$col);
|
||||
// calculate new color
|
||||
for($i=0;$i<=2;$i++) {
|
||||
$rgb[$i]=
|
||||
$c[0][$i]*(($w-$x)*($h-$y)/($w*$h)) +
|
||||
$c[1][$i]*($x *($h-$y)/($w*$h)) +
|
||||
$c[2][$i]*(($w-$x)*$y /($w*$h)) +
|
||||
$c[3][$i]*($x *$y /($w*$h));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $im;
|
||||
}
|
||||
|
||||
function hex2rgb($hex)
|
||||
{
|
||||
$rgb[0]=hexdec(substr($hex,0,2));
|
||||
$rgb[1]=hexdec(substr($hex,2,2));
|
||||
$rgb[2]=hexdec(substr($hex,4,2));
|
||||
return($rgb);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
class TextController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('txt','text','csv');}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
class UrlController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('url');}
|
||||
public function handleHash($hash,$url){}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
class VideoController implements ContentController
|
||||
{
|
||||
public const ctype = 'static';
|
||||
|
||||
//returns all extensions registered by this type of content
|
||||
public function getRegisteredExtensions(){return array('mp4');}
|
||||
|
||||
|
||||
191
inc/core.php
191
inc/core.php
@@ -76,7 +76,21 @@ function architect($url)
|
||||
break; // we break here because we already have the file. no need to check other storage controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// if it's still false, we only have one hope: Maybe it's from a dynamic controller and the cache hasn't been created yet
|
||||
else if($hash===false)
|
||||
{
|
||||
foreach(loadAllContentControllers(true) as $cc)
|
||||
{
|
||||
if((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u) )
|
||||
{
|
||||
$hash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +126,10 @@ function architect($url)
|
||||
|
||||
foreach(loadAllContentControllers(true) as $cc)
|
||||
{
|
||||
if(in_array($extension,(new $cc)->getRegisteredExtensions()))
|
||||
if(
|
||||
((new $cc)::ctype=='dynamic' && in_array((new $cc)->getRegisteredExtensions()[0],$u)) ||
|
||||
((new $cc)::ctype=='static' && in_array($extension,(new $cc)->getRegisteredExtensions()))
|
||||
)
|
||||
{
|
||||
(new $cc())->handleHash($hash,$u);
|
||||
return;
|
||||
@@ -464,6 +481,176 @@ function isSize($var)
|
||||
return true;
|
||||
}
|
||||
|
||||
function isColor($var)
|
||||
{
|
||||
if(strlen($var)==6 && ctype_xdigit($var)) return true;
|
||||
else
|
||||
{
|
||||
$col = color_name_to_hex($var);
|
||||
if($col) return true;
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
||||
function color_name_to_hex($color_name)
|
||||
{
|
||||
// standard 147 HTML color names
|
||||
$colors = array(
|
||||
'aliceblue'=>'F0F8FF',
|
||||
'antiquewhite'=>'FAEBD7',
|
||||
'aqua'=>'00FFFF',
|
||||
'aquamarine'=>'7FFFD4',
|
||||
'azure'=>'F0FFFF',
|
||||
'beige'=>'F5F5DC',
|
||||
'bisque'=>'FFE4C4',
|
||||
'black'=>'000000',
|
||||
'blanchedalmond '=>'FFEBCD',
|
||||
'blue'=>'0000FF',
|
||||
'blueviolet'=>'8A2BE2',
|
||||
'brown'=>'A52A2A',
|
||||
'burlywood'=>'DEB887',
|
||||
'cadetblue'=>'5F9EA0',
|
||||
'chartreuse'=>'7FFF00',
|
||||
'chocolate'=>'D2691E',
|
||||
'coral'=>'FF7F50',
|
||||
'cornflowerblue'=>'6495ED',
|
||||
'cornsilk'=>'FFF8DC',
|
||||
'crimson'=>'DC143C',
|
||||
'cyan'=>'00FFFF',
|
||||
'darkblue'=>'00008B',
|
||||
'darkcyan'=>'008B8B',
|
||||
'darkgoldenrod'=>'B8860B',
|
||||
'darkgray'=>'A9A9A9',
|
||||
'darkgreen'=>'006400',
|
||||
'darkgrey'=>'A9A9A9',
|
||||
'darkkhaki'=>'BDB76B',
|
||||
'darkmagenta'=>'8B008B',
|
||||
'darkolivegreen'=>'556B2F',
|
||||
'darkorange'=>'FF8C00',
|
||||
'darkorchid'=>'9932CC',
|
||||
'darkred'=>'8B0000',
|
||||
'darksalmon'=>'E9967A',
|
||||
'darkseagreen'=>'8FBC8F',
|
||||
'darkslateblue'=>'483D8B',
|
||||
'darkslategray'=>'2F4F4F',
|
||||
'darkslategrey'=>'2F4F4F',
|
||||
'darkturquoise'=>'00CED1',
|
||||
'darkviolet'=>'9400D3',
|
||||
'deeppink'=>'FF1493',
|
||||
'deepskyblue'=>'00BFFF',
|
||||
'dimgray'=>'696969',
|
||||
'dimgrey'=>'696969',
|
||||
'dodgerblue'=>'1E90FF',
|
||||
'firebrick'=>'B22222',
|
||||
'floralwhite'=>'FFFAF0',
|
||||
'forestgreen'=>'228B22',
|
||||
'fuchsia'=>'FF00FF',
|
||||
'gainsboro'=>'DCDCDC',
|
||||
'ghostwhite'=>'F8F8FF',
|
||||
'gold'=>'FFD700',
|
||||
'goldenrod'=>'DAA520',
|
||||
'gray'=>'808080',
|
||||
'green'=>'008000',
|
||||
'greenyellow'=>'ADFF2F',
|
||||
'grey'=>'808080',
|
||||
'honeydew'=>'F0FFF0',
|
||||
'hotpink'=>'FF69B4',
|
||||
'indianred'=>'CD5C5C',
|
||||
'indigo'=>'4B0082',
|
||||
'ivory'=>'FFFFF0',
|
||||
'khaki'=>'F0E68C',
|
||||
'lavender'=>'E6E6FA',
|
||||
'lavenderblush'=>'FFF0F5',
|
||||
'lawngreen'=>'7CFC00',
|
||||
'lemonchiffon'=>'FFFACD',
|
||||
'lightblue'=>'ADD8E6',
|
||||
'lightcoral'=>'F08080',
|
||||
'lightcyan'=>'E0FFFF',
|
||||
'lightgoldenrodyellow'=>'FAFAD2',
|
||||
'lightgray'=>'D3D3D3',
|
||||
'lightgreen'=>'90EE90',
|
||||
'lightgrey'=>'D3D3D3',
|
||||
'lightpink'=>'FFB6C1',
|
||||
'lightsalmon'=>'FFA07A',
|
||||
'lightseagreen'=>'20B2AA',
|
||||
'lightskyblue'=>'87CEFA',
|
||||
'lightslategray'=>'778899',
|
||||
'lightslategrey'=>'778899',
|
||||
'lightsteelblue'=>'B0C4DE',
|
||||
'lightyellow'=>'FFFFE0',
|
||||
'lime'=>'00FF00',
|
||||
'limegreen'=>'32CD32',
|
||||
'linen'=>'FAF0E6',
|
||||
'magenta'=>'FF00FF',
|
||||
'maroon'=>'800000',
|
||||
'mediumaquamarine'=>'66CDAA',
|
||||
'mediumblue'=>'0000CD',
|
||||
'mediumorchid'=>'BA55D3',
|
||||
'mediumpurple'=>'9370D0',
|
||||
'mediumseagreen'=>'3CB371',
|
||||
'mediumslateblue'=>'7B68EE',
|
||||
'mediumspringgreen'=>'00FA9A',
|
||||
'mediumturquoise'=>'48D1CC',
|
||||
'mediumvioletred'=>'C71585',
|
||||
'midnightblue'=>'191970',
|
||||
'mintcream'=>'F5FFFA',
|
||||
'mistyrose'=>'FFE4E1',
|
||||
'moccasin'=>'FFE4B5',
|
||||
'navajowhite'=>'FFDEAD',
|
||||
'navy'=>'000080',
|
||||
'oldlace'=>'FDF5E6',
|
||||
'olive'=>'808000',
|
||||
'olivedrab'=>'6B8E23',
|
||||
'orange'=>'FFA500',
|
||||
'orangered'=>'FF4500',
|
||||
'orchid'=>'DA70D6',
|
||||
'palegoldenrod'=>'EEE8AA',
|
||||
'palegreen'=>'98FB98',
|
||||
'paleturquoise'=>'AFEEEE',
|
||||
'palevioletred'=>'DB7093',
|
||||
'papayawhip'=>'FFEFD5',
|
||||
'peachpuff'=>'FFDAB9',
|
||||
'peru'=>'CD853F',
|
||||
'pink'=>'FFC0CB',
|
||||
'plum'=>'DDA0DD',
|
||||
'powderblue'=>'B0E0E6',
|
||||
'purple'=>'800080',
|
||||
'red'=>'FF0000',
|
||||
'rosybrown'=>'BC8F8F',
|
||||
'royalblue'=>'4169E1',
|
||||
'saddlebrown'=>'8B4513',
|
||||
'salmon'=>'FA8072',
|
||||
'sandybrown'=>'F4A460',
|
||||
'seagreen'=>'2E8B57',
|
||||
'seashell'=>'FFF5EE',
|
||||
'sienna'=>'A0522D',
|
||||
'silver'=>'C0C0C0',
|
||||
'skyblue'=>'87CEEB',
|
||||
'slateblue'=>'6A5ACD',
|
||||
'slategray'=>'708090',
|
||||
'slategrey'=>'708090',
|
||||
'snow'=>'FFFAFA',
|
||||
'springgreen'=>'00FF7F',
|
||||
'steelblue'=>'4682B4',
|
||||
'tan'=>'D2B48C',
|
||||
'teal'=>'008080',
|
||||
'thistle'=>'D8BFD8',
|
||||
'tomato'=>'FF6347',
|
||||
'turquoise'=>'40E0D0',
|
||||
'violet'=>'EE82EE',
|
||||
'wheat'=>'F5DEB3',
|
||||
'white'=>'FFFFFF',
|
||||
'whitesmoke'=>'F5F5F5',
|
||||
'yellow'=>'FFFF00',
|
||||
'yellowgreen'=>'9ACD32');
|
||||
|
||||
$color_name = strtolower($color_name);
|
||||
if (isset($colors[$color_name]))
|
||||
return $colors[$color_name];
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
function getStorageControllers()
|
||||
{
|
||||
$controllers = array();
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
|
||||
interface ContentController
|
||||
{
|
||||
/**
|
||||
* When implementing a new content controller, you must specify a constant with the name of "ctype"
|
||||
* This constant will be used to distinguish static controllers (which have files on disk) from dynamic ones (which don't, like the placeholder controller)
|
||||
*
|
||||
* Possible values: 'static' or 'dynamic'
|
||||
*/
|
||||
//const ctype = 'static';
|
||||
|
||||
/** This method will return all file extensions that will be associated with this content type
|
||||
* for example 'pdf' but it could be anything really. You just need a way later to confirm that a type is what it says it is
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user