Created
October 6, 2012 16:22
-
-
Save fprochazka/3845361 to your computer and use it in GitHub Desktop.
Image pipe for #nettefw templates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Filip Procházka <[email protected]> | |
*/ | |
abstract class BasePresenter extends Nette\Application\UI\Presenter | |
{ | |
/** | |
* @var \Img\ImagePipe | |
*/ | |
private $imgPipe; | |
/** | |
* @param \Img\ImagePipe $imgPipe | |
*/ | |
public function injectImgPipe(\Img\ImagePipe $imgPipe) | |
{ | |
$this->imgPipe = $imgPipe; | |
} | |
/** | |
* @param string $class | |
* | |
* @return Nette\Templating\FileTemplate | |
*/ | |
protected function createTemplate($class = NULL) | |
{ | |
$tmp = parent::createTemplate($class); | |
/** @var \Nette\Templating\FileTemplate|\stdClass $tmp */ | |
$tmp->_imagePipe = $this->imgPipe; | |
return $tmp; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nette: | |
latte: | |
macros: [Img\ImgMacro] | |
services: | |
imageStorage: Img\ImageStorage(%tempDir%) | |
imagePipe: Img\ImagePipe(%wwwDir%) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<img n:tempImg="$uploadedImg" alt="" /> nebo <img n:img="$uploadedImg" alt="" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @author Filip Procházka <[email protected]> | |
*/ | |
class FooPresenter extends BasePresenter | |
{ | |
/** | |
* @var \Img\ImageStorage | |
*/ | |
private $imageStorage; | |
/** | |
* @param \Img\ImageStorage $storage | |
*/ | |
public function injectImageStorage(\Img\ImageStorage $storage) | |
{ | |
$this->imageStorage = $storage; | |
} | |
public function renderDefault() | |
{ | |
$this->template->uploadedImg = new Img\Image(...); // z databáze | |
} | |
/** | |
* @param string $name | |
* @return \Nette\Application\UI\Form | |
*/ | |
protected function createComponentUpload($name) | |
{ | |
$form = new UI\Form; | |
$form->addUpload('file') | |
->addRule(UI\Form::IMAGE, 'Obrázek musí být JPEG, PNG nebo GIF.') | |
->addRule(UI\Form::FILLED, 'Zvolte prosím obrázek.'); | |
$form->addSubmit('upload', 'Upload image') | |
->setValidationScope(FALSE) | |
->onClick[] = $this->processUpload; | |
return $form; | |
} | |
/** | |
* @param \Nette\Forms\Controls\SubmitButton $button | |
*/ | |
public function processUpload(SubmitButton $button) | |
{ | |
try { | |
$image = $this->imageStorage->upload($button->form['file']->value); | |
} catch (\Exception $e) { | |
$button->form->addError($e->getMessage()); | |
return; | |
} | |
// $this->flashMessage('Huray!'); | |
// $this->redirect('this'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Img; | |
use Nette; | |
/** | |
* @author Filip Procházka <[email protected]> | |
* | |
* @property-read string $file | |
* @property \Size $size | |
* @property-read \Size $size | |
*/ | |
class Image extends Nette\Object | |
{ | |
/** | |
* @var string | |
*/ | |
private $file; | |
/** | |
* @var \Size | |
*/ | |
private $size; | |
/** | |
* @param string $file | |
*/ | |
public function __construct($file) | |
{ | |
$this->file = $file; | |
$this->size = Size::fromFile($file); | |
} | |
/** | |
* @return bool | |
*/ | |
public function exists() | |
{ | |
return file_exists($this->file); | |
} | |
/** | |
* @return float|int | |
*/ | |
public function getFile() | |
{ | |
return $this->file; | |
} | |
/** | |
* @return \Size | |
*/ | |
public function getSize() | |
{ | |
return $this->size; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Img; | |
use Kdyby; | |
use Nette\Http\Request; | |
use Nette; | |
/** | |
* @author Filip Procházka <[email protected]> | |
*/ | |
class ImagePipe extends Nette\Object | |
{ | |
const TEMP = 1; | |
/** | |
* @var string | |
*/ | |
private $assetsDir; | |
/** | |
* @var string | |
*/ | |
private $wwwDir; | |
/** | |
* @var string | |
*/ | |
private $baseUrl; | |
/** | |
* @param string $wwwDir | |
* @param \Nette\Http\Request $httpRequest | |
* @param string $assetsDir | |
* | |
* @throws \Nette\IOException | |
*/ | |
public function __construct($wwwDir, Request $httpRequest, $assetsDir = 'assets') | |
{ | |
// public assets dir | |
$assetsDir = $wwwDir . '/' . $assetsDir; | |
if (!is_dir($assetsDir) || !is_writable($assetsDir)) { | |
throw new Nette\IOException("Directory $assetsDir is not writable."); | |
} | |
$this->wwwDir = $wwwDir; | |
$this->assetsDir = $assetsDir; | |
// for temporary files | |
if (!is_dir($tempDir = ($this->assetsDir . '/temp'))) { | |
self::mkdir($tempDir); | |
} | |
// base of public url | |
$this->baseUrl = rtrim($httpRequest->url->baseUrl, '/'); | |
} | |
/** | |
* @param string|Image $sourceImage | |
* @param array $size | |
* @param int $flags | |
* | |
* @return string | |
* @throws \Nette\IOException | |
*/ | |
public function request($sourceImage, array $size = array(), $flags = 0) | |
{ | |
if (!$sourceImage instanceof Image) { | |
$sourceImage = new Image($sourceImage); | |
} | |
$image = NULL; | |
/** @var Nette\Image $image */ | |
if ($sourceImage->size->width > 500 || $sourceImage->size->height > 500) { // w & h | |
$image = Nette\Image::fromFile($sourceImage->file)->resize(500, 500); | |
} // elseif($size) todo: zpracování velikosti | |
// figure out public directory | |
$dir = $flags & self::TEMP ? $this->assetsDir . '/temp' : $this->assetsDir; | |
$dir .= '/' . (implode('x', $size) ? : 'original'); | |
if (!is_dir($dir)) { | |
self::mkdir($dir); | |
} | |
// filename | |
$targetPath = $dir . '/' . basename($sourceImage->file); | |
// continue processing only if newer version is available | |
if (file_exists($targetPath) && filectime($targetPath) < filectime($sourceImage->file)) { | |
return $this->publicPath($targetPath); | |
} | |
// copy source file | |
if ($image) { | |
if (!$image->save($targetPath)) { | |
throw new Nette\IOException("Cannot resize $sourceImage->file to $targetPath"); | |
} | |
} else { | |
if (!@copy($sourceImage->file, $targetPath)) { | |
throw new Nette\IOException("Cannot copy $sourceImage->file to $targetPath"); | |
} | |
} | |
return $this->publicPath($targetPath); | |
} | |
/** | |
* @param string $file | |
* @return string | |
*/ | |
private function publicPath($file) | |
{ | |
return $this->baseUrl . str_replace($this->wwwDir, '', $file); | |
} | |
/** | |
* @param string $dir | |
* | |
* @throws \Nette\IOException | |
* @return void | |
*/ | |
private static function mkdir($dir) | |
{ | |
$oldMask = umask(0); | |
@mkdir($dir, 0777); | |
@chmod($dir, 0777); | |
umask($oldMask); | |
if (!is_dir($dir) || !is_writable($dir)) { | |
throw new Nette\IOException("Please create writable directory $dir."); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Img; | |
use Kdyby; | |
use Nette; | |
use Nette\Http\FileUpload; | |
use Nette\Utils\Finder; | |
use Nette\Utils\Strings; | |
/** | |
* @author Filip Procházka <[email protected]> | |
*/ | |
class ImageStorage extends Nette\Object | |
{ | |
/** | |
* @var string | |
*/ | |
private $imagesDir; | |
/** | |
* @param string $dir | |
*/ | |
public function __construct($dir) | |
{ | |
$dir .= '/images'; | |
if (!is_dir($dir)) { | |
umask(0); | |
mkdir($dir, 0777); | |
} | |
$this->imagesDir = $dir; | |
} | |
/** | |
* @param FileUpload $file | |
* @return Image | |
* @throws \Nette\InvalidArgumentException | |
*/ | |
public function upload(FileUpload $file) | |
{ | |
if (!$file->isOk() || !$file->isImage()) { | |
throw new Nette\InvalidArgumentException; | |
} | |
do { | |
$name = Strings::random(10) . '.' . $file->getSanitizedName(); | |
} while (file_exists($path = $this->imagesDir . DIRECTORY_SEPARATOR . $name)); | |
$file->move($path); | |
return new Image($path); | |
} | |
/** | |
* @param string $content | |
* @param string $filename | |
* @return Image | |
*/ | |
public function save($content, $filename) | |
{ | |
do { | |
$name = Strings::random(10) . '.' . $filename; | |
} while (file_exists($path = $this->imagesDir . DIRECTORY_SEPARATOR . $name)); | |
file_put_contents($path, $content); | |
return new Image($path); | |
} | |
/** | |
* @return string | |
*/ | |
public function getImagesDir() | |
{ | |
return $this->imagesDir; | |
} | |
/** | |
* @param $param | |
* @throws FileNotFoundException | |
* @return string | |
*/ | |
public function find($param) | |
{ | |
foreach (Finder::findFiles($param)->from($this->imagesDir) as $file) { | |
/** @var \SplFileInfo $file */ | |
return $file->getPathname(); | |
} | |
throw new FileNotFoundException("File $param not found."); | |
} | |
} | |
/** | |
* @author Filip Procházka <[email protected]> | |
*/ | |
class FileNotFoundException extends \RuntimeException | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Img; | |
use Kdyby; | |
use Nette; | |
use Nette\Latte\PhpWriter; | |
use Nette\Latte\MacroNode; | |
use Nette\Latte\Compiler; | |
/** | |
* @author Filip Procházka <[email protected]> | |
*/ | |
class ImgMacro extends Nette\Latte\Macros\MacroSet | |
{ | |
/** | |
* @var bool | |
*/ | |
private $isUsed = FALSE; | |
/** | |
* @param \Nette\Latte\Compiler $compiler | |
* | |
* @return ImgMacro|\Nette\Latte\Macros\MacroSet | |
*/ | |
public static function install(Compiler $compiler) | |
{ | |
$me = new static($compiler); | |
// todo: předání parametrů s velikostí | |
/** | |
* {img} je obecně pro jakýkoliv obrázek, který je veřejný na webu. | |
*/ | |
$me->addMacro('img', array($me, 'macroImg'), NULL, array($me, 'macroAttrImg')); | |
/** | |
* {tempImg} je kvůli obrázkům, které nejsou "schválené" a je pravděpodobné, | |
* že se budou mazat. Tak aby byly ve zvláštní složce, kterou může promazávat robot. | |
* Pokud nic takového nepotřebujete, používejte prostě {img}. | |
*/ | |
$me->addMacro('tempImg', array($me, 'macroImgTemp'), NULL, array($me, 'macroAttrImgTemp')); | |
return $me; | |
} | |
/** | |
* @param \Nette\Latte\MacroNode $node | |
* @param \Nette\Latte\PhpWriter $writer | |
* @return string | |
*/ | |
public function macroImg(MacroNode $node, PhpWriter $writer) | |
{ | |
$this->isUsed = TRUE; | |
return $writer->write('echo %escape($_imagePipe->request(%node.word))'); | |
} | |
/** | |
* @param \Nette\Latte\MacroNode $node | |
* @param \Nette\Latte\PhpWriter $writer | |
* @return string | |
*/ | |
public function macroAttrImg(MacroNode $node, PhpWriter $writer) | |
{ | |
$this->isUsed = TRUE; | |
return $writer->write('?>src="<?php echo %escape($_imagePipe->request(%node.word))?>" <?php'); | |
} | |
/** | |
* @param \Nette\Latte\MacroNode $node | |
* @param \Nette\Latte\PhpWriter $writer | |
* @return string | |
*/ | |
public function macroImgTemp(MacroNode $node, PhpWriter $writer) | |
{ | |
$this->isUsed = TRUE; | |
return $writer->write('echo %escape($_imagePipe->request(%node.word, array(), $_imagePipe::TEMP))'); | |
} | |
/** | |
* @param \Nette\Latte\MacroNode $node | |
* @param \Nette\Latte\PhpWriter $writer | |
* @return string | |
*/ | |
public function macroAttrImgTemp(MacroNode $node, PhpWriter $writer) | |
{ | |
$this->isUsed = TRUE; | |
return $writer->write('?>src="<?php echo %escape($_imagePipe->request(%node.word, array(), $_imagePipe::TEMP))?>" <?php'); | |
} | |
/** | |
*/ | |
public function initialize() | |
{ | |
$this->isUsed = FALSE; | |
} | |
/** | |
* Finishes template parsing. | |
* @return array(prolog, epilog) | |
*/ | |
public function finalize() | |
{ | |
if (!$this->isUsed) { | |
return array(); | |
} | |
return array( | |
get_called_class() . '::validateTemplateParams($template);', | |
NULL | |
); | |
} | |
/** | |
* @param \Nette\Templating\Template $template | |
* @throws \Nette\InvalidStateException | |
*/ | |
public static function validateTemplateParams(Nette\Templating\Template $template) | |
{ | |
$params = $template->getParameters(); | |
if (!isset($params['_imagePipe']) || !$params['_imagePipe'] instanceof ImagePipe) { | |
$where = isset($params['control']) ? | |
" of component " . get_class($params['control']) . '(' . $params['control']->getName() . ')' | |
: NULL; | |
throw new Nette\InvalidStateException( | |
'Please provide an instanceof Img\\ImagePipe ' . | |
'as a parameter $_imagePipe to template' . $where | |
); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Img; | |
use Nette; | |
/** | |
* @author Filip Procházka <[email protected]> | |
* | |
* @property float|int $width | |
* @property float|int $height | |
* @property-read float|int $height | |
*/ | |
class Size extends Nette\Object | |
{ | |
/** | |
* @var float|int | |
*/ | |
private $width; | |
/** | |
* @var float|int | |
*/ | |
private $height; | |
/** | |
* @param float|int $width | |
* @param float|int $height | |
*/ | |
public function __construct($width, $height) | |
{ | |
$this->width = $width; | |
$this->height = $height; | |
} | |
/** | |
* @return float|int | |
*/ | |
public function getHeight() | |
{ | |
return $this->height; | |
} | |
/** | |
* @return float|int | |
*/ | |
public function getWidth() | |
{ | |
return $this->width; | |
} | |
/** | |
* @param string $file | |
* | |
* @return Size | |
*/ | |
public static function fromFile($file) | |
{ | |
list($width, $height) = @getimagesize($file); | |
return new Size($width, $height); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Berte v úvahu, že dané řešení dává smysl v konkrétní aplikaci a proto tam mám například natvrdo maximální velikost obrázku 500x500 a jiné neřeším. Tyhle drobnůstky je třeba doladit ;)